001package com.nimbusds.jose.crypto; 002 003 004import java.security.SecureRandom; 005import java.security.interfaces.RSAPublicKey; 006 007import javax.crypto.SecretKey; 008 009import com.nimbusds.jose.EncryptionMethod; 010import com.nimbusds.jose.JOSEException; 011import com.nimbusds.jose.JWEAlgorithm; 012import com.nimbusds.jose.JWECryptoParts; 013import com.nimbusds.jose.JWEEncrypter; 014import com.nimbusds.jose.JWEHeader; 015import com.nimbusds.jose.util.Base64URL; 016import com.nimbusds.jose.util.StringUtils; 017import net.jcip.annotations.ThreadSafe; 018 019 020/** 021 * RSA encrypter 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 * @author David Ortiz 046 * @author Vladimir Dzhuvinov 047 * @version $version$ (2014-08-20) 048 */ 049@ThreadSafe 050public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter { 051 052 053 /** 054 * The public RSA key. 055 */ 056 private final RSAPublicKey publicKey; 057 058 059 /** 060 * Creates a new RSA encrypter. 061 * 062 * @param publicKey The public RSA key. Must not be {@code null}. 063 */ 064 public RSAEncrypter(final RSAPublicKey publicKey) { 065 066 if (publicKey == null) { 067 throw new IllegalArgumentException("The public RSA key must not be null"); 068 } 069 070 this.publicKey = publicKey; 071 } 072 073 074 /** 075 * Gets the public RSA key. 076 * 077 * @return The public RSA key. 078 */ 079 public RSAPublicKey getPublicKey() { 080 081 return publicKey; 082 } 083 084 085 @Override 086 public JWECryptoParts encrypt(final JWEHeader header, final byte[] bytes) 087 throws JOSEException { 088 089 final JWEAlgorithm alg = header.getAlgorithm(); 090 final EncryptionMethod enc = header.getEncryptionMethod(); 091 092 // Generate and encrypt the CEK according to the enc method 093 final SecureRandom randomGen = getSecureRandom(); 094 final SecretKey cek = AES.generateKey(enc.cekBitLength(), randomGen); 095 096 Base64URL encryptedKey; // The second JWE part 097 098 if (alg.equals(JWEAlgorithm.RSA1_5)) { 099 100 encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek, keyEncryptionProvider)); 101 102 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 103 104 encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek, keyEncryptionProvider)); 105 106 } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) { 107 108 encryptedKey = Base64URL.encode(RSA_OAEP_256.encryptCEK(publicKey, cek, keyEncryptionProvider)); 109 110 } else { 111 112 throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5, RSA-OAEP, or RSA-OAEP-256"); 113 } 114 115 116 // Apply compression if instructed 117 byte[] plainText = DeflateHelper.applyCompression(header, bytes); 118 119 // Compose the AAD 120 byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString()); 121 122 // Encrypt the plain text according to the JWE enc 123 byte[] iv; 124 AuthenticatedCipherText authCipherText; 125 126 if (enc.equals(EncryptionMethod.A128CBC_HS256) || 127 enc.equals(EncryptionMethod.A192CBC_HS384) || 128 enc.equals(EncryptionMethod.A256CBC_HS512) ) { 129 130 iv = AESCBC.generateIV(randomGen); 131 132 authCipherText = AESCBC.encryptAuthenticated( 133 cek, iv, plainText, aad, 134 contentEncryptionProvider, macProvider); 135 136 } else if (enc.equals(EncryptionMethod.A128GCM) || 137 enc.equals(EncryptionMethod.A192GCM) || 138 enc.equals(EncryptionMethod.A256GCM) ) { 139 140 iv = AESGCM.generateIV(randomGen); 141 142 authCipherText = AESGCM.encrypt( 143 cek, iv, plainText, aad, 144 contentEncryptionProvider); 145 146 } else if (enc.equals(EncryptionMethod.A128CBC_HS256_DEPRECATED) || 147 enc.equals(EncryptionMethod.A256CBC_HS512_DEPRECATED) ) { 148 149 iv = AESCBC.generateIV(randomGen); 150 151 authCipherText = AESCBC.encryptWithConcatKDF( 152 header, cek, encryptedKey, iv, plainText, 153 contentEncryptionProvider, macProvider); 154 155 } else { 156 157 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A256GCM"); 158 } 159 160 return new JWECryptoParts(encryptedKey, 161 Base64URL.encode(iv), 162 Base64URL.encode(authCipherText.getCipherText()), 163 Base64URL.encode(authCipherText.getAuthenticationTag())); 164 } 165}