001package com.nimbusds.jose.crypto; 002 003 004import java.security.SecureRandom; 005import java.security.interfaces.RSAPrivateKey; 006import java.util.HashSet; 007import java.util.Set; 008 009import javax.crypto.SecretKey; 010 011import com.nimbusds.jose.EncryptionMethod; 012import com.nimbusds.jose.JOSEException; 013import com.nimbusds.jose.JWEAlgorithm; 014import com.nimbusds.jose.JWEDecrypter; 015import com.nimbusds.jose.ReadOnlyJWEHeader; 016import com.nimbusds.jose.util.Base64URL; 017import com.nimbusds.jose.util.StringUtils; 018 019 020/** 021 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class 022 * is thread-safe. 023 * 024 * <p>Supports the following JWE algorithms: 025 * 026 * <ul> 027 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} 028 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} 029 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP_256} 030 * </ul> 031 * 032 * <p>Supports the following encryption methods: 033 * 034 * <ul> 035 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 036 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 037 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 038 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 039 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 040 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 041 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 042 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 043 * </ul> 044 * 045 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames 046 * registered JWE header parameters}. Use {@link #setAcceptedAlgorithms} and 047 * {@link #setAcceptedEncryptionMethods} to restrict the acceptable JWE 048 * algorithms and encryption methods. 049 * 050 * @author David Ortiz 051 * @author Vladimir Dzhuvinov 052 * @version $version$ (2014-05-23) 053 * 054 */ 055public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter { 056 057 058 /** 059 * The accepted JWE algorithms. 060 */ 061 private Set<JWEAlgorithm> acceptedAlgs = 062 new HashSet<JWEAlgorithm>(supportedAlgorithms()); 063 064 065 /** 066 * The accepted encryption methods. 067 */ 068 private Set<EncryptionMethod> acceptedEncs = 069 new HashSet<EncryptionMethod>(supportedEncryptionMethods()); 070 071 072 /** 073 * The critical header parameter checker. 074 */ 075 private final CriticalHeaderParameterChecker critParamChecker = 076 new CriticalHeaderParameterChecker(); 077 078 079 /** 080 * The private RSA key. 081 */ 082 private final RSAPrivateKey privateKey; 083 084 085 /** 086 * Creates a new RSA decrypter. 087 * 088 * @param privateKey The private RSA key. Must not be {@code null}. 089 */ 090 public RSADecrypter(final RSAPrivateKey privateKey) { 091 092 if (privateKey == null) { 093 094 throw new IllegalArgumentException("The private RSA key must not be null"); 095 } 096 097 this.privateKey = privateKey; 098 } 099 100 101 /** 102 * Gets the private RSA key. 103 * 104 * @return The private RSA key. 105 */ 106 public RSAPrivateKey getPrivateKey() { 107 108 return privateKey; 109 } 110 111 112 @Override 113 public Set<JWEAlgorithm> getAcceptedAlgorithms() { 114 115 return acceptedAlgs; 116 } 117 118 119 @Override 120 public void setAcceptedAlgorithms(final Set<JWEAlgorithm> acceptedAlgs) { 121 122 if (acceptedAlgs == null) { 123 throw new IllegalArgumentException("The accepted JWE algorithms must not be null"); 124 } 125 126 if (! supportedAlgorithms().containsAll(acceptedAlgs)) { 127 throw new IllegalArgumentException("Unsupported JWE algorithm(s)"); 128 } 129 130 this.acceptedAlgs = acceptedAlgs; 131 } 132 133 134 @Override 135 public Set<EncryptionMethod> getAcceptedEncryptionMethods() { 136 137 return acceptedEncs; 138 } 139 140 141 @Override 142 public void setAcceptedEncryptionMethods(final Set<EncryptionMethod> acceptedEncs) { 143 144 if (acceptedEncs == null) 145 throw new IllegalArgumentException("The accepted encryption methods must not be null"); 146 147 if (!supportedEncryptionMethods().containsAll(acceptedEncs)) { 148 throw new IllegalArgumentException("Unsupported encryption method(s)"); 149 } 150 151 this.acceptedEncs = acceptedEncs; 152 } 153 154 155 @Override 156 public Set<String> getIgnoredCriticalHeaderParameters() { 157 158 return critParamChecker.getIgnoredCriticalHeaders(); 159 } 160 161 162 @Override 163 public void setIgnoredCriticalHeaderParameters(final Set<String> headers) { 164 165 critParamChecker.setIgnoredCriticalHeaders(headers); 166 } 167 168 169 @Override 170 public byte[] decrypt(final ReadOnlyJWEHeader header, 171 final Base64URL encryptedKey, 172 final Base64URL iv, 173 final Base64URL cipherText, 174 final Base64URL authTag) 175 throws JOSEException { 176 177 // Validate required JWE parts 178 if (encryptedKey == null) { 179 180 throw new JOSEException("The encrypted key must not be null"); 181 } 182 183 if (iv == null) { 184 185 throw new JOSEException("The initialization vector (IV) must not be null"); 186 } 187 188 if (authTag == null) { 189 190 throw new JOSEException("The authentication tag must not be null"); 191 } 192 193 if (! critParamChecker.headerPasses(header)) { 194 195 throw new JOSEException("Unsupported critical header parameter"); 196 } 197 198 199 // Derive the content encryption key 200 JWEAlgorithm alg = header.getAlgorithm(); 201 202 SecretKey cek; 203 204 if (alg.equals(JWEAlgorithm.RSA1_5)) { 205 206 int keyLength = header.getEncryptionMethod().cekBitLength(); 207 208 // Protect against MMA attack by generating random CEK on failure, 209 // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html 210 SecureRandom randomGen = getSecureRandom(); 211 SecretKey randomCEK = AES.generateKey(keyLength, randomGen); 212 213 try { 214 cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength, keyEncryptionProvider); 215 216 if (cek == null) { 217 // CEK length mismatch, signalled by null instead of 218 // exception to prevent MMA attack 219 cek = randomCEK; 220 } 221 222 } catch (Exception e) { 223 // continue 224 cek = randomCEK; 225 } 226 227 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 228 229 cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode(), keyEncryptionProvider); 230 231 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 232 233 cek = RSA_OAEP_256.decryptCEK(privateKey, encryptedKey.decode(), keyEncryptionProvider); 234 235 } else { 236 237 throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5 or RSA_OAEP"); 238 } 239 240 // Compose the AAD 241 byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString()); 242 243 // Decrypt the cipher text according to the JWE enc 244 EncryptionMethod enc = header.getEncryptionMethod(); 245 246 byte[] plainText; 247 248 if (enc.equals(EncryptionMethod.A128CBC_HS256) || 249 enc.equals(EncryptionMethod.A192CBC_HS384) || 250 enc.equals(EncryptionMethod.A256CBC_HS512) ) { 251 252 plainText = AESCBC.decryptAuthenticated( 253 cek, 254 iv.decode(), 255 cipherText.decode(), 256 aad, 257 authTag.decode(), 258 contentEncryptionProvider, 259 macProvider); 260 261 } else if (enc.equals(EncryptionMethod.A128GCM) || 262 enc.equals(EncryptionMethod.A192GCM) || 263 enc.equals(EncryptionMethod.A256GCM) ) { 264 265 plainText = AESGCM.decrypt( 266 cek, 267 iv.decode(), 268 cipherText.decode(), 269 aad, 270 authTag.decode(), 271 contentEncryptionProvider); 272 273 } else if (enc.equals(EncryptionMethod.A128CBC_HS256_DEPRECATED) || 274 enc.equals(EncryptionMethod.A256CBC_HS512_DEPRECATED) ) { 275 276 plainText = AESCBC.decryptWithConcatKDF( 277 header, 278 cek, 279 encryptedKey, 280 iv, 281 cipherText, 282 authTag, 283 contentEncryptionProvider, 284 macProvider); 285 286 } else { 287 288 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A256GCM"); 289 } 290 291 292 // Apply decompression if requested 293 return DeflateHelper.applyDecompression(header, plainText); 294 } 295} 296