001package com.nimbusds.jose.crypto;
002
003
004import java.security.PrivateKey;
005import java.util.Set;
006import javax.crypto.SecretKey;
007
008import com.nimbusds.jose.*;
009import com.nimbusds.jose.jwk.RSAKey;
010import com.nimbusds.jose.util.Base64URL;
011import net.jcip.annotations.ThreadSafe;
012
013
014/**
015 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a
016 * private RSA key.
017 *
018 * <p>Decrypts the encrypted Content Encryption Key (CEK) with the private RSA
019 * key, and then uses the CEK along with the IV and authentication tag to
020 * decrypt the cipher text. See RFC 7518, sections
021 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and
022 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more
023 * information.
024 *
025 * <p>This class is thread-safe.
026 *
027 * <p>Supports the following key management algorithms:
028 *
029 * <ul>
030 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5}
031 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP}
032 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256}
033 * </ul>
034 *
035 * <p>Supports the following content encryption algorithms:
036 *
037 * <ul>
038 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
039 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
040 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
041 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
042 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
043 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
044 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
045 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
046 * </ul>
047 * 
048 * @author David Ortiz
049 * @author Vladimir Dzhuvinov
050 * @author Dimitar A. Stoikov
051 * @version 2016-06-29
052 */
053@ThreadSafe
054public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
055
056
057        /**
058         * The critical header policy.
059         */
060        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
061
062
063        /**
064         * The private RSA key.
065         */
066        private final PrivateKey privateKey;
067
068
069        /**
070         * Creates a new RSA decrypter.
071         *
072         * @param privateKey The private RSA key. Must not be {@code null}.
073         */
074        public RSADecrypter(final PrivateKey privateKey) {
075
076                this(privateKey, null);
077        }
078
079
080        /**
081         * Creates a new RSA decrypter.
082         *
083         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private
084         *               part. Must not be {@code null}.
085         *
086         * @throws JOSEException If the RSA JWK doesn't contain a private part
087         *                       or its extraction failed.
088         */
089        public RSADecrypter(final RSAKey rsaJWK)
090                throws JOSEException {
091
092                if (! rsaJWK.isPrivate()) {
093                        throw new JOSEException("The RSA JWK doesn't contain a private part");
094                }
095
096                privateKey = rsaJWK.toRSAPrivateKey();
097        }
098
099
100        /**
101         * Creates a new RSA decrypter.
102         *
103         * @param privateKey     The private RSA key. Its algorithm must be
104         *                       "RSA". Must not be {@code null}.
105         * @param defCritHeaders The names of the critical header parameters
106         *                       that are deferred to the application for
107         *                       processing, empty set or {@code null} if none.
108         */
109        public RSADecrypter(final PrivateKey privateKey,
110                            final Set<String> defCritHeaders) {
111
112                if (privateKey == null) {
113                        throw new IllegalArgumentException("The private RSA key must not be null");
114                }
115
116                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
117                        throw new IllegalArgumentException("The private key algorithm must be RSA");
118                }
119
120                this.privateKey = privateKey;
121
122                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
123        }
124
125
126        /**
127         * Gets the private RSA key.
128         *
129         * @return The private RSA key. Casting to
130         *         {@link java.security.interfaces.RSAPrivateKey} may not be
131         *         possible if the key is backed by a key store that doesn't
132         *         expose the private key parameters.
133         */
134        public PrivateKey getPrivateKey() {
135
136                return privateKey;
137        }
138
139
140        @Override
141        public Set<String> getProcessedCriticalHeaderParams() {
142
143                return critPolicy.getProcessedCriticalHeaderParams();
144        }
145
146
147        @Override
148        public Set<String> getDeferredCriticalHeaderParams() {
149
150                return critPolicy.getProcessedCriticalHeaderParams();
151        }
152
153
154        @Override
155        public byte[] decrypt(final JWEHeader header,
156                              final Base64URL encryptedKey,
157                              final Base64URL iv,
158                              final Base64URL cipherText,
159                              final Base64URL authTag) 
160                throws JOSEException {
161
162                // Validate required JWE parts
163                if (encryptedKey == null) {
164                        throw new JOSEException("Missing JWE encrypted key");
165                }       
166
167                if (iv == null) {
168                        throw new JOSEException("Missing JWE initialization vector (IV)");
169                }
170
171                if (authTag == null) {
172                        throw new JOSEException("Missing JWE authentication tag");
173                }
174
175                critPolicy.ensureHeaderPasses(header);
176                
177
178                // Derive the content encryption key
179                JWEAlgorithm alg = header.getAlgorithm();
180
181                SecretKey cek;
182
183                if (alg.equals(JWEAlgorithm.RSA1_5)) {
184
185                        int keyLength = header.getEncryptionMethod().cekBitLength();
186
187                        // Protect against MMA attack by generating random CEK on failure,
188                        // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html
189                        final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom());
190
191                        try {
192                                cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider());
193
194                                if (cek == null) {
195                                        // CEK length mismatch, signalled by null instead of
196                                        // exception to prevent MMA attack
197                                        cek = randomCEK;
198                                }
199
200                        } catch (Exception e) {
201                                // continue
202                                cek = randomCEK;
203                        }
204                
205                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
206
207                        cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
208
209                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
210                        
211                        cek = RSA_OAEP_256.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
212                        
213                } else {
214                
215                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS));
216                }
217
218                return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
219        }
220}
221