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