001package com.nimbusds.jose.crypto;
002
003
004import java.util.HashSet;
005import java.util.Set;
006
007import javax.crypto.SecretKey;
008import javax.crypto.spec.SecretKeySpec;
009
010import net.jcip.annotations.ThreadSafe;
011
012import com.nimbusds.jose.EncryptionMethod;
013import com.nimbusds.jose.JOSEException;
014import com.nimbusds.jose.JWEAlgorithm;
015import com.nimbusds.jose.JWEDecrypter;
016import com.nimbusds.jose.JWEHeader;
017import com.nimbusds.jose.util.Base64URL;
018import com.nimbusds.jose.util.StringUtils;
019
020
021/**
022 * AES decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. This class
023 * is thread-safe.
024 *
025 * <p>Supports the following JWE algorithms:
026 *
027 * <ul>
028 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A128KW}
029 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A192KW}
030 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A256KW}
031 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A128GCMKW}
032 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A192GCMKW}
033 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#A256GCMKW}
034 * </ul>
035 *
036 * <p>Supports the following encryption methods:
037 *
038 * <ul>
039 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
040 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
041 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
042 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
043 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
044 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
045 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
046 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
047 * </ul>
048 *
049 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames
050 * registered JWE header parameters}. Use {@link #setAcceptedAlgorithms} and
051 * {@link #setAcceptedEncryptionMethods} to restrict the acceptable JWE
052 * algorithms and encryption methods.
053 *
054 * @author Melisa Halsband 
055 * @version $version$ (2014-08-20)
056 */
057@ThreadSafe
058public class AESDecrypter extends AESCryptoProvider implements JWEDecrypter {
059
060
061        /**
062         * The accepted JWE algorithms.
063         */
064        private Set<JWEAlgorithm> acceptedAlgs;
065
066
067        /**
068         * The accepted encryption methods.
069         */
070        private Set<EncryptionMethod> acceptedEncs =
071                new HashSet<>(supportedEncryptionMethods());
072
073
074        /**
075         * The critical header parameter checker.
076         */
077        private final CriticalHeaderParameterChecker critParamChecker =
078                new CriticalHeaderParameterChecker();
079
080
081        /**
082         * The key encrypting key.
083         */
084        private final SecretKey kek;
085
086
087        /**
088         * Creates a new AES decrypter.
089         *
090         * @param kek The Key Encrypting Key. Must be 128 bits (16 bytes), 192
091         *            bits (24 bytes) or 256 bits (32 bytes). Must not be
092         *            {@code null}.
093         *
094         * @throws IllegalArgumentException If called with a null parameter or
095         *                                  unsupported key length
096         */
097        public AESDecrypter(final SecretKey kek) {
098
099                if (kek == null) {
100
101                        throw new IllegalArgumentException("The Key Encrypting Key must not be null");
102                }
103
104                this.kek = kek;
105
106                acceptedAlgs = compatibleAlgorithms();
107
108                if (acceptedAlgs == null){
109                        throw new IllegalArgumentException("The Key Encrypting Key must be 128, 192 or 256 bits long");
110                }
111        }
112
113
114        /**
115         * Creates a new AES decrypter.
116         *
117         * @param keyBytes The Key Encrypting Key, as a byte array. Must be 128
118         *                 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32
119         *                 bytes). Must not be {@code null}.
120         *
121         * @throws IllegalArgumentException If called with a null parameter or
122         *                                  unsupported key length
123         */
124        public AESDecrypter(final byte[] keyBytes)
125                throws IllegalArgumentException {
126
127                this(new SecretKeySpec(keyBytes, "AES"));
128        }
129
130
131        /**
132         * Returns the JWK algorithms compatible with the key size.
133         *
134         * @return The set of compatible algorithms.
135         */
136        public Set<JWEAlgorithm> compatibleAlgorithms() {
137
138                return COMPATIBLE_ALGORITHMS.get(kek.getEncoded().length);
139        }
140
141
142        /**
143         * Gets the Key Encrypting Key.
144         *
145         * @return The Key Encrypting Key.
146         */
147        public SecretKey getKey() {
148
149                return kek;
150        }
151
152
153        @Override
154        public Set<JWEAlgorithm> getAcceptedAlgorithms() {
155
156                return acceptedAlgs;
157        }
158
159
160        @Override
161        public void setAcceptedAlgorithms(final Set<JWEAlgorithm> acceptedAlgs) {
162
163                if (acceptedAlgs == null) {
164                        throw new IllegalArgumentException("The accepted JWE algorithms must not be null");
165                }
166
167                if (!supportedAlgorithms().containsAll(acceptedAlgs)) {
168                        throw new IllegalArgumentException("Unsupported JWE algorithm(s)");
169                }
170
171                if (!compatibleAlgorithms().containsAll(acceptedAlgs)) {
172                        throw new IllegalArgumentException("JWE algorithm(s) not compatible with key size");
173                }
174
175                this.acceptedAlgs = acceptedAlgs;
176        }
177
178
179        @Override
180        public Set<EncryptionMethod> getAcceptedEncryptionMethods() {
181
182                return acceptedEncs;
183        }
184
185
186        @Override
187        public void setAcceptedEncryptionMethods(final Set<EncryptionMethod> acceptedEncs) {
188
189                if (acceptedEncs == null)
190                        throw new IllegalArgumentException("The accepted encryption methods must not be null");
191
192                if (!supportedEncryptionMethods().containsAll(acceptedEncs)) {
193                        throw new IllegalArgumentException("Unsupported encryption method(s)");
194                }
195
196                this.acceptedEncs = acceptedEncs;
197        }
198
199
200        @Override
201        public Set<String> getIgnoredCriticalHeaderParameters() {
202
203                return critParamChecker.getIgnoredCriticalHeaders();
204        }
205
206
207        @Override
208        public void setIgnoredCriticalHeaderParameters(final Set<String> headers) {
209
210                critParamChecker.setIgnoredCriticalHeaders(headers);
211        }
212
213
214        @Override
215        public byte[] decrypt(final JWEHeader header,
216                              final Base64URL encryptedKey,
217                              final Base64URL iv,
218                              final Base64URL cipherText,
219                              final Base64URL authTag)
220                throws JOSEException {
221
222                // Validate required JWE parts
223                if (encryptedKey == null) {
224
225                        throw new JOSEException("The encrypted key must not be null");
226                }
227
228                if (iv == null) {
229
230                        throw new JOSEException("The initialization vector (IV) must not be null");
231                }
232
233                if (authTag == null) {
234
235                        throw new JOSEException("The authentication tag must not be null");
236                }
237
238                if (!critParamChecker.headerPasses(header)) {
239
240                        throw new JOSEException("Unsupported critical header parameter");
241                }
242
243
244                // Derive the content encryption key
245                JWEAlgorithm alg = header.getAlgorithm();
246                int keyLength = header.getEncryptionMethod().cekBitLength();
247
248                SecretKey cek;
249
250                if (alg.equals(JWEAlgorithm.A128KW) ||
251                    alg.equals(JWEAlgorithm.A192KW) ||
252                    alg.equals(JWEAlgorithm.A256KW))   {
253
254                        cek = AESKW.decryptCEK(kek, encryptedKey.decode());
255
256                } else if (alg.equals(JWEAlgorithm.A128GCMKW) ||
257                           alg.equals(JWEAlgorithm.A192GCMKW) ||
258                           alg.equals(JWEAlgorithm.A256GCMKW)) {
259
260                        byte[] keyIV = header.getIV().decode();
261                        byte[] keyTag = header.getAuthTag().decode();
262                        AuthenticatedCipherText authEncrCEK = new AuthenticatedCipherText(encryptedKey.decode(), keyTag);
263                        cek = AESGCMKW.decryptCEK(kek, keyIV, authEncrCEK, keyLength, keyEncryptionProvider);
264
265                } else {
266
267                        throw new JOSEException("Unsupported JWE algorithm, must be A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW orA256GCMKW");
268                }
269
270                // Compose the AAD
271                byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString());
272
273                // Decrypt the cipher text according to the JWE enc
274                EncryptionMethod enc = header.getEncryptionMethod();
275
276                byte[] plainText;
277
278                if (enc.equals(EncryptionMethod.A128CBC_HS256) ||
279                        enc.equals(EncryptionMethod.A192CBC_HS384) ||
280                        enc.equals(EncryptionMethod.A256CBC_HS512)) {
281
282                        plainText = AESCBC.decryptAuthenticated(
283                                cek,
284                                iv.decode(),
285                                cipherText.decode(),
286                                aad,
287                                authTag.decode(),
288                                contentEncryptionProvider,
289                                macProvider);
290
291                } else if (enc.equals(EncryptionMethod.A128GCM) ||
292                        enc.equals(EncryptionMethod.A192GCM) ||
293                        enc.equals(EncryptionMethod.A256GCM)) {
294
295                        plainText = AESGCM.decrypt(
296                                cek,
297                                iv.decode(),
298                                cipherText.decode(),
299                                aad,
300                                authTag.decode(),
301                                contentEncryptionProvider);
302
303                } else if (enc.equals(EncryptionMethod.A128CBC_HS256_DEPRECATED) ||
304                        enc.equals(EncryptionMethod.A256CBC_HS512_DEPRECATED)) {
305
306                        plainText = AESCBC.decryptWithConcatKDF(
307                                header,
308                                cek,
309                                encryptedKey,
310                                iv,
311                                cipherText,
312                                authTag,
313                                contentEncryptionProvider,
314                                macProvider);
315
316                } else {
317
318                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A256GCM");
319                }
320
321
322                // Apply decompression if requested
323                return DeflateHelper.applyDecompression(header, plainText);
324        }
325}