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}