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