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 020 021import javax.crypto.SecretKey; 022import javax.crypto.spec.SecretKeySpec; 023 024import net.jcip.annotations.ThreadSafe; 025 026import com.nimbusds.jose.*; 027import com.nimbusds.jose.jwk.OctetSequenceKey; 028import com.nimbusds.jose.util.Base64URL; 029import com.nimbusds.jose.util.ByteUtils; 030import com.nimbusds.jose.util.Container; 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 * </ul> 070 * 071 * @author Melisa Halsband 072 * @author Vladimir Dzhuvinov 073 * @author Dimitar A. Stoikov 074 * @version 2016-10-13 075 */ 076@ThreadSafe 077public class AESEncrypter extends AESCryptoProvider implements JWEEncrypter { 078 079 080 /** 081 * Algorithm family constants. 082 */ 083 private enum AlgFamily { 084 085 AESKW, AESGCMKW 086 } 087 088 089 /** 090 * Creates a new AES encrypter. 091 * 092 * @param kek The Key Encryption Key. Must be 128 bits (16 bytes), 192 093 * bits (24 bytes) or 256 bits (32 bytes). Must not be 094 * {@code null}. 095 * 096 * @throws KeyLengthException If the KEK length is invalid. 097 */ 098 public AESEncrypter(final SecretKey kek) 099 throws KeyLengthException { 100 101 super(kek); 102 } 103 104 /** 105 * Creates a new AES encrypter. 106 * 107 * @param keyBytes The Key Encryption Key, as a byte array. Must be 128 108 * bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 109 * bytes). Must not be {@code null}. 110 * 111 * @throws KeyLengthException If the KEK length is invalid. 112 */ 113 public AESEncrypter(final byte[] keyBytes) 114 throws KeyLengthException { 115 116 this(new SecretKeySpec(keyBytes, "AES")); 117 } 118 119 120 /** 121 * Creates a new AES encrypter. 122 * 123 * @param octJWK The Key Encryption Key, as a JWK. Must be 128 bits (16 124 * bytes), 192 bits (24 bytes), 256 bits (32 bytes), 384 125 * bits (48 bytes) or 512 bits (64 bytes) long. Must not 126 * be {@code null}. 127 * 128 * @throws KeyLengthException If the KEK length is invalid. 129 */ 130 public AESEncrypter(final OctetSequenceKey octJWK) 131 throws KeyLengthException { 132 133 this(octJWK.toSecretKey("AES")); 134 } 135 136 137 @Override 138 public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText) 139 throws JOSEException { 140 141 final JWEAlgorithm alg = header.getAlgorithm(); 142 143 // Check the AES key size and determine the algorithm family 144 final AlgFamily algFamily; 145 146 if (alg.equals(JWEAlgorithm.A128KW)) { 147 148 if(ByteUtils.bitLength(getKey().getEncoded()) != 128){ 149 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128KW encryption"); 150 } 151 algFamily = AlgFamily.AESKW; 152 153 } else if (alg.equals(JWEAlgorithm.A192KW)) { 154 155 if(ByteUtils.bitLength(getKey().getEncoded()) != 192){ 156 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192KW encryption"); 157 } 158 algFamily = AlgFamily.AESKW; 159 160 } else if (alg.equals(JWEAlgorithm.A256KW)) { 161 162 if (ByteUtils.bitLength(getKey().getEncoded()) != 256) { 163 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256KW encryption"); 164 } 165 algFamily = AlgFamily.AESKW; 166 167 } else if (alg.equals(JWEAlgorithm.A128GCMKW)) { 168 169 if(ByteUtils.bitLength(getKey().getEncoded()) != 128){ 170 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128GCMKW encryption"); 171 } 172 algFamily = AlgFamily.AESGCMKW; 173 174 } else if (alg.equals(JWEAlgorithm.A192GCMKW)) { 175 176 if(ByteUtils.bitLength(getKey().getEncoded()) != 192){ 177 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192GCMKW encryption"); 178 } 179 algFamily = AlgFamily.AESGCMKW; 180 181 } else if (alg.equals(JWEAlgorithm.A256GCMKW)) { 182 183 if(ByteUtils.bitLength(getKey().getEncoded()) != 256){ 184 throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256GCMKW encryption"); 185 } 186 algFamily = AlgFamily.AESGCMKW; 187 188 } else { 189 190 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS)); 191 } 192 193 194 final JWEHeader updatedHeader; // We need to work on the header 195 final Base64URL encryptedKey; // The second JWE part 196 197 // Generate and encrypt the CEK according to the enc method 198 final EncryptionMethod enc = header.getEncryptionMethod(); 199 final SecretKey cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom()); 200 201 if(AlgFamily.AESKW.equals(algFamily)) { 202 203 encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, getKey(), getJCAContext().getKeyEncryptionProvider())); 204 updatedHeader = header; // simply copy ref 205 206 } else if(AlgFamily.AESGCMKW.equals(algFamily)) { 207 208 final Container<byte[]> keyIV = new Container<>(AESGCM.generateIV(getJCAContext().getSecureRandom())); 209 final AuthenticatedCipherText authCiphCEK = AESGCMKW.encryptCEK(cek, keyIV, getKey(), getJCAContext().getKeyEncryptionProvider()); 210 encryptedKey = Base64URL.encode(authCiphCEK.getCipherText()); 211 212 // Add iv and tag to the header 213 updatedHeader = new JWEHeader.Builder(header). 214 iv(Base64URL.encode(keyIV.get())). 215 authTag(Base64URL.encode(authCiphCEK.getAuthenticationTag())). 216 build(); 217 } else { 218 // This should never happen 219 throw new JOSEException("Unexpected JWE algorithm: " + alg); 220 } 221 222 return ContentCryptoProvider.encrypt(updatedHeader, clearText, cek, encryptedKey, getJCAContext()); 223 } 224}