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