001package com.nimbusds.jose.crypto; 002 003 004import java.security.NoSuchAlgorithmException; 005import java.security.SecureRandom; 006import java.security.interfaces.RSAPublicKey; 007 008import javax.crypto.SecretKey; 009 010import com.nimbusds.jose.EncryptionMethod; 011import com.nimbusds.jose.JOSEException; 012import com.nimbusds.jose.JWEAlgorithm; 013import com.nimbusds.jose.JWECryptoParts; 014import com.nimbusds.jose.JWEEncrypter; 015import com.nimbusds.jose.ReadOnlyJWEHeader; 016import com.nimbusds.jose.util.Base64URL; 017import com.nimbusds.jose.util.StringUtils; 018 019 020 021/** 022 * RSA encrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class 023 * is thread-safe. 024 * 025 * <p>Supports the following JWE algorithms: 026 * 027 * <ul> 028 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA1_5} 029 * <li>{@link com.nimbusds.jose.JWEAlgorithm#RSA_OAEP} 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#A256CBC_HS512} 037 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 038 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 039 * </ul> 040 * 041 * @author David Ortiz 042 * @author Vladimir Dzhuvinov 043 * @version $version$ (2013-05-29) 044 */ 045public class RSAEncrypter extends RSACryptoProvider implements JWEEncrypter { 046 047 048 /** 049 * Random byte generator. 050 */ 051 private static SecureRandom randomGen; 052 053 054 /** 055 * The public RSA key. 056 */ 057 private final RSAPublicKey publicKey; 058 059 060 /** 061 * Initialises the secure random byte generator. 062 * 063 * @throws JOSEException If the secure random byte generator couldn't 064 * be instantiated. 065 */ 066 private void initSecureRandom() 067 throws JOSEException { 068 069 try { 070 randomGen = SecureRandom.getInstance("SHA1PRNG"); 071 072 } catch(NoSuchAlgorithmException e) { 073 074 throw new JOSEException(e.getMessage(), e); 075 } 076 } 077 078 079 /** 080 * Creates a new RSA encrypter. 081 * 082 * @param publicKey The public RSA key. Must not be {@code null}. 083 * 084 * @throws JOSEException If the underlying secure random generator 085 * couldn't be instantiated. 086 */ 087 public RSAEncrypter(final RSAPublicKey publicKey) 088 throws JOSEException { 089 090 if (publicKey == null) { 091 092 throw new IllegalArgumentException("The public RSA key must not be null"); 093 } 094 095 this.publicKey = publicKey; 096 097 098 if (randomGen == null) { 099 100 initSecureRandom(); 101 } 102 } 103 104 105 /** 106 * Gets the public RSA key. 107 * 108 * @return The public RSA key. 109 */ 110 public RSAPublicKey getPublicKey() { 111 112 return publicKey; 113 } 114 115 116 @Override 117 public JWECryptoParts encrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, final byte[] bytes) 118 throws JOSEException { 119 120 JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm(); 121 EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod(); 122 123 // Generate and encrypt the CEK according to the enc method 124 SecretKey cek = AES.generateKey(enc.cekBitLength()); 125 126 Base64URL encryptedKey = null; // The second JWE part 127 128 if (alg.equals(JWEAlgorithm.RSA1_5)) { 129 130 encryptedKey = Base64URL.encode(RSA1_5.encryptCEK(publicKey, cek)); 131 132 } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) { 133 134 encryptedKey = Base64URL.encode(RSA_OAEP.encryptCEK(publicKey, cek)); 135 136 } else { 137 138 throw new JOSEException("Unsupported JWE algorithm, must be RSA1_5 or RSA-OAEP"); 139 } 140 141 142 // Apply compression if instructed 143 byte[] plainText = DeflateHelper.applyCompression(readOnlyJWEHeader, bytes); 144 145 // Compose the AAD 146 byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString()); 147 148 // Encrypt the plain text according to the JWE enc 149 byte[] iv; 150 AuthenticatedCipherText authCipherText; 151 152 if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A256CBC_HS512)) { 153 154 iv = AESCBC.generateIV(randomGen); 155 156 authCipherText = AESCBC.encryptAuthenticated(cek, iv, plainText, aad); 157 158 } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A256GCM)) { 159 160 iv = AESGCM.generateIV(randomGen); 161 162 authCipherText = AESGCM.encrypt(cek, iv, plainText, aad); 163 164 } else { 165 166 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A256CBC_HS512, A128GCM or A128GCM"); 167 } 168 169 return new JWECryptoParts(encryptedKey, 170 Base64URL.encode(iv), 171 Base64URL.encode(authCipherText.getCipherText()), 172 Base64URL.encode(authCipherText.getAuthenticationTag())); 173 } 174}