001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.crypto;
019
020
021import java.security.PrivateKey;
022import java.util.Set;
023import javax.crypto.SecretKey;
024
025import static com.nimbusds.jose.jwk.gen.RSAKeyGenerator.MIN_KEY_SIZE_BITS;
026
027import net.jcip.annotations.ThreadSafe;
028
029import com.nimbusds.jose.*;
030import com.nimbusds.jose.crypto.impl.*;
031import com.nimbusds.jose.jwk.RSAKey;
032import com.nimbusds.jose.util.Base64URL;
033
034
035/**
036 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a
037 * private RSA key.
038 *
039 * <p>Decrypts the encrypted Content Encryption Key (CEK) with the private RSA
040 * key, and then uses the CEK along with the IV and authentication tag to
041 * decrypt the cipher text. See RFC 7518, sections
042 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and
043 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more
044 * information.
045 *
046 * <p>This class is thread-safe.
047 *
048 * <p>Supports the following key management algorithms:
049 *
050 * <ul>
051 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256}
052 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_384}
053 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_512}
054 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} (deprecated)
055 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} (deprecated)
056 * </ul>
057 *
058 * <p>Supports the following content encryption algorithms:
059 *
060 * <ul>
061 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
062 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
063 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
064 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
065 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
066 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
067 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
068 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
069 *     <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P}
070 * </ul>
071 * 
072 * @author David Ortiz
073 * @author Vladimir Dzhuvinov
074 * @author Dimitar A. Stoikov
075 * @author Egor Puzanov
076 * @version 2023-09-10
077 */
078@ThreadSafe
079public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
080
081
082        /**
083         * The critical header policy.
084         */
085        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
086
087
088        /**
089         * The private RSA key.
090         */
091        private final PrivateKey privateKey;
092        
093        
094        /**
095         * Stores a CEK decryption exception is one was encountered during the
096         * last {@link #decrypt} run.
097         */
098        private Exception cekDecryptionException;
099
100
101        /**
102         * Creates a new RSA decrypter. This constructor can also accept a
103         * private RSA key located in a PKCS#11 store that doesn't expose the
104         * private key parameters (such as a smart card or HSM).
105         *
106         * @param privateKey The private RSA key. Its algorithm must be "RSA"
107         *                   and its length at least 2048 bits. Note that the
108         *                   length of an RSA key in a PKCS#11 store cannot be
109         *                   checked. Must not be {@code null}.
110         */
111        public RSADecrypter(final PrivateKey privateKey) {
112
113                this(privateKey, null, false);
114        }
115
116
117        /**
118         * Creates a new RSA decrypter.
119         *
120         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
121         *               a private part. Its length must be at least 2048 bits.
122         *               Note that the length of an RSA key in a PKCS#11 store
123         *               cannot be checked. Must not be {@code null}.
124         *
125         * @throws JOSEException If the RSA JWK doesn't contain a private part
126         *                       or its extraction failed.
127         */
128        public RSADecrypter(final RSAKey rsaJWK)
129                throws JOSEException {
130
131                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK));
132        }
133
134
135        /**
136         * Creates a new RSA decrypter. This constructor can also accept a
137         * private RSA key located in a PKCS#11 store that doesn't expose the
138         * private key parameters (such as a smart card or HSM).
139         *
140         * @param privateKey     The private RSA key. Its algorithm must be
141         *                       "RSA" and its length at least 2048 bits. Note
142         *                       that the length of an RSA key in a PKCS#11
143         *                       store cannot be checked. Must not be
144         *                       {@code null}.
145         * @param defCritHeaders The names of the critical header parameters
146         *                       that are deferred to the application for
147         *                       processing, empty set or {@code null} if none.
148         */
149        public RSADecrypter(final PrivateKey privateKey,
150                            final Set<String> defCritHeaders) {
151
152                this(privateKey, defCritHeaders, false);
153        }
154
155
156        /**
157         * Creates a new RSA decrypter. This constructor can also accept a
158         * private RSA key located in a PKCS#11 store that doesn't expose the
159         * private key parameters (such as a smart card or HSM).
160         *
161         * @param privateKey     The private RSA key. Its algorithm must be
162         *                       "RSA" and its length at least 2048 bits. Note
163         *                       that the length of an RSA key in a PKCS#11
164         *                       store cannot be checked. Must not be
165         *                       {@code null}.
166         * @param defCritHeaders The names of the critical header parameters
167         *                       that are deferred to the application for
168         *                       processing, empty set or {@code null} if none.
169         * @param allowWeakKey   {@code true} to allow an RSA key shorter than
170         *                       2048 bits.
171         */
172        public RSADecrypter(final PrivateKey privateKey,
173                            final Set<String> defCritHeaders,
174                            final boolean allowWeakKey) {
175
176                super(null);
177
178                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
179                        throw new IllegalArgumentException("The private key algorithm must be RSA");
180                }
181                
182                if (! allowWeakKey) {
183                        
184                        int keyBitLength = RSAKeyUtils.keyBitLength(privateKey);
185                        
186                        if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) {
187                                throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits");
188                        }
189                }
190
191                this.privateKey = privateKey;
192
193                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
194        }
195
196
197        /**
198         * Gets the private RSA key.
199         *
200         * @return The private RSA key. Casting to
201         *         {@link java.security.interfaces.RSAPrivateKey} may not be
202         *         possible if the key is located in a PKCS#11 store that
203         *         doesn't expose the private key parameters.
204         */
205        public PrivateKey getPrivateKey() {
206
207                return privateKey;
208        }
209
210
211        @Override
212        public Set<String> getProcessedCriticalHeaderParams() {
213
214                return critPolicy.getProcessedCriticalHeaderParams();
215        }
216
217
218        @Override
219        public Set<String> getDeferredCriticalHeaderParams() {
220
221                return critPolicy.getProcessedCriticalHeaderParams();
222        }
223
224
225        /**
226         * Decrypts the specified cipher text of a {@link JWEObject JWE Object}.
227         *
228         * @param header       The JSON Web Encryption (JWE) header. Must
229         *                     specify a supported JWE algorithm and method.
230         *                     Must not be {@code null}.
231         * @param encryptedKey The encrypted key, {@code null} if not required
232         *                     by the JWE algorithm.
233         * @param iv           The initialisation vector, {@code null} if not
234         *                     required by the JWE algorithm.
235         * @param cipherText   The cipher text to decrypt. Must not be
236         *                     {@code null}.
237         * @param authTag      The authentication tag, {@code null} if not
238         *                     required.
239         *
240         * @return The clear text.
241         *
242         * @throws JOSEException If the JWE algorithm or method is not
243         *                       supported, if a critical header parameter is
244         *                       not supported or marked for deferral to the
245         *                       application, or if decryption failed for some
246         *                       other reason.
247         */
248        @Deprecated
249        public byte[] decrypt(final JWEHeader header,
250                       final Base64URL encryptedKey,
251                       final Base64URL iv,
252                       final Base64URL cipherText,
253                       final Base64URL authTag)
254                throws JOSEException {
255
256                return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header));
257        }
258
259
260        @Override
261        public byte[] decrypt(final JWEHeader header,
262                              final Base64URL encryptedKey,
263                              final Base64URL iv,
264                              final Base64URL cipherText,
265                              final Base64URL authTag, 
266                              final byte[] aad) 
267                throws JOSEException {
268
269                // Validate required JWE parts
270                if (encryptedKey == null) {
271                        throw new JOSEException("Missing JWE encrypted key");
272                }       
273
274                if (iv == null) {
275                        throw new JOSEException("Missing JWE initialization vector (IV)");
276                }
277
278                if (authTag == null) {
279                        throw new JOSEException("Missing JWE authentication tag");
280                }
281
282                critPolicy.ensureHeaderPasses(header);
283                
284
285                // Derive the content encryption key
286                JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header);
287
288                SecretKey cek;
289
290                if (alg.equals(JWEAlgorithm.RSA1_5)) {
291
292                        int keyLength = header.getEncryptionMethod().cekBitLength();
293
294                        // Protect against MMA attack by generating random CEK to be used on decryption failure,
295                        // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html
296                        final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom());
297
298                        try {
299                                cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider());
300
301                                if (cek == null) {
302                                        // CEK length mismatch, signalled by null instead of
303                                        // exception to prevent MMA attack
304                                        cek = randomCEK;
305                                }
306
307                        } catch (Exception e) {
308                                // continue
309                                cekDecryptionException = e;
310                                cek = randomCEK;
311                        }
312                        
313                        cekDecryptionException = null;
314                
315                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
316                        cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
317                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
318                        cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 256, getJCAContext().getKeyEncryptionProvider());
319                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_384)) {
320                        cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 384, getJCAContext().getKeyEncryptionProvider());
321                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_512)){
322                        cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 512, getJCAContext().getKeyEncryptionProvider());
323                } else {
324                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS));
325                }
326
327                return ContentCryptoProvider.decrypt(header, aad, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
328        }
329        
330        
331        /**
332         * Returns the Content Encryption Key (CEK) decryption exception if one
333         * was encountered during the last {@link #decrypt} run. Intended for
334         * logging and debugging purposes.
335         *
336         * @return The recorded exception, {@code null} if none.
337         */
338        public Exception getCEKDecryptionException() {
339                
340                return cekDecryptionException;
341        }
342}
343