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