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