001package com.nimbusds.jose.crypto; 002 003 004import javax.crypto.SecretKey; 005import javax.crypto.spec.SecretKeySpec; 006 007import net.jcip.annotations.ThreadSafe; 008 009import com.nimbusds.jose.*; 010import com.nimbusds.jose.jwk.OctetSequenceKey; 011import com.nimbusds.jose.util.Base64URL; 012import com.nimbusds.jose.util.ByteUtils; 013 014 015/** 016 * AES and AES GCM key wrap encrypter of {@link com.nimbusds.jose.JWEObject JWE 017 * objects}. Expects an AES key. 018 * 019 * <p>Encrypts the plain text with a generated AES key (the Content Encryption 020 * Key) according to the specified JOSE encryption method, then wraps the CEK 021 * with the specified AES key and returns it alongside the IV, cipher text and 022 * authentication tag. See RFC 7518, sections 023 * <a href="https://tools.ietf.org/html/rfc7518#section-4.4">4.4</a> and 024 * <a href="https://tools.ietf.org/html/rfc7518#section-4.7">4.7</a> for more 025 * information. 026 * 027 * <p>This class is thread-safe. 028 * 029 * <p>Supports the following key management algorithms: 030 * 031 * <ul> 032 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW} 033 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW} 034 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW} 035 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW} 036 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW} 037 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW} 038 * </ul> 039 * 040 * <p>Supports the following content encryption algorithms: 041 * 042 * <ul> 043 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 044 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 045 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 046 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 047 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 048 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 049 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 050 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 051 * </ul> 052 * 053 * @author Melisa Halsband 054 * @author Vladimir Dzhuvinov 055 * @version 2015-06-29 056 */ 057@ThreadSafe 058public class AESEncrypter extends AESCryptoProvider implements JWEEncrypter { 059 060 061 /** 062 * Algorithm family constants. 063 */ 064 private enum AlgFamily { 065 066 AESKW, AESGCMKW 067 } 068 069 070 /** 071 * Creates a new AES encrypter. 072 * 073 * @param kek The Key Encryption Key. Must be 128 bits (16 bytes), 192 074 * bits (24 bytes) or 256 bits (32 bytes). Must not be 075 * {@code null}. 076 * 077 * @throws KeyLengthException If the KEK length is invalid. 078 */ 079 public AESEncrypter(final SecretKey kek) 080 throws KeyLengthException { 081 082 super(kek); 083 } 084 085 /** 086 * Creates a new AES encrypter. 087 * 088 * @param keyBytes The Key Encryption Key, as a byte array. Must be 128 089 * bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 090 * bytes). Must not be {@code null}. 091 * 092 * @throws KeyLengthException If the KEK length is invalid. 093 */ 094 public AESEncrypter(final byte[] keyBytes) 095 throws KeyLengthException { 096 097 this(new SecretKeySpec(keyBytes, "AES")); 098 } 099 100 101 /** 102 * Creates a new AES encrypter. 103 * 104 * @param octJWK The Key Encryption Key, as a JWK. Must be 128 bits (16 105 * bytes), 192 bits (24 bytes), 256 bits (32 bytes), 384 106 * bits (48 bytes) or 512 bits (64 bytes) long. Must not 107 * be {@code null}. 108 * 109 * @throws KeyLengthException If the KEK length is invalid. 110 */ 111 public AESEncrypter(final OctetSequenceKey octJWK) 112 throws KeyLengthException { 113 114 this(octJWK.toSecretKey("AES")); 115 } 116 117 118 @Override 119 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 120 throws JOSEException { 121 122 final JWEAlgorithm alg = header.getAlgorithm(); 123 124 // Check the AES key size and determine the algorithm family 125 final AlgFamily algFamily; 126 127 if (alg.equals(JWEAlgorithm.A128KW)) { 128 129 if(ByteUtils.bitLength(getKey().getEncoded()) != 128){ 130 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128KW encryption"); 131 } 132 algFamily = AlgFamily.AESKW; 133 134 } else if (alg.equals(JWEAlgorithm.A192KW)) { 135 136 if(ByteUtils.bitLength(getKey().getEncoded()) != 192){ 137 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192KW encryption"); 138 } 139 algFamily = AlgFamily.AESKW; 140 141 } else if (alg.equals(JWEAlgorithm.A256KW)) { 142 143 if (ByteUtils.bitLength(getKey().getEncoded()) != 256) { 144 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256KW encryption"); 145 } 146 algFamily = AlgFamily.AESKW; 147 148 } else if (alg.equals(JWEAlgorithm.A128GCMKW)) { 149 150 if(ByteUtils.bitLength(getKey().getEncoded()) != 128){ 151 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128GCMKW encryption"); 152 } 153 algFamily = AlgFamily.AESGCMKW; 154 155 } else if (alg.equals(JWEAlgorithm.A192GCMKW)) { 156 157 if(ByteUtils.bitLength(getKey().getEncoded()) != 192){ 158 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192GCMKW encryption"); 159 } 160 algFamily = AlgFamily.AESGCMKW; 161 162 } else if (alg.equals(JWEAlgorithm.A256GCMKW)) { 163 164 if(ByteUtils.bitLength(getKey().getEncoded()) != 256){ 165 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256GCMKW encryption"); 166 } 167 algFamily = AlgFamily.AESGCMKW; 168 169 } else { 170 171 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 172 } 173 174 175 final JWEHeader updatedHeader; // We need to work on the header 176 final Base64URL encryptedKey; // The second JWE part 177 178 // Generate and encrypt the CEK according to the enc method 179 final EncryptionMethod enc = header.getEncryptionMethod(); 180 final SecretKey cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 181 182 if(AlgFamily.AESKW.equals(algFamily)) { 183 184 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, getKey(), getJCAContext().getKeyEncryptionProvider())); 185 updatedHeader = header; // simply copy ref 186 187 } else if(AlgFamily.AESGCMKW.equals(algFamily)) { 188 189 final byte[] keyIV = AESGCM.generateIV(getJCAContext().getSecureRandom()); 190 final AuthenticatedCipherText authCiphCEK = AESGCMKW.encryptCEK(cek, keyIV, getKey(), getJCAContext().getKeyEncryptionProvider()); 191 encryptedKey = Base64URL.encode(authCiphCEK.getCipherText()); 192 193 // Add iv and tag to the header 194 updatedHeader = new JWEHeader.Builder(header). 195 iv(Base64URL.encode(keyIV)). 196 authTag(Base64URL.encode(authCiphCEK.getAuthenticationTag())). 197 build(); 198 } else { 199 // This should never happen 200 throw new JOSEException("Unexpected JWE algorithm: " + alg); 201 } 202 203 return ContentCryptoProvider.encrypt(updatedHeader, clearText, cek, encryptedKey, getJCAContext()); 204 } 205}