001package com.nimbusds.jose.crypto;
002
003
004import javax.crypto.SecretKey;
005
006import com.nimbusds.jose.DefaultJWEHeaderFilter;
007import com.nimbusds.jose.EncryptionMethod;
008import com.nimbusds.jose.JOSEException;
009import com.nimbusds.jose.JWEAlgorithm;
010import com.nimbusds.jose.JWEDecrypter;
011import com.nimbusds.jose.JWEHeaderFilter;
012import com.nimbusds.jose.ReadOnlyJWEHeader;
013import com.nimbusds.jose.util.Base64URL;
014import com.nimbusds.jose.util.StringUtils;
015
016
017/**
018 * Direct decrypter of {@link com.nimbusds.jose.JWEObject JWE objects} with a
019 * shared symmetric key. This class is thread-safe.
020 *
021 * <p>Supports the following JWE algorithms:
022 *
023 * <ul>
024 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#DIR}
025 * </ul>
026 *
027 * <p>Supports the following encryption methods:
028 *
029 * <ul>
030 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
031 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
032 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
033 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
034 * </ul>
035 *
036 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames
037 * registered JWE header parameters}. Modify the {@link #getJWEHeaderFilter
038 * header filter} properties to restrict the acceptable JWE algorithms, 
039 * encryption methods and header parameters, or to allow custom JWE header 
040 * parameters.
041 * 
042 * @author Vladimir Dzhuvinov
043 * @version $version$ (2013-10-07)
044 *
045 */
046public class DirectDecrypter extends DirectCryptoProvider implements JWEDecrypter {
047
048
049        /**
050         * The JWE header filter.
051         */
052        private final DefaultJWEHeaderFilter headerFilter;
053
054
055        /**
056         * Creates a new direct decrypter.
057         *
058         * @param key The shared symmetric key. Its algorithm must be "AES".
059         *            Must be 128 bits (16 bytes), 256 bits (32 bytes) or 512 
060         *            bits (64 bytes) long. Must not be {@code null}.
061         *
062         * @throws JOSEException If the key length is unexpected.
063         */
064        public DirectDecrypter(final SecretKey key)
065                throws JOSEException {
066
067                super(key);
068
069                headerFilter = new DefaultJWEHeaderFilter(supportedAlgorithms(), supportedEncryptionMethods());
070        }
071
072
073        /**
074         * Creates a new direct decrypter.
075         *
076         * @param keyBytes The shared symmetric key, as a byte array. Must be 
077         *                 128 bits (16 bytes), 256 bits (32 bytes) or 512 bits
078         *                 (64 bytes) long. Must not be {@code null}.
079         *
080         * @throws JOSEException If the key length is unexpected.
081         */
082        public DirectDecrypter(final byte[] keyBytes)
083                throws JOSEException {
084
085                super(keyBytes);
086
087                headerFilter = new DefaultJWEHeaderFilter(supportedAlgorithms(), supportedEncryptionMethods());
088        }
089
090
091        @Override
092        public JWEHeaderFilter getJWEHeaderFilter() {
093
094                return headerFilter;
095        }
096
097
098        @Override
099        public byte[] decrypt(final ReadOnlyJWEHeader readOnlyJWEHeader,
100                              final Base64URL encryptedKey,
101                              final Base64URL iv,
102                              final Base64URL cipherText,
103                              final Base64URL authTag) 
104                throws JOSEException {
105
106                // Validate required JWE parts
107                if (encryptedKey != null) {
108
109                        throw new JOSEException("Unexpected encrypted key, must be omitted");
110                }       
111
112                if (iv == null) {
113
114                        throw new JOSEException("The initialization vector (IV) must not be null");
115                }
116
117                if (authTag == null) {
118
119                        throw new JOSEException("The authentication tag must not be null");
120                }
121                
122
123                JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm();
124
125                if (! alg.equals(JWEAlgorithm.DIR)) {
126
127                        throw new JOSEException("Unsupported algorithm, must be \"dir\"");
128                }
129
130                // Compose the AAD
131                byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString());
132
133                // Decrypt the cipher text according to the JWE enc
134                EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod();
135
136                byte[] plainText;
137
138                if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A256CBC_HS512)) {
139
140                        plainText = AESCBC.decryptAuthenticated(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode());
141
142                } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A256GCM)) {
143
144                        plainText = AESGCM.decrypt(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode());
145
146                } else {
147
148                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A256CBC_HS512, A128GCM or A128GCM");
149                }
150
151
152                // Apply decompression if requested
153                return DeflateHelper.applyDecompression(readOnlyJWEHeader, plainText);
154        }
155}
156