001package com.nimbusds.jose.crypto;
002
003
004import java.util.Set;
005
006import javax.crypto.SecretKey;
007import javax.crypto.spec.SecretKeySpec;
008
009import net.jcip.annotations.ThreadSafe;
010
011import com.nimbusds.jose.*;
012import com.nimbusds.jose.jwk.OctetSequenceKey;
013import com.nimbusds.jose.util.Base64URL;
014
015
016/**
017 * AES and AES GCM key wrap decrypter of {@link com.nimbusds.jose.JWEObject JWE
018 * objects}. Expects an AES key.
019 *
020 * <p>Unwraps the encrypted Content Encryption Key (CEK) with the specified AES
021 * key, and then uses the CEK along with the IV and authentication tag to
022 * decrypt the cipher text. See RFC 7518, sections
023 * <a href="https://tools.ietf.org/html/rfc7518#section-4.4">4.4</a> and
024 * <a href="https://tools.ietf.org/html/rfc7518#section-4.7">4.7</a> for more
025 * information.
026 *
027 * <p>This class is thread-safe.
028 *
029 * <p>Supports the following key management algorithms:
030 *
031 * <ul>
032 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW}
033 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW}
034 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW}
035 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW}
036 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW}
037 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW}
038 * </ul>
039 *
040 * <p>Supports the following content encryption algorithms:
041 *
042 * <ul>
043 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
044 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
045 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
046 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
047 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
048 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
049 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
050 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
051 * </ul>
052 *
053 * @author Melisa Halsband
054 * @author Vladimir Dzhuvinov
055 * @version 2015-06-29
056 */
057@ThreadSafe
058public class AESDecrypter extends AESCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
059
060
061        /**
062         * The critical header policy.
063         */
064        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
065
066
067        /**
068         * Creates a new AES decrypter.
069         *
070         * @param kek The Key Encrypting Key. Must be 128 bits (16 bytes), 192
071         *            bits (24 bytes) or 256 bits (32 bytes). Must not be
072         *            {@code null}.
073         *
074         * @throws KeyLengthException If the KEK length is invalid.
075         */
076        public AESDecrypter(final SecretKey kek)
077                throws KeyLengthException {
078
079                this(kek, null);
080        }
081
082
083        /**
084         * Creates a new AES decrypter.
085         *
086         * @param keyBytes The Key Encrypting Key, as a byte array. Must be 128
087         *                 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32
088         *                 bytes). Must not be {@code null}.
089         *
090         * @throws KeyLengthException If the KEK length is invalid.
091         */
092        public AESDecrypter(final byte[] keyBytes)
093                throws KeyLengthException {
094
095                this(new SecretKeySpec(keyBytes, "AES"));
096        }
097
098
099        /**
100         * Creates a new AES decrypter.
101         *
102         * @param octJWK The Key Encryption Key, as a JWK. Must be 128 bits (16
103         *               bytes), 192 bits (24 bytes), 256 bits (32 bytes), 384
104         *               bits (48 bytes) or 512 bits (64 bytes) long. Must not
105         *               be {@code null}.
106         *
107         * @throws KeyLengthException If the KEK length is invalid.
108         */
109        public AESDecrypter(final OctetSequenceKey octJWK)
110                throws KeyLengthException {
111
112                this(octJWK.toSecretKey("AES"));
113        }
114
115
116        /**
117         * Creates a new AES decrypter.
118         *
119         * @param kek            The Key Encrypting Key. Must be 128 bits (16
120         *                       bytes), 192 bits (24 bytes) or 256 bits (32
121         *                       bytes). Must not be {@code null}.
122         * @param defCritHeaders The names of the critical header parameters
123         *                       that are deferred to the application for
124         *                       processing, empty set or {@code null} if none.
125         *
126         * @throws KeyLengthException If the KEK length is invalid.
127         */
128        public AESDecrypter(final SecretKey kek, final Set<String> defCritHeaders)
129                throws KeyLengthException {
130
131                super(kek);
132
133                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
134        }
135
136
137        @Override
138        public Set<String> getProcessedCriticalHeaderParams() {
139
140                return critPolicy.getProcessedCriticalHeaderParams();
141        }
142
143
144        @Override
145        public Set<String> getDeferredCriticalHeaderParams() {
146
147                return critPolicy.getProcessedCriticalHeaderParams();
148        }
149
150
151        @Override
152        public byte[] decrypt(final JWEHeader header,
153                              final Base64URL encryptedKey,
154                              final Base64URL iv,
155                              final Base64URL cipherText,
156                              final Base64URL authTag)
157                throws JOSEException {
158
159                // Validate required JWE parts
160                if (encryptedKey == null) {
161                        throw new JOSEException("Missing JWE encrypted key");
162                }
163
164                if (iv == null) {
165                        throw new JOSEException("Missing JWE initialization vector (IV)");
166                }
167
168                if (authTag == null) {
169                        throw new JOSEException("Missing JWE authentication tag");
170                }
171
172                critPolicy.ensureHeaderPasses(header);
173
174                // Derive the content encryption key
175                JWEAlgorithm alg = header.getAlgorithm();
176                int keyLength = header.getEncryptionMethod().cekBitLength();
177
178                final SecretKey cek;
179
180                if (alg.equals(JWEAlgorithm.A128KW) ||
181                    alg.equals(JWEAlgorithm.A192KW) ||
182                    alg.equals(JWEAlgorithm.A256KW))   {
183
184                        cek = AESKW.unwrapCEK(getKey(), encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
185
186                } else if (alg.equals(JWEAlgorithm.A128GCMKW) ||
187                           alg.equals(JWEAlgorithm.A192GCMKW) ||
188                           alg.equals(JWEAlgorithm.A256GCMKW)) {
189
190                        if (header.getIV() == null) {
191                                throw new JOSEException("Missing JWE \"iv\" header parameter");
192                        }
193
194                        byte[] keyIV = header.getIV().decode();
195
196                        if (header.getAuthTag() == null) {
197                                throw new JOSEException("Missing JWE \"tag\" header parameter");
198                        }
199
200                        byte[] keyTag = header.getAuthTag().decode();
201
202                        AuthenticatedCipherText authEncrCEK = new AuthenticatedCipherText(encryptedKey.decode(), keyTag);
203                        cek = AESGCMKW.decryptCEK(getKey(), keyIV, authEncrCEK, keyLength, getJCAContext().getKeyEncryptionProvider());
204
205                } else {
206
207                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWEAlgorithm(alg, SUPPORTED_ALGORITHMS));
208                }
209
210                return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
211        }
212}