001package com.nimbusds.jose.crypto; 002 003 004import java.security.interfaces.RSAPrivateKey; 005 006import javax.crypto.SecretKey; 007 008import com.nimbusds.jose.DefaultJWEHeaderFilter; 009import com.nimbusds.jose.EncryptionMethod; 010import com.nimbusds.jose.JOSEException; 011import com.nimbusds.jose.JWEAlgorithm; 012import com.nimbusds.jose.JWEDecrypter; 013import com.nimbusds.jose.JWEHeaderFilter; 014import com.nimbusds.jose.ReadOnlyJWEHeader; 015import com.nimbusds.jose.util.Base64URL; 016import com.nimbusds.jose.util.StringUtils; 017 018 019/** 020 * RSA decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class 021 * is thread-safe. 022 * 023 * <p>Supports the following JWE algorithms: 024 * 025 * <ul> 026 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} 027 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} 028 * </ul> 029 * 030 * <p>Supports the following encryption methods: 031 * 032 * <ul> 033 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 034 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 035 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 036 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 037 * </ul> 038 * 039 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames 040 * registered JWE header parameters}. Modify the {@link #getJWEHeaderFilter 041 * header filter} properties to restrict the acceptable JWE algorithms, 042 * encryption methods and header parameters, or to allow custom JWE header 043 * parameters. 044 * 045 * @author David Ortiz 046 * @author Vladimir Dzhuvinov 047 * @version $version$ (2013-10-07) 048 * 049 */ 050public class RSADecrypter extends RSACryptoProvider implements JWEDecrypter { 051 052 053 /** 054 * The JWE header filter. 055 */ 056 private final DefaultJWEHeaderFilter headerFilter; 057 058 059 /** 060 * The private RSA key. 061 */ 062 private RSAPrivateKey privateKey; 063 064 065 /** 066 * Creates a new RSA decrypter. 067 * 068 * @param privateKey The private RSA key. Must not be {@code null}. 069 */ 070 public RSADecrypter(final RSAPrivateKey privateKey) { 071 072 if (privateKey == null) { 073 074 throw new IllegalArgumentException("The private RSA key must not be null"); 075 } 076 077 this.privateKey = privateKey; 078 079 headerFilter = new DefaultJWEHeaderFilter(supportedAlgorithms(), supportedEncryptionMethods()); 080 } 081 082 083 /** 084 * Gets the private RSA key. 085 * 086 * @return The private RSA key. 087 */ 088 public RSAPrivateKey getPrivateKey() { 089 090 return privateKey; 091 } 092 093 094 @Override 095 public JWEHeaderFilter getJWEHeaderFilter() { 096 097 return headerFilter; 098 } 099 100 101 @Override 102 public byte[] decrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, 103 final Base64URL encryptedKey, 104 final Base64URL iv, 105 final Base64URL cipherText, 106 final Base64URL authTag) 107 throws JOSEException { 108 109 // Validate required JWE parts 110 if (encryptedKey == null) { 111 112 throw new JOSEException("The encrypted key must not be null"); 113 } 114 115 if (iv == null) { 116 117 throw new JOSEException("The initialization vector (IV) must not be null"); 118 } 119 120 if (authTag == null) { 121 122 throw new JOSEException("The authentication tag must not be null"); 123 } 124 125 126 // Derive the content encryption key 127 JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm(); 128 129 SecretKey cek = null; 130 131 if (alg.equals(JWEAlgorithm.RSA1_5)) { 132 133 int keyLength = readOnlyJWEHeader.getEncryptionMethod().cekBitLength(); 134 135 SecretKey randomCEK = AES.generateKey(keyLength); 136 137 try { 138 cek = RSA1_5.decryptCEK(privateKey, encryptedKey.decode(), keyLength); 139 140 } catch (Exception e) { 141 142 // Protect against MMA attack by generating random CEK on failure, 143 // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html 144 cek = randomCEK; 145 } 146 147 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 148 149 cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey.decode()); 150 151 } else { 152 153 throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5 or RSA_OAEP"); 154 } 155 156 // Compose the AAD 157 byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString()); 158 159 // Decrypt the cipher text according to the JWE enc 160 EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod(); 161 162 byte[] plainText; 163 164 if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A256CBC_HS512)) { 165 166 plainText = AESCBC.decryptAuthenticated(cek, iv.decode(), cipherText.decode(), aad, authTag.decode()); 167 168 } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A256GCM)) { 169 170 plainText = AESGCM.decrypt(cek, iv.decode(), cipherText.decode(), aad, authTag.decode()); 171 172 } else { 173 174 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A256CBC_HS512, A128GCM or A128GCM"); 175 } 176 177 178 // Apply decompression if requested 179 return DeflateHelper.applyDecompression(readOnlyJWEHeader, plainText); 180 } 181} 182