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