001package com.nimbusds.jose.crypto;
002
003
004import java.security.interfaces.RSAPrivateKey;
005
006import javax.crypto.SecretKey;
007
008import com.nimbusds.jose.DefaultJWEHeaderFilter;
009import com.nimbusds.jose.EncryptionMethod;
010import com.nimbusds.jose.JOSEException;
011import com.nimbusds.jose.JWEAlgorithm;
012import com.nimbusds.jose.JWEDecrypter;
013import com.nimbusds.jose.JWEHeaderFilter;
014import com.nimbusds.jose.ReadOnlyJWEHeader;
015import com.nimbusds.jose.util.Base64URL;
016import com.nimbusds.jose.util.StringUtils;
017
018
019/**
020 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class
021 * is thread-safe.
022 *
023 * <p>Supports the following JWE algorithms:
024 *
025 * <ul>
026 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5}
027 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP}
028 * </ul>
029 *
030 * <p>Supports the following encryption methods:
031 *
032 * <ul>
033 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
034 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
035 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
036 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
037 * </ul>
038 *
039 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames
040 * registered JWE header parameters}. Modify the {@link #getJWEHeaderFilter
041 * header filter} properties to restrict the acceptable JWE algorithms, 
042 * encryption methods and header parameters, or to allow custom JWE header 
043 * parameters.
044 * 
045 * @author David Ortiz
046 * @author Vladimir Dzhuvinov
047 * @version $version$ (2013-10-07)
048 *
049 */
050public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter {
051
052
053        /**
054         * The JWE header filter.
055         */
056        private final DefaultJWEHeaderFilter headerFilter;
057
058
059        /**
060         * The private RSA key.
061         */
062        private RSAPrivateKey privateKey;
063
064
065        /**
066         * Creates a new RSA decrypter.
067         *
068         * @param privateKey The private RSA key. Must not be {@code null}.
069         */
070        public RSADecrypter(final RSAPrivateKey privateKey) {
071
072                if (privateKey == null) {
073
074                        throw new IllegalArgumentException("The private RSA key must not be null");
075                }
076
077                this.privateKey = privateKey;
078
079                headerFilter = new DefaultJWEHeaderFilter(supportedAlgorithms(), supportedEncryptionMethods());
080        }
081
082
083        /**
084         * Gets the private RSA key.
085         *
086         * @return The private RSA key.
087         */
088        public RSAPrivateKey getPrivateKey() {
089
090                return privateKey;
091        }
092
093
094        @Override
095        public JWEHeaderFilter getJWEHeaderFilter() {
096
097                return headerFilter;
098        }
099
100
101        @Override
102        public byte[] decrypt(final ReadOnlyJWEHeader readOnlyJWEHeader,
103                              final Base64URL encryptedKey,
104                              final Base64URL iv,
105                              final Base64URL cipherText,
106                              final Base64URL authTag) 
107                throws JOSEException {
108
109                // Validate required JWE parts
110                if (encryptedKey == null) {
111
112                        throw new JOSEException("The encrypted key must not be null");
113                }       
114
115                if (iv == null) {
116
117                        throw new JOSEException("The initialization vector (IV) must not be null");
118                }
119
120                if (authTag == null) {
121
122                        throw new JOSEException("The authentication tag must not be null");
123                }
124                
125
126                // Derive the content encryption key
127                JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm();
128
129                SecretKey cek = null;
130
131                if (alg.equals(JWEAlgorithm.RSA1_5)) {
132
133                        int keyLength = readOnlyJWEHeader.getEncryptionMethod().cekBitLength();
134
135                        SecretKey randomCEK = AES.generateKey(keyLength);
136
137                        try {
138                                cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength);  
139                        
140                        } catch (Exception e) {
141
142                                // Protect against MMA attack by generating random CEK on failure, 
143                                // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html
144                                cek = randomCEK;
145                        }
146                
147                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
148
149                        cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode());
150
151                } else {
152                
153                        throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5 or RSA_OAEP");
154                }
155
156                // Compose the AAD
157                byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString());
158
159                // Decrypt the cipher text according to the JWE enc
160                EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod();
161
162                byte[] plainText;
163
164                if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A256CBC_HS512)) {
165
166                        plainText = AESCBC.decryptAuthenticated(cek, iv.decode(), cipherText.decode(), aad, authTag.decode());
167
168                } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A256GCM)) {
169
170                        plainText = AESGCM.decrypt(cek, iv.decode(), cipherText.decode(), aad, authTag.decode());
171
172                } else {
173
174                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A256CBC_HS512, A128GCM or A128GCM");
175                }
176
177
178                // Apply decompression if requested
179                return DeflateHelper.applyDecompression(readOnlyJWEHeader, plainText);
180        }
181}
182