001package com.nimbusds.jose.crypto;
002
003
004import java.security.SecureRandom;
005import java.security.interfaces.RSAPublicKey;
006
007import javax.crypto.SecretKey;
008
009import com.nimbusds.jose.EncryptionMethod;
010import com.nimbusds.jose.JOSEException;
011import com.nimbusds.jose.JWEAlgorithm;
012import com.nimbusds.jose.JWECryptoParts;
013import com.nimbusds.jose.JWEEncrypter;
014import com.nimbusds.jose.ReadOnlyJWEHeader;
015import com.nimbusds.jose.util.Base64URL;
016import com.nimbusds.jose.util.StringUtils;
017
018
019
020/**
021 * RSA encrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class
022 * is thread-safe.
023 *
024 * <p>Supports the following JWE algorithms:
025 *
026 * <ul>
027 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5}
028 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP}
029 * </ul>
030 *
031 * <p>Supports the following encryption methods:
032 *
033 * <ul>
034 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
035 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
036 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
037 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
038 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
039 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
040 * </ul>
041 *
042 * @author David Ortiz
043 * @author Vladimir Dzhuvinov
044 * @version $version$ (2014-01-28)
045 */
046public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter {
047
048
049        /**
050         * The public RSA key.
051         */
052        private final RSAPublicKey publicKey;
053
054
055        /**
056         * Creates a new RSA encrypter.
057         *
058         * @param publicKey The public RSA key. Must not be {@code null}.
059         *
060         * @throws JOSEException If the underlying secure random generator
061         *                       couldn't be instantiated.
062         */
063        public RSAEncrypter(final RSAPublicKey publicKey)
064                throws JOSEException {
065
066                if (publicKey == null) {
067
068                        throw new IllegalArgumentException("The public RSA key must not be null");
069                }
070
071                this.publicKey = publicKey;
072        }
073
074
075        /**
076         * Gets the public RSA key.
077         *
078         * @return The public RSA key.
079         */
080        public RSAPublicKey getPublicKey() {
081
082                return publicKey;
083        }
084
085
086        @Override
087        public JWECryptoParts encrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, final byte[] bytes)
088                throws JOSEException {
089
090                JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm();
091                EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod();
092
093                // Generate and encrypt the CEK according to the enc method
094                SecureRandom randomGen = getSecureRandom();
095                SecretKey cek = AES.generateKey(enc.cekBitLength(), randomGen);
096
097                Base64URL encryptedKey; // The second JWE part
098
099                if (alg.equals(JWEAlgorithm.RSA1_5)) {
100
101                        encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, keyEncryptionProvider));
102
103                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
104
105                        encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, keyEncryptionProvider));
106
107                } else {
108
109                        throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5 or RSA-OAEP");
110                }
111
112
113                // Apply compression if instructed
114                byte[] plainText = DeflateHelper.applyCompression(readOnlyJWEHeader, bytes);
115
116                // Compose the AAD
117                byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString());
118
119                // Encrypt the plain text according to the JWE enc
120                byte[] iv;
121                AuthenticatedCipherText authCipherText;
122                
123                if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) {
124
125                        iv = AESCBC.generateIV(randomGen);
126
127                        authCipherText = AESCBC.encryptAuthenticated(cek, iv, plainText, aad, contentEncryptionProvider, macProvider);
128
129                } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) {
130
131                        iv = AESGCM.generateIV(randomGen);
132
133                        authCipherText = AESGCM.encrypt(cek, iv, plainText, aad, contentEncryptionProvider);
134
135                } else {
136
137                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A256GCM");
138                }
139
140                return new JWECryptoParts(encryptedKey,  
141                                          Base64URL.encode(iv), 
142                                          Base64URL.encode(authCipherText.getCipherText()),
143                                          Base64URL.encode(authCipherText.getAuthenticationTag()));
144        }
145}