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