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