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 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256}
030 * </ul>
031 *
032 * <p>Supports the following encryption methods:
033 *
034 * <ul>
035 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
036 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
037 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
038 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
039 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
040 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
041 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
042 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
043 * </ul>
044 *
045 * @author David Ortiz
046 * @author Vladimir Dzhuvinov
047 * @version $version$ (2014-05-23)
048 */
049public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter {
050
051
052        /**
053         * The public RSA key.
054         */
055        private final RSAPublicKey publicKey;
056
057
058        /**
059         * Creates a new RSA encrypter.
060         *
061         * @param publicKey The public RSA key. Must not be {@code null}.
062         *
063         * @throws JOSEException If the underlying secure random generator
064         *                       couldn't be instantiated.
065         */
066        public RSAEncrypter(final RSAPublicKey publicKey)
067                throws JOSEException {
068
069                if (publicKey == null) {
070
071                        throw new IllegalArgumentException("The public RSA key must not be null");
072                }
073
074                this.publicKey = publicKey;
075        }
076
077
078        /**
079         * Gets the public RSA key.
080         *
081         * @return The public RSA key.
082         */
083        public RSAPublicKey getPublicKey() {
084
085                return publicKey;
086        }
087
088
089        @Override
090        public JWECryptoParts encrypt(final ReadOnlyJWEHeader header, final byte[] bytes)
091                throws JOSEException {
092
093                final JWEAlgorithm alg = header.getAlgorithm();
094                final EncryptionMethod enc = header.getEncryptionMethod();
095
096                // Generate and encrypt the CEK according to the enc method
097                final SecureRandom randomGen = getSecureRandom();
098                final SecretKey cek = AES.generateKey(enc.cekBitLength(), randomGen);
099
100                Base64URL encryptedKey; // The second JWE part
101
102                if (alg.equals(JWEAlgorithm.RSA1_5)) {
103
104                        encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, keyEncryptionProvider));
105
106                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
107
108                        encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, keyEncryptionProvider));
109
110                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
111                        
112                        encryptedKey = Base64URL.encode(RSA_OAEP_256.encryptCEK(publicKey, cek, keyEncryptionProvider));
113                        
114                } else {
115
116                        throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5, RSA-OAEP, or RSA-OAEP-256");
117                }
118
119
120                // Apply compression if instructed
121                byte[] plainText = DeflateHelper.applyCompression(header, bytes);
122
123                // Compose the AAD
124                byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString());
125
126                // Encrypt the plain text according to the JWE enc
127                byte[] iv;
128                AuthenticatedCipherText authCipherText;
129                
130                if (enc.equals(EncryptionMethod.A128CBC_HS256) ||
131                    enc.equals(EncryptionMethod.A192CBC_HS384) ||
132                    enc.equals(EncryptionMethod.A256CBC_HS512)    ) {
133
134                        iv = AESCBC.generateIV(randomGen);
135
136                        authCipherText = AESCBC.encryptAuthenticated(
137                                cek, iv, plainText, aad,
138                                contentEncryptionProvider, macProvider);
139
140                } else if (enc.equals(EncryptionMethod.A128GCM) ||
141                           enc.equals(EncryptionMethod.A192GCM) ||
142                           enc.equals(EncryptionMethod.A256GCM)    ) {
143
144                        iv = AESGCM.generateIV(randomGen);
145
146                        authCipherText = AESGCM.encrypt(
147                                cek, iv, plainText, aad,
148                                contentEncryptionProvider);
149
150                } else if (enc.equals(EncryptionMethod.A128CBC_HS256_DEPRECATED) ||
151                           enc.equals(EncryptionMethod.A256CBC_HS512_DEPRECATED)    ) {
152
153                        iv = AESCBC.generateIV(randomGen);
154
155                        authCipherText = AESCBC.encryptWithConcatKDF(
156                                header, cek, encryptedKey, iv, plainText,
157                                contentEncryptionProvider, macProvider);
158
159                } else {
160
161                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A256GCM");
162                }
163
164                return new JWECryptoParts(encryptedKey,  
165                                          Base64URL.encode(iv), 
166                                          Base64URL.encode(authCipherText.getCipherText()),
167                                          Base64URL.encode(authCipherText.getAuthenticationTag()));
168        }
169}