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