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