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 * @version 2021-09-23 076 */ 077@ThreadSafe 078public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 079 080 081 /** 082 * The critical header policy. 083 */ 084 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 085 086 087 /** 088 * The private RSA key. 089 */ 090 private final PrivateKey privateKey; 091 092 093 /** 094 * Stores a CEK decryption exception is one was encountered during the 095 * last {@link #decrypt} run. 096 */ 097 private Exception cekDecryptionException; 098 099 100 /** 101 * Creates a new RSA decrypter. This constructor can also accept a 102 * private RSA key located in a PKCS#11 store that doesn't expose the 103 * private key parameters (such as a smart card or HSM). 104 * 105 * @param privateKey The private RSA key. Its algorithm must be "RSA" 106 * and its length at least 2048 bits. Note that the 107 * length of an RSA key in a PKCS#11 store cannot be 108 * checked. Must not be {@code null}. 109 */ 110 public RSADecrypter(final PrivateKey privateKey) { 111 112 this(privateKey, null, false); 113 } 114 115 116 /** 117 * Creates a new RSA decrypter. 118 * 119 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 120 * a private part. Its length must be at least 2048 bits. 121 * Note that the length of an RSA key in a PKCS#11 store 122 * cannot be checked. Must not be {@code null}. 123 * 124 * @throws JOSEException If the RSA JWK doesn't contain a private part 125 * or its extraction failed. 126 */ 127 public RSADecrypter(final RSAKey rsaJWK) 128 throws JOSEException { 129 130 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK)); 131 } 132 133 134 /** 135 * Creates a new RSA decrypter. This constructor can also accept a 136 * private RSA key located in a PKCS#11 store that doesn't expose the 137 * private key parameters (such as a smart card or HSM). 138 * 139 * @param privateKey The private RSA key. Its algorithm must be 140 * "RSA" and its length at least 2048 bits. Note 141 * that the length of an RSA key in a PKCS#11 142 * store cannot be checked. Must not be 143 * {@code null}. 144 * @param defCritHeaders The names of the critical header parameters 145 * that are deferred to the application for 146 * processing, empty set or {@code null} if none. 147 */ 148 public RSADecrypter(final PrivateKey privateKey, 149 final Set<String> defCritHeaders) { 150 151 this(privateKey, defCritHeaders, false); 152 } 153 154 155 /** 156 * Creates a new RSA decrypter. This constructor can also accept a 157 * private RSA key located in a PKCS#11 store that doesn't expose the 158 * private key parameters (such as a smart card or HSM). 159 * 160 * @param privateKey The private RSA key. Its algorithm must be 161 * "RSA" and its length at least 2048 bits. Note 162 * that the length of an RSA key in a PKCS#11 163 * store cannot be checked. Must not be 164 * {@code null}. 165 * @param defCritHeaders The names of the critical header parameters 166 * that are deferred to the application for 167 * processing, empty set or {@code null} if none. 168 * @param allowWeakKey {@code true} to allow an RSA key shorter than 169 * 2048 bits. 170 */ 171 public RSADecrypter(final PrivateKey privateKey, 172 final Set<String> defCritHeaders, 173 final boolean allowWeakKey) { 174 175 if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) { 176 throw new IllegalArgumentException("The private key algorithm must be RSA"); 177 } 178 179 if (! allowWeakKey) { 180 181 int keyBitLength = RSAKeyUtils.keyBitLength(privateKey); 182 183 if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) { 184 throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits"); 185 } 186 } 187 188 this.privateKey = privateKey; 189 190 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 191 } 192 193 194 /** 195 * Gets the private RSA key. 196 * 197 * @return The private RSA key. Casting to 198 * {@link java.security.interfaces.RSAPrivateKey} may not be 199 * possible if the key is located in a PKCS#11 store that 200 * doesn't expose the private key parameters. 201 */ 202 public PrivateKey getPrivateKey() { 203 204 return privateKey; 205 } 206 207 208 @Override 209 public Set<String> getProcessedCriticalHeaderParams() { 210 211 return critPolicy.getProcessedCriticalHeaderParams(); 212 } 213 214 215 @Override 216 public Set<String> getDeferredCriticalHeaderParams() { 217 218 return critPolicy.getProcessedCriticalHeaderParams(); 219 } 220 221 222 @Override 223 public byte[] decrypt(final JWEHeader header, 224 final Base64URL encryptedKey, 225 final Base64URL iv, 226 final Base64URL cipherText, 227 final Base64URL authTag) 228 throws JOSEException { 229 230 // Validate required JWE parts 231 if (encryptedKey == null) { 232 throw new JOSEException("Missing JWE encrypted key"); 233 } 234 235 if (iv == null) { 236 throw new JOSEException("Missing JWE initialization vector (IV)"); 237 } 238 239 if (authTag == null) { 240 throw new JOSEException("Missing JWE authentication tag"); 241 } 242 243 critPolicy.ensureHeaderPasses(header); 244 245 246 // Derive the content encryption key 247 JWEAlgorithm alg = header.getAlgorithm(); 248 249 SecretKey cek; 250 251 if (alg.equals(JWEAlgorithm.RSA1_5)) { 252 253 int keyLength = header.getEncryptionMethod().cekBitLength(); 254 255 // Protect against MMA attack by generating random CEK to be used on decryption failure, 256 // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html 257 final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom()); 258 259 try { 260 cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider()); 261 262 if (cek == null) { 263 // CEK length mismatch, signalled by null instead of 264 // exception to prevent MMA attack 265 cek = randomCEK; 266 } 267 268 } catch (Exception e) { 269 // continue 270 cekDecryptionException = e; 271 cek = randomCEK; 272 } 273 274 cekDecryptionException = null; 275 276 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 277 cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 278 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 279 cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 256, getJCAContext().getKeyEncryptionProvider()); 280 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_384)) { 281 cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 384, getJCAContext().getKeyEncryptionProvider()); 282 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_512)){ 283 cek = RSA_OAEP_SHA2.decryptCEK(privateKey, encryptedKey.decode(), 512, getJCAContext().getKeyEncryptionProvider()); 284 } else { 285 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 286 } 287 288 return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext()); 289 } 290 291 292 /** 293 * Returns the Content Encryption Key (CEK) decryption exception if one 294 * was encountered during the last {@link #decrypt} run. Intended for 295 * logging and debugging purposes. 296 * 297 * @return The recorded exception, {@code null} if none. 298 */ 299 public Exception getCEKDecryptionException() { 300 301 return cekDecryptionException; 302 } 303} 304