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