package org.jboss.resteasy.jose.jwe.crypto; import org.jboss.resteasy.jose.Base64Url; import org.jboss.resteasy.jose.i18n.Messages; import org.jboss.resteasy.jose.jwe.Algorithm; import org.jboss.resteasy.jose.jwe.EncryptionMethod; import org.jboss.resteasy.jose.jwe.JWEHeader; import javax.crypto.SecretKey; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.interfaces.RSAPrivateKey; /** * RSA decrypter. This class * is thread-safe. * <p/> * <p>Supports the following JWE algorithms: * <p/> * <ul> * <li>RSA1_5 * <li>RSA_OAEP * </ul> * <p/> * <p>Supports the following encryption methods: * <p/> * <ul> * <li>A128CBC_HS256 * <li>A256CBC_HS512 * <li>A128GCM * <li>A256GCM * </ul> * * @author David Ortiz * @author Vladimir Dzhuvinov * @version $version$ (2013-05-29) */ public class RSADecrypter { public static byte[] decrypt(final JWEHeader readOnlyJWEHeader, final String encodedHeader, final String encodedEncryptedKey, final String encodedIv, final String encodedCipherText, final String encodedAuthTag, final RSAPrivateKey privateKey ) { // Validate required JWE parts if (encodedEncryptedKey == null) { throw new RuntimeException(Messages.MESSAGES.encryptedKeyMustNotBeNull()); } if (encodedIv == null) { throw new RuntimeException(Messages.MESSAGES.initializationVectorMustNotBeNull()); } if (encodedAuthTag == null) { throw new RuntimeException(Messages.MESSAGES.authenticationTagMustNotBeNull()); } // Derive the content encryption key Algorithm alg = readOnlyJWEHeader.getAlgorithm(); SecretKey cek = null; byte[] encryptedKey = Base64Url.decode(encodedEncryptedKey); byte[] aad = encodedHeader.getBytes(StandardCharsets.UTF_8); byte[] iv = Base64Url.decode(encodedIv); byte[] cipherText = Base64Url.decode(encodedCipherText); byte[] authTag = Base64Url.decode(encodedAuthTag); if (alg.equals(Algorithm.RSA1_5)) { int keyLength = readOnlyJWEHeader.getEncryptionMethod().getCekBitLength(); SecretKey randomCEK = AES.generateKey(keyLength); try { cek = RSA1_5.decryptCEK(privateKey, encryptedKey, keyLength); } catch (Exception e) { // Protect against MMA attack by generating random CEK on failure, // see http://www.ietf.org/mail-archive/web/jose/current/msg01832.html cek = randomCEK; } } else if (alg.equals(Algorithm.RSA_OAEP)) { cek = RSA_OAEP.decryptCEK(privateKey, encryptedKey); } else { throw new RuntimeException(Messages.MESSAGES.unsupportedJWEalgorithm()); } // Decrypt the cipher text according to the JWE enc EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod(); byte[] plainText; if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A256CBC_HS512)) { plainText = AESCBC.decryptAuthenticated(cek, iv, cipherText, aad, authTag); } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A256GCM)) { plainText = AESGCM.decrypt(cek, iv, cipherText, aad, authTag); } else { throw new RuntimeException(Messages.MESSAGES.unsupportedEncryptionMethod()); } // Apply decompression if requested return DeflateHelper.applyDecompression(readOnlyJWEHeader.getCompressionAlgorithm(), plainText); } }