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 2026-09-16
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.
144         *
145         * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference
146         *               a private part. Its length must be at least 2048 bits.
147         *               Note that the length of an RSA key in a PKCS#11 store
148         *               cannot be checked. Must not be {@code null}.
149         * @param opts   The decryption options, empty or {@code null} if none.
150         *
151         * @throws JOSEException If the RSA JWK doesn't contain a private part
152         *                       or its extraction failed.
153         */
154        public RSADecrypter(final RSAKey rsaJWK,
155                            final Set<JWEDecrypterOption> opts)
156                throws JOSEException {
157
158                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), null, opts);
159        }
160
161
162        /**
163         * Creates a new RSA decrypter.
164         *
165         * @param rsaJWK         The RSA JSON Web Key (JWK). Must contain or
166         *                       reference a private part. Its length must be at
167         *                       least 2048 bits. Note that the length of an
168         *                       RSA key in a PKCS#11 store cannot be checked.
169         *                       Must not be {@code null}.
170         * @param defCritHeaders The names of the critical header parameters
171         *                       that are deferred to the application for
172         *                       processing, empty set or {@code null} if none.
173         * @param opts           The decryption options, empty or {@code null}
174         *                       if none.
175         *
176         * @throws JOSEException If the RSA JWK doesn't contain a private part
177         *                       or its extraction failed.
178         */
179        public RSADecrypter(final RSAKey rsaJWK,
180                            final Set<String> defCritHeaders,
181                            final Set<JWEDecrypterOption> opts)
182                throws JOSEException {
183
184                this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), defCritHeaders, opts);
185        }
186
187
188        /**
189         * Creates a new RSA decrypter. This constructor can also accept a
190         * private RSA key located in a PKCS#11 store that doesn't expose the
191         * private key parameters (such as a smart card or HSM).
192         *
193         * @param privateKey     The private RSA key. Its algorithm must be
194         *                       "RSA" and its length at least 2048 bits. Note
195         *                       that the length of an RSA key in a PKCS#11
196         *                       store cannot be checked. Must not be
197         *                       {@code null}.
198         * @param defCritHeaders The names of the critical header parameters
199         *                       that are deferred to the application for
200         *                       processing, empty set or {@code null} if none.
201         */
202        public RSADecrypter(final PrivateKey privateKey,
203                            final Set<String> defCritHeaders) {
204
205                this(privateKey, defCritHeaders, Collections.<JWEDecrypterOption>emptySet());
206        }
207
208
209        /**
210         * Creates a new RSA decrypter. This constructor can also accept a
211         * private RSA key located in a PKCS#11 store that doesn't expose the
212         * private key parameters (such as a smart card or HSM).
213         *
214         * @param privateKey     The private RSA key. Its algorithm must be
215         *                       "RSA" and its length at least 2048 bits. Note
216         *                       that the length of an RSA key in a PKCS#11
217         *                       store cannot be checked. Must not be
218         *                       {@code null}.
219         * @param defCritHeaders The names of the critical header parameters
220         *                       that are deferred to the application for
221         *                       processing, empty set or {@code null} if none.
222         * @param allowWeakKey   {@code true} to allow an RSA key shorter than
223         *                       2048 bits.
224         */
225        @Deprecated
226        public RSADecrypter(final PrivateKey privateKey,
227                            final Set<String> defCritHeaders,
228                            final boolean allowWeakKey) {
229
230                this(
231                        privateKey,
232                        defCritHeaders,
233                        allowWeakKey ? Collections.<JWEDecrypterOption>singleton(AllowWeakRSAKey.getInstance()) : Collections.<JWEDecrypterOption>emptySet()
234                );
235        }
236
237
238        /**
239         * Creates a new RSA decrypter. This constructor can also accept a
240         * private RSA key located in a PKCS#11 store that doesn't expose the
241         * private key parameters (such as a smart card or HSM).
242         *
243         * @param privateKey     The private RSA key. Its algorithm must be
244         *                       "RSA" and its length at least 2048 bits. Note
245         *                       that the length of an RSA key in a PKCS#11
246         *                       store cannot be checked. Must not be
247         *                       {@code null}.
248         * @param defCritHeaders The names of the critical header parameters
249         *                       that are deferred to the application for
250         *                       processing, empty set or {@code null} if none.
251         * @param opts           The decryption options, empty or {@code null}
252         *                       if none.
253         */
254        public RSADecrypter(final PrivateKey privateKey,
255                            final Set<String> defCritHeaders,
256                            final Set<JWEDecrypterOption> opts) {
257
258                super(null);
259
260                if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) {
261                        throw new IllegalArgumentException("The private key algorithm must be RSA");
262                }
263
264                this.opts = opts != null ? opts : Collections.<JWEDecrypterOption>emptySet();
265
266                OptionUtils.ensureMinRSAPrivateKeySize(privateKey, this.opts);
267
268                this.privateKey = privateKey;
269
270                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
271        }
272
273
274        /**
275         * Gets the private RSA key.
276         *
277         * @return The private RSA key. Casting to
278         *         {@link java.security.interfaces.RSAPrivateKey} may not be
279         *         possible if the key is located in a PKCS#11 store that
280         *         doesn't expose the private key parameters.
281         */
282        public PrivateKey getPrivateKey() {
283
284                return privateKey;
285        }
286
287
288        @Override
289        public Set<String> getProcessedCriticalHeaderParams() {
290
291                return critPolicy.getProcessedCriticalHeaderParams();
292        }
293
294
295        @Override
296        public Set<String> getDeferredCriticalHeaderParams() {
297
298                return critPolicy.getDeferredCriticalHeaderParams();
299        }
300
301
302        /**
303         * Decrypts the specified cipher text of a {@link JWEObject JWE Object}.
304         *
305         * @param header       The JSON Web Encryption (JWE) header. Must
306         *                     specify a supported JWE algorithm and method.
307         *                     Must not be {@code null}.
308         * @param encryptedKey The encrypted key, {@code null} if not required
309         *                     by the JWE algorithm.
310         * @param iv           The initialisation vector, {@code null} if not
311         *                     required by the JWE algorithm.
312         * @param cipherText   The cipher text to decrypt. Must not be
313         *                     {@code null}.
314         * @param authTag      The authentication tag, {@code null} if not
315         *                     required.
316         *
317         * @return The clear text.
318         *
319         * @throws JOSEException If the JWE algorithm or method is not
320         *                       supported, if a critical header parameter is
321         *                       not supported or marked for deferral to the
322         *                       application, or if decryption failed for some
323         *                       other reason.
324         */
325        @Deprecated
326        public byte[] decrypt(final JWEHeader header,
327                       final Base64URL encryptedKey,
328                       final Base64URL iv,
329                       final Base64URL cipherText,
330                       final Base64URL authTag)
331                throws JOSEException {
332
333                return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header));
334        }
335
336
337        private CipherMode resolveCipherModeForOAEP() {
338
339                if (opts.contains(CipherMode.ENCRYPT_DECRYPT)) {
340                        return CipherMode.ENCRYPT_DECRYPT;
341                } else {
342                        // The default cipher mode for RSA OAEP
343                        return CipherMode.WRAP_UNWRAP;
344                }
345                // The contains ENCRYPT_DECRYPT / WRAP_UNWRAP is not checked
346        }
347
348
349        @Override
350        public byte[] decrypt(final JWEHeader header,
351                              final Base64URL encryptedKey,
352                              final Base64URL iv,
353                              final Base64URL cipherText,
354                              final Base64URL authTag, 
355                              final byte[] aad) 
356                throws JOSEException {
357
358                // Validate required JWE parts
359                if (encryptedKey == null) {
360                        throw new JOSEException("Missing JWE encrypted key");
361                }       
362
363                if (iv == null) {
364                        throw new JOSEException("Missing JWE initialization vector (IV)");
365                }
366
367                if (authTag == null) {
368                        throw new JOSEException("Missing JWE authentication tag");
369                }
370
371                critPolicy.ensureHeaderPasses(header);
372                
373
374                // Derive the content encryption key
375                JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header);
376
377                SecretKey cek;
378
379                if (alg.equals(JWEAlgorithm.RSA1_5)) {
380
381                        int keyLength = header.getEncryptionMethod().cekBitLength();
382
383                        // Protect against MMA attack by generating random CEK to be used on decryption failure,
384                        // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html
385                        final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom());
386
387                        try {
388                                cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider());
389
390                                if (cek == null) {
391                                        // CEK length mismatch, signalled by null instead of
392                                        // exception to prevent MMA attack
393                                        cek = randomCEK;
394                                }
395
396                        } catch (Exception e) {
397                                // continue
398                                cekDecryptionException = e;
399                                cek = randomCEK;
400                        }
401                        
402                        cekDecryptionException = null;
403                
404                } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
405                        cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider());
406                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
407                        cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 256, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider());
408                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_384)) {
409                        cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 384, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider());
410                } else if (alg.equals(JWEAlgorithm.RSA_OAEP_512)){
411                        cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 512, resolveCipherModeForOAEP(), getJCAContext().getKeyEncryptionProvider());
412                } else {
413                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS));
414                }
415
416                int maxDecompressedLength = OptionUtils.resolveMaxDecompressedPlainTextLength(opts);
417
418                return ContentCryptoProvider.decrypt(header, aad, encryptedKey, iv, cipherText, authTag, cek, getJCAContext(), maxDecompressedLength);
419        }
420        
421        
422        /**
423         * Returns the Content Encryption Key (CEK) decryption exception if one
424         * was encountered during the last {@link #decrypt} run. Intended for
425         * logging and debugging purposes.
426         *
427         * @return The recorded exception, {@code null} if none.
428         */
429        public Exception getCEKDecryptionException() {
430                
431                return cekDecryptionException;
432        }
433}
434