001package com.nimbusds.jose.crypto; 002 003 004import java.security.SecureRandom; 005 006import javax.crypto.SecretKey; 007 008import com.nimbusds.jose.EncryptionMethod; 009import com.nimbusds.jose.JOSEException; 010import com.nimbusds.jose.JWEAlgorithm; 011import com.nimbusds.jose.JWECryptoParts; 012import com.nimbusds.jose.JWEEncrypter; 013import com.nimbusds.jose.ReadOnlyJWEHeader; 014import com.nimbusds.jose.util.Base64URL; 015import com.nimbusds.jose.util.StringUtils; 016 017 018/** 019 * Direct encrypter of {@link com.nimbusds.jose.JWEObject JWE objects} with a 020 * shared symmetric key. This class is thread-safe. 021 * 022 * <p>Supports the following JWE algorithms: 023 * 024 * <ul> 025 * <li>{@link com.nimbusds.jose.JWEAlgorithm#DIR} 026 * </ul> 027 * 028 * <p>Supports the following encryption methods: 029 * 030 * <ul> 031 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 032 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 033 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 034 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 035 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 036 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 037 * </ul> 038 * 039 * @author Vladimir Dzhuvinov 040 * @version $version$ (2014-01-28) 041 */ 042public class DirectEncrypter extends DirectCryptoProvider implements JWEEncrypter { 043 044 045 /** 046 * Creates a new direct encrypter. 047 * 048 * @param key The shared symmetric key. Its algorithm must be "AES". 049 * Must be 128 bits (16 bytes), 256 bits (32 bytes) or 512 050 * bits (64 bytes) long. Must not be {@code null}. 051 * 052 * @throws JOSEException If the key length or algorithm are unexpected, 053 * or if the underlying secure random generator 054 * couldn't be instantiated. 055 */ 056 public DirectEncrypter(final SecretKey key) 057 throws JOSEException { 058 059 super(key); 060 } 061 062 063 /** 064 * Creates a new direct encrypter. 065 * 066 * @param keyBytes The shared symmetric key, as a byte array. Must be 067 * 128 bits (16 bytes), 256 bits (32 bytes) or 512 bits 068 * (64 bytes) long. Must not be {@code null}. 069 * 070 * @throws JOSEException If the key length or algorithm are unexpected, 071 * or if the underlying secure random generator 072 * couldn't be instantiated. 073 */ 074 public DirectEncrypter(final byte[] keyBytes) 075 throws JOSEException { 076 077 super(keyBytes); 078 } 079 080 081 @Override 082 public JWECryptoParts encrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, final byte[] bytes) 083 throws JOSEException { 084 085 JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm(); 086 087 if (! alg.equals(JWEAlgorithm.DIR)) { 088 089 throw new JOSEException("Unsupported JWE algorithm, must be \"dir\""); 090 } 091 092 // Check key length matches matches encryption method 093 EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod(); 094 095 if (enc.cekBitLength() != getKey().getEncoded().length * 8) { 096 097 throw new JOSEException("The Content Encryption Key (CEK) length must be " + enc.cekBitLength() + " bits for " + enc + " encryption"); 098 } 099 100 final Base64URL encryptedKey = null; // The second JWE part 101 102 103 // Apply compression if instructed 104 byte[] plainText = DeflateHelper.applyCompression(readOnlyJWEHeader, bytes); 105 106 107 // Compose the AAD 108 byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString()); 109 110 111 // Encrypt the plain text according to the JWE enc 112 byte[] iv; 113 AuthenticatedCipherText authCipherText; 114 SecureRandom randomGen = getSecureRandom(); 115 116 if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) { 117 118 iv = AESCBC.generateIV(randomGen); 119 120 authCipherText = AESCBC.encryptAuthenticated(getKey(), iv, plainText, aad, contentEncryptionProvider, macProvider); 121 122 } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) { 123 124 iv = AESGCM.generateIV(randomGen); 125 126 authCipherText = AESGCM.encrypt(getKey(), iv, plainText, aad, contentEncryptionProvider); 127 128 } else { 129 130 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A128GCM"); 131 } 132 133 return new JWECryptoParts(encryptedKey, 134 Base64URL.encode(iv), 135 Base64URL.encode(authCipherText.getCipherText()), 136 Base64URL.encode(authCipherText.getAuthenticationTag())); 137 } 138}