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