001package com.nimbusds.jose.crypto; 002 003 004import java.security.PrivateKey; 005import java.util.Set; 006import javax.crypto.SecretKey; 007 008import com.nimbusds.jose.*; 009import com.nimbusds.jose.jwk.RSAKey; 010import com.nimbusds.jose.util.Base64URL; 011import net.jcip.annotations.ThreadSafe; 012 013 014/** 015 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. Expects a 016 * private RSA key. 017 * 018 * <p>Decrypts the encrypted Content Encryption Key (CEK) with the private RSA 019 * key, and then uses the CEK along with the IV and authentication tag to 020 * decrypt the cipher text. See RFC 7518, sections 021 * <a href="https://tools.ietf.org/html/rfc7518#section-4.2">4.2</a> and 022 * <a href="https://tools.ietf.org/html/rfc7518#section-4.3">4.3</a> for more 023 * information. 024 * 025 * <p>This class is thread-safe. 026 * 027 * <p>Supports the following key management algorithms: 028 * 029 * <ul> 030 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} 031 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} 032 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 033 * </ul> 034 * 035 * <p>Supports the following content encryption algorithms: 036 * 037 * <ul> 038 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 039 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 040 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 041 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 042 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 043 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 044 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 045 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 046 * </ul> 047 * 048 * @author David Ortiz 049 * @author Vladimir Dzhuvinov 050 * @author Dimitar A. Stoikov 051 * @version 2016-06-29 052 */ 053@ThreadSafe 054public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 055 056 057 /** 058 * The critical header policy. 059 */ 060 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 061 062 063 /** 064 * The private RSA key. 065 */ 066 private final PrivateKey privateKey; 067 068 069 /** 070 * Creates a new RSA decrypter. 071 * 072 * @param privateKey The private RSA key. Must not be {@code null}. 073 */ 074 public RSADecrypter(final PrivateKey privateKey) { 075 076 this(privateKey, null); 077 } 078 079 080 /** 081 * Creates a new RSA decrypter. 082 * 083 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain a private 084 * part. Must not be {@code null}. 085 * 086 * @throws JOSEException If the RSA JWK doesn't contain a private part 087 * or its extraction failed. 088 */ 089 public RSADecrypter(final RSAKey rsaJWK) 090 throws JOSEException { 091 092 if (! rsaJWK.isPrivate()) { 093 throw new JOSEException("The RSA JWK doesn't contain a private part"); 094 } 095 096 privateKey = rsaJWK.toRSAPrivateKey(); 097 } 098 099 100 /** 101 * Creates a new RSA decrypter. 102 * 103 * @param privateKey The private RSA key. Its algorithm must be 104 * "RSA". Must not be {@code null}. 105 * @param defCritHeaders The names of the critical header parameters 106 * that are deferred to the application for 107 * processing, empty set or {@code null} if none. 108 */ 109 public RSADecrypter(final PrivateKey privateKey, 110 final Set<String> defCritHeaders) { 111 112 if (privateKey == null) { 113 throw new IllegalArgumentException("The private RSA key must not be null"); 114 } 115 116 if (! privateKey.getAlgorithm().equalsIgnoreCase("RSA")) { 117 throw new IllegalArgumentException("The private key algorithm must be RSA"); 118 } 119 120 this.privateKey = privateKey; 121 122 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 123 } 124 125 126 /** 127 * Gets the private RSA key. 128 * 129 * @return The private RSA key. Casting to 130 * {@link java.security.interfaces.RSAPrivateKey} may not be 131 * possible if the key is backed by a key store that doesn't 132 * expose the private key parameters. 133 */ 134 public PrivateKey getPrivateKey() { 135 136 return privateKey; 137 } 138 139 140 @Override 141 public Set<String> getProcessedCriticalHeaderParams() { 142 143 return critPolicy.getProcessedCriticalHeaderParams(); 144 } 145 146 147 @Override 148 public Set<String> getDeferredCriticalHeaderParams() { 149 150 return critPolicy.getProcessedCriticalHeaderParams(); 151 } 152 153 154 @Override 155 public byte[] decrypt(final JWEHeader header, 156 final Base64URL encryptedKey, 157 final Base64URL iv, 158 final Base64URL cipherText, 159 final Base64URL authTag) 160 throws JOSEException { 161 162 // Validate required JWE parts 163 if (encryptedKey == null) { 164 throw new JOSEException("Missing JWE encrypted key"); 165 } 166 167 if (iv == null) { 168 throw new JOSEException("Missing JWE initialization vector (IV)"); 169 } 170 171 if (authTag == null) { 172 throw new JOSEException("Missing JWE authentication tag"); 173 } 174 175 critPolicy.ensureHeaderPasses(header); 176 177 178 // Derive the content encryption key 179 JWEAlgorithm alg = header.getAlgorithm(); 180 181 SecretKey cek; 182 183 if (alg.equals(JWEAlgorithm.RSA1_5)) { 184 185 int keyLength = header.getEncryptionMethod().cekBitLength(); 186 187 // Protect against MMA attack by generating random CEK on failure, 188 // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html 189 final SecretKey randomCEK = ContentCryptoProvider.generateCEK(header.getEncryptionMethod(), getJCAContext().getSecureRandom()); 190 191 try { 192 cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, getJCAContext().getKeyEncryptionProvider()); 193 194 if (cek == null) { 195 // CEK length mismatch, signalled by null instead of 196 // exception to prevent MMA attack 197 cek = randomCEK; 198 } 199 200 } catch (Exception e) { 201 // continue 202 cek = randomCEK; 203 } 204 205 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 206 207 cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 208 209 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 210 211 cek = RSA_OAEP_256.decryptCEK(privateKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 212 213 } else { 214 215 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 216 } 217 218 return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext()); 219 } 220} 221