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), 192 bits (24 bytes), 256 050 * bits (32 bytes), 384 bits (48 bytes) or 512 bits 051 * (64 bytes) long. Must not be {@code null}. 052 * 053 * @throws JOSEException If the key length or algorithm are unexpected, 054 * or if the underlying secure random generator 055 * couldn't be instantiated. 056 */ 057 public DirectEncrypter(final SecretKey key) 058 throws JOSEException { 059 060 super(key); 061 } 062 063 064 /** 065 * Creates a new direct encrypter. 066 * 067 * @param keyBytes The shared symmetric key, as a byte array. Must be 068 * 128 bits (16 bytes), 192 bits (24 bytes), 256 bits 069 * (32 bytes), 384 bits (48 bytes) or 512 bits (64 070 * bytes) long. Must not be {@code null}. 071 * 072 * @throws JOSEException If the key length or algorithm are unexpected, 073 * or if the underlying secure random generator 074 * couldn't be instantiated. 075 */ 076 public DirectEncrypter(final byte[] keyBytes) 077 throws JOSEException { 078 079 super(keyBytes); 080 } 081 082 083 @Override 084 public JWECryptoParts encrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, final byte[] bytes) 085 throws JOSEException { 086 087 JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm(); 088 089 if (! alg.equals(JWEAlgorithm.DIR)) { 090 091 throw new JOSEException("Unsupported JWE algorithm, must be \"dir\""); 092 } 093 094 // Check key length matches matches encryption method 095 EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod(); 096 097 if (enc.cekBitLength() != getKey().getEncoded().length * 8) { 098 099 throw new JOSEException("The Content Encryption Key (CEK) length must be " + enc.cekBitLength() + " bits for " + enc + " encryption"); 100 } 101 102 final Base64URL encryptedKey = null; // The second JWE part 103 104 105 // Apply compression if instructed 106 byte[] plainText = DeflateHelper.applyCompression(readOnlyJWEHeader, bytes); 107 108 109 // Compose the AAD 110 byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString()); 111 112 113 // Encrypt the plain text according to the JWE enc 114 byte[] iv; 115 AuthenticatedCipherText authCipherText; 116 SecureRandom randomGen = getSecureRandom(); 117 118 if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) { 119 120 iv = AESCBC.generateIV(randomGen); 121 122 authCipherText = AESCBC.encryptAuthenticated(getKey(), iv, plainText, aad, contentEncryptionProvider, macProvider); 123 124 } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) { 125 126 iv = AESGCM.generateIV(randomGen); 127 128 authCipherText = AESGCM.encrypt(getKey(), iv, plainText, aad, contentEncryptionProvider); 129 130 } else { 131 132 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A128GCM"); 133 } 134 135 return new JWECryptoParts(encryptedKey, 136 Base64URL.encode(iv), 137 Base64URL.encode(authCipherText.getCipherText()), 138 Base64URL.encode(authCipherText.getAuthenticationTag())); 139 } 140}