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