001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020import java.util.Arrays; 021import javax.crypto.SecretKey; 022import javax.crypto.spec.SecretKeySpec; 023 024import com.nimbusds.jose.*; 025import com.nimbusds.jose.crypto.impl.*; 026import com.nimbusds.jose.jwk.OctetSequenceKey; 027import com.nimbusds.jose.util.Base64URL; 028import com.nimbusds.jose.util.ByteUtils; 029import com.nimbusds.jose.util.Container; 030import net.jcip.annotations.ThreadSafe; 031 032 033/** 034 * AES and AES GCM key wrap encrypter of {@link com.nimbusds.jose.JWEObject JWE 035 * objects}. Expects an AES key. 036 * 037 * <p>Encrypts the plain text with a generated AES key (the Content Encryption 038 * Key) according to the specified JOSE encryption method, then wraps the CEK 039 * with the specified AES key and returns it alongside the IV, cipher text and 040 * authentication tag. See RFC 7518, sections 041 * <a href="https://tools.ietf.org/html/rfc7518#section-4.4">4.4</a> and 042 * <a href="https://tools.ietf.org/html/rfc7518#section-4.7">4.7</a> for more 043 * information. 044 * 045 * <p>This class is thread-safe. 046 * 047 * <p>Supports the following key management algorithms: 048 * 049 * <ul> 050 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW} 051 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW} 052 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW} 053 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW} 054 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW} 055 * <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW} 056 * </ul> 057 * 058 * <p>Supports the following content encryption algorithms: 059 * 060 * <ul> 061 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 062 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 063 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 064 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 065 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 066 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 067 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 068 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 069 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 070 * </ul> 071 * 072 * @author Melisa Halsband 073 * @author Vladimir Dzhuvinov 074 * @author Dimitar A. Stoikov 075 * @author Egor Puzanov 076 * @version 2023-09-10 077 */ 078@ThreadSafe 079public class AESEncrypter extends AESCryptoProvider implements JWEEncrypter { 080 081 082 /** 083 * Algorithm family constants. 084 */ 085 private enum AlgFamily { 086 087 AESKW, AESGCMKW 088 } 089 090 091 /** 092 * Creates a new AES encrypter. 093 * 094 * @param kek The Key Encryption Key. Must be 128 bits 095 * (16 bytes), 192 bits (24 bytes) or 096 * 256 bits (32 bytes). Must not be 097 * {@code null}. 098 * @param contentEncryptionKey The content encryption key (CEK) to use. 099 * If specified its algorithm must be "AES" 100 * or "ChaCha20" and its length must match 101 * the expected for the JWE encryption 102 * method ("enc"). If {@code null} a CEK 103 * will be generated for each JWE. 104 * 105 * @throws KeyLengthException If the KEK length is invalid. 106 */ 107 public AESEncrypter(final SecretKey kek, final SecretKey contentEncryptionKey) 108 throws KeyLengthException { 109 110 super(kek, contentEncryptionKey); 111 } 112 113 114 /** 115 * Creates a new AES encrypter. 116 * 117 * @param kek The Key Encryption Key. Must be 128 bits (16 bytes), 192 118 * bits (24 bytes) or 256 bits (32 bytes). Must not be 119 * {@code null}. 120 * 121 * @throws KeyLengthException If the KEK length is invalid. 122 */ 123 public AESEncrypter(final SecretKey kek) 124 throws KeyLengthException { 125 126 this(kek, null); 127 } 128 129 /** 130 * Creates a new AES encrypter. 131 * 132 * @param keyBytes The Key Encryption Key, as a byte array. Must be 128 133 * bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 134 * bytes). Must not be {@code null}. 135 * 136 * @throws KeyLengthException If the KEK length is invalid. 137 */ 138 public AESEncrypter(final byte[] keyBytes) 139 throws KeyLengthException { 140 141 this(new SecretKeySpec(keyBytes, "AES")); 142 } 143 144 145 /** 146 * Creates a new AES encrypter. 147 * 148 * @param octJWK The Key Encryption Key, as a JWK. Must be 128 bits (16 149 * bytes), 192 bits (24 bytes), 256 bits (32 bytes), 384 150 * bits (48 bytes) or 512 bits (64 bytes) long. Must not 151 * be {@code null}. 152 * 153 * @throws KeyLengthException If the KEK length is invalid. 154 */ 155 public AESEncrypter(final OctetSequenceKey octJWK) 156 throws KeyLengthException { 157 158 this(octJWK.toSecretKey("AES")); 159 } 160 161 162 /** 163 * Encrypts the specified clear text of a {@link JWEObject JWE object}. 164 * 165 * @param header The JSON Web Encryption (JWE) header. Must specify 166 * a supported JWE algorithm and method. Must not be 167 * {@code null}. 168 * @param clearText The clear text to encrypt. Must not be {@code null}. 169 * 170 * @return The resulting JWE crypto parts. 171 * 172 * @throws JOSEException If the JWE algorithm or method is not 173 * supported or if encryption failed for some 174 * other internal reason. 175 */ 176 @Deprecated 177 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 178 throws JOSEException { 179 180 return encrypt(header, clearText, AAD.compute(header)); 181 } 182 183 184 @Override 185 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText, final byte[] aad) 186 throws JOSEException { 187 188 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 189 final EncryptionMethod enc = header.getEncryptionMethod(); 190 191 // Check the AES key size and determine the algorithm family 192 final AlgFamily algFamily; 193 194 if (alg.equals(JWEAlgorithm.A128KW)) { 195 196 if(ByteUtils.safeBitLength(getKey().getEncoded()) != 128){ 197 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128KW encryption"); 198 } 199 algFamily = AlgFamily.AESKW; 200 201 } else if (alg.equals(JWEAlgorithm.A192KW)) { 202 203 if(ByteUtils.safeBitLength(getKey().getEncoded()) != 192){ 204 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192KW encryption"); 205 } 206 algFamily = AlgFamily.AESKW; 207 208 } else if (alg.equals(JWEAlgorithm.A256KW)) { 209 210 if (ByteUtils.safeBitLength(getKey().getEncoded()) != 256) { 211 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256KW encryption"); 212 } 213 algFamily = AlgFamily.AESKW; 214 215 } else if (alg.equals(JWEAlgorithm.A128GCMKW)) { 216 217 if(ByteUtils.safeBitLength(getKey().getEncoded()) != 128){ 218 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128GCMKW encryption"); 219 } 220 algFamily = AlgFamily.AESGCMKW; 221 222 } else if (alg.equals(JWEAlgorithm.A192GCMKW)) { 223 224 if(ByteUtils.safeBitLength(getKey().getEncoded()) != 192){ 225 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192GCMKW encryption"); 226 } 227 algFamily = AlgFamily.AESGCMKW; 228 229 } else if (alg.equals(JWEAlgorithm.A256GCMKW)) { 230 231 if(ByteUtils.safeBitLength(getKey().getEncoded()) != 256){ 232 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256GCMKW encryption"); 233 } 234 algFamily = AlgFamily.AESGCMKW; 235 236 } else { 237 238 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 239 } 240 241 242 final JWEHeader updatedHeader; // We need to work on the header 243 final Base64URL encryptedKey; // The second JWE part 244 final SecretKey cek = getCEK(enc); // Generate and encrypt the CEK according to the enc method 245 246 if(AlgFamily.AESKW.equals(algFamily)) { 247 248 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, getKey(), getJCAContext().getKeyEncryptionProvider())); 249 updatedHeader = header; // simply copy ref 250 251 } else if(AlgFamily.AESGCMKW.equals(algFamily)) { 252 253 final Container<byte[]> keyIV = new Container<>(AESGCM.generateIV(getJCAContext().getSecureRandom())); 254 final AuthenticatedCipherText authCiphCEK = AESGCMKW.encryptCEK(cek, keyIV, getKey(), getJCAContext().getKeyEncryptionProvider()); 255 encryptedKey = Base64URL.encode(authCiphCEK.getCipherText()); 256 257 // Add iv and tag to the header 258 updatedHeader = new JWEHeader.Builder(header). 259 iv(Base64URL.encode(keyIV.get())). 260 authTag(Base64URL.encode(authCiphCEK.getAuthenticationTag())). 261 build(); 262 } else { 263 // This should never happen 264 throw new JOSEException("Unexpected JWE algorithm: " + alg); 265 } 266 267 // for JWEObject we need update the AAD as well 268 final byte[] updatedAAD = Arrays.equals(AAD.compute(header), aad) ? AAD.compute(updatedHeader) : aad; 269 270 return ContentCryptoProvider.encrypt(updatedHeader, clearText, updatedAAD, cek, encryptedKey, getJCAContext()); 271 } 272}