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