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