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}. This class is thread-safe. 018 * 019 * <p>Supports the following key management algorithms: 020 * 021 * <ul> 022 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW} 023 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW} 024 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW} 025 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW} 026 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW} 027 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW} 028 * </ul> 029 * 030 * <p>Supports the following content encryption algorithms: 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 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 040 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 041 * </ul> 042 * 043 * @author Melisa Halsband 044 * @author Vladimir Dzhuvinov 045 * @version 2015-06-29 046 */ 047@ThreadSafe 048public class AESEncrypter extends AESCryptoProvider implements JWEEncrypter { 049 050 051 /** 052 * Algorithm family constants. 053 */ 054 private enum AlgFamily { 055 056 AESKW, AESGCMKW 057 } 058 059 060 /** 061 * Creates a new AES encrypter. 062 * 063 * @param kek The Key Encryption Key. Must be 128 bits (16 bytes), 192 064 * bits (24 bytes) or 256 bits (32 bytes). Must not be 065 * {@code null}. 066 * 067 * @throws KeyLengthException If the KEK length is invalid. 068 */ 069 public AESEncrypter(final SecretKey kek) 070 throws KeyLengthException { 071 072 super(kek); 073 } 074 075 /** 076 * Creates a new AES encrypter. 077 * 078 * @param keyBytes The Key Encryption Key, as a byte array. Must be 128 079 * bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 080 * bytes). Must not be {@code null}. 081 * 082 * @throws KeyLengthException If the KEK length is invalid. 083 */ 084 public AESEncrypter(final byte[] keyBytes) 085 throws KeyLengthException { 086 087 this(new SecretKeySpec(keyBytes, "AES")); 088 } 089 090 091 /** 092 * Creates a new AES encrypter. 093 * 094 * @param octJWK The Key Encryption Key, as a JWK. Must be 128 bits (16 095 * bytes), 192 bits (24 bytes), 256 bits (32 bytes), 384 096 * bits (48 bytes) or 512 bits (64 bytes) long. Must not 097 * be {@code null}. 098 * 099 * @throws KeyLengthException If the KEK length is invalid. 100 */ 101 public AESEncrypter(final OctetSequenceKey octJWK) 102 throws KeyLengthException { 103 104 this(octJWK.toSecretKey("AES")); 105 } 106 107 108 @Override 109 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 110 throws JOSEException { 111 112 final JWEAlgorithm alg = header.getAlgorithm(); 113 114 // Check the AES key size and determine the algorithm family 115 final AlgFamily algFamily; 116 117 if (alg.equals(JWEAlgorithm.A128KW)) { 118 119 if(ByteUtils.bitLength(getKey().getEncoded()) != 128){ 120 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128KW encryption"); 121 } 122 algFamily = AlgFamily.AESKW; 123 124 } else if (alg.equals(JWEAlgorithm.A192KW)) { 125 126 if(ByteUtils.bitLength(getKey().getEncoded()) != 192){ 127 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192KW encryption"); 128 } 129 algFamily = AlgFamily.AESKW; 130 131 } else if (alg.equals(JWEAlgorithm.A256KW)) { 132 133 if (ByteUtils.bitLength(getKey().getEncoded()) != 256) { 134 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256KW encryption"); 135 } 136 algFamily = AlgFamily.AESKW; 137 138 } else if (alg.equals(JWEAlgorithm.A128GCMKW)) { 139 140 if(ByteUtils.bitLength(getKey().getEncoded()) != 128){ 141 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128GCMKW encryption"); 142 } 143 algFamily = AlgFamily.AESGCMKW; 144 145 } else if (alg.equals(JWEAlgorithm.A192GCMKW)) { 146 147 if(ByteUtils.bitLength(getKey().getEncoded()) != 192){ 148 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192GCMKW encryption"); 149 } 150 algFamily = AlgFamily.AESGCMKW; 151 152 } else if (alg.equals(JWEAlgorithm.A256GCMKW)) { 153 154 if(ByteUtils.bitLength(getKey().getEncoded()) != 256){ 155 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256GCMKW encryption"); 156 } 157 algFamily = AlgFamily.AESGCMKW; 158 159 } else { 160 161 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 162 } 163 164 165 final JWEHeader updatedHeader; // We need to work on the header 166 final Base64URL encryptedKey; // The second JWE part 167 168 // Generate and encrypt the CEK according to the enc method 169 final EncryptionMethod enc = header.getEncryptionMethod(); 170 final SecretKey cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 171 172 if(AlgFamily.AESKW.equals(algFamily)) { 173 174 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, getKey(), getJCAContext().getKeyEncryptionProvider())); 175 updatedHeader = header; // simply copy ref 176 177 } else if(AlgFamily.AESGCMKW.equals(algFamily)) { 178 179 final byte[] keyIV = AESGCM.generateIV(getJCAContext().getSecureRandom()); 180 final AuthenticatedCipherText authCiphCEK = AESGCMKW.encryptCEK(cek, keyIV, getKey(), getJCAContext().getKeyEncryptionProvider()); 181 encryptedKey = Base64URL.encode(authCiphCEK.getCipherText()); 182 183 // Add iv and tag to the header 184 updatedHeader = new JWEHeader.Builder(header). 185 iv(Base64URL.encode(keyIV)). 186 authTag(Base64URL.encode(authCiphCEK.getAuthenticationTag())). 187 build(); 188 } else { 189 // This should never happen 190 throw new JOSEException("Unexpected JWE algorithm: " + alg); 191 } 192 193 return ContentCryptoProvider.encrypt(updatedHeader, clearText, cek, encryptedKey, getJCAContext()); 194 } 195}