001package com.nimbusds.jose.crypto;
002
003
004import java.util.HashSet;
005import java.util.Set;
006import javax.crypto.SecretKey;
007
008import com.nimbusds.jose.EncryptionMethod;
009import com.nimbusds.jose.JOSEException;
010import com.nimbusds.jose.JWEAlgorithm;
011import com.nimbusds.jose.JWEDecrypter;
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}. Use {@link #setAcceptedAlgorithms} and
040 * {@link #setAcceptedEncryptionMethods} to restrict the acceptable JWE
041 * algorithms and encryption methods.
042 * 
043 * @author Vladimir Dzhuvinov
044 * @version $version$ (2014-04-22)
045 */
046public class DirectDecrypter extends DirectCryptoProvider implements JWEDecrypter {
047
048
049        /**
050         * The accepted JWE algorithms.
051         */
052        private Set<JWEAlgorithm> acceptedAlgs =
053                new HashSet<JWEAlgorithm>(supportedAlgorithms());
054
055
056        /**
057         * The accepted encryption methods.
058         */
059        private Set<EncryptionMethod> acceptedEncs =
060                new HashSet<EncryptionMethod>(supportedEncryptionMethods());
061
062
063        /**
064         * The critical header parameter checker.
065         */
066        private final CriticalHeaderParameterChecker critParamChecker =
067                new CriticalHeaderParameterChecker();
068
069
070        /**
071         * Creates a new direct decrypter.
072         *
073         * @param key The shared symmetric key. Its algorithm must be "AES".
074         *            Must be 128 bits (16 bytes), 192 bits (24 bytes), 256
075         *            bits (32 bytes), 384 bits (48 bytes) or 512 bits
076         *            (64 bytes) long. Must not be {@code null}.
077         *
078         * @throws JOSEException If the key length is unexpected.
079         */
080        public DirectDecrypter(final SecretKey key)
081                throws JOSEException {
082
083                super(key);
084        }
085
086
087        /**
088         * Creates a new direct decrypter.
089         *
090         * @param keyBytes The shared symmetric key, as a byte array. Must be 
091         *                 128 bits (16 bytes), 192 bits (24 bytes), 256 bits
092         *                 (32 bytes), 384 bits (48 bytes) or 512 bits (64
093         *                 bytes) long. Must not be {@code null}.
094         *
095         * @throws JOSEException If the key length is unexpected.
096         */
097        public DirectDecrypter(final byte[] keyBytes)
098                throws JOSEException {
099
100                super(keyBytes);
101        }
102
103
104        @Override
105        public Set<JWEAlgorithm> getAcceptedAlgorithms() {
106
107                return acceptedAlgs;
108        }
109
110
111        @Override
112        public void setAcceptedAlgorithms(final Set<JWEAlgorithm> acceptedAlgs) {
113
114                if (acceptedAlgs == null) {
115                        throw new IllegalArgumentException("The accepted JWE algorithms must not be null");
116                }
117
118                if (! supportedAlgorithms().containsAll(acceptedAlgs)) {
119                        throw new IllegalArgumentException("Unsupported JWE algorithm(s)");
120                }
121
122                this.acceptedAlgs = acceptedAlgs;
123        }
124
125
126        @Override
127        public Set<EncryptionMethod> getAcceptedEncryptionMethods() {
128
129                return acceptedEncs;
130        }
131
132
133        @Override
134        public void setAcceptedEncryptionMethods(final Set<EncryptionMethod> acceptedEncs) {
135
136                if (acceptedEncs == null)
137                        throw new IllegalArgumentException("The accepted encryption methods must not be null");
138
139                if (!supportedEncryptionMethods().containsAll(acceptedEncs)) {
140                        throw new IllegalArgumentException("Unsupported encryption method(s)");
141                }
142
143                this.acceptedEncs = acceptedEncs;
144        }
145
146
147        @Override
148        public Set<String> getIgnoredCriticalHeaderParameters() {
149
150                return critParamChecker.getIgnoredCriticalHeaders();
151        }
152
153
154        @Override
155        public void setIgnoredCriticalHeaderParameters(final Set<String> headers) {
156
157                critParamChecker.setIgnoredCriticalHeaders(headers);
158        }
159
160
161        @Override
162        public byte[] decrypt(final ReadOnlyJWEHeader header,
163                              final Base64URL encryptedKey,
164                              final Base64URL iv,
165                              final Base64URL cipherText,
166                              final Base64URL authTag) 
167                throws JOSEException {
168
169                // Validate required JWE parts
170                if (encryptedKey != null) {
171
172                        throw new JOSEException("Unexpected encrypted key, must be omitted");
173                }       
174
175                if (iv == null) {
176
177                        throw new JOSEException("The initialization vector (IV) must not be null");
178                }
179
180                if (authTag == null) {
181
182                        throw new JOSEException("The authentication tag must not be null");
183                }
184                
185
186                JWEAlgorithm alg = header.getAlgorithm();
187
188                if (! alg.equals(JWEAlgorithm.DIR)) {
189
190                        throw new JOSEException("Unsupported algorithm, must be \"dir\"");
191                }
192
193                if (! critParamChecker.headerPasses(header)) {
194
195                        throw new JOSEException("Unsupported critical header parameter");
196                }
197
198                // Compose the AAD
199                byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString());
200
201                // Decrypt the cipher text according to the JWE enc
202                EncryptionMethod enc = header.getEncryptionMethod();
203
204                byte[] plainText;
205
206                if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) {
207
208                        plainText = AESCBC.decryptAuthenticated(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode(), contentEncryptionProvider, macProvider);
209
210                } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) {
211
212                        plainText = AESGCM.decrypt(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode(), contentEncryptionProvider);
213
214                } else {
215
216                        throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A128GCM");
217                }
218
219
220                // Apply decompression if requested
221                return DeflateHelper.applyDecompression(header, plainText);
222        }
223}
224