001package com.nimbusds.jose.crypto;
002
003
004import java.nio.charset.Charset;
005import java.util.Set;
006
007import javax.crypto.SecretKey;
008
009import net.jcip.annotations.ThreadSafe;
010
011import com.nimbusds.jose.*;
012import com.nimbusds.jose.util.Base64URL;
013
014
015/**
016 * Password-based decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}.
017 * This class is thread-safe.
018 *
019 * <p>Supports the following key management algorithms:
020 *
021 * <ul>
022 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS256_A128KW}
023 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS384_A192KW}
024 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS512_A256KW}
025 * </ul>
026 *
027 * <p>Supports the following content encryption algorithms:
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 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
037 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
038 * </ul>
039 *
040 * @author Vladimir Dzhuvinov
041 * @version 2015-06-07
042 */
043@ThreadSafe
044public class PasswordBasedDecrypter extends PasswordBasedCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
045
046
047        /**
048         * The critical header policy.
049         */
050        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
051
052
053        /**
054         * Creates a new password-based decrypter.
055         *
056         * @param password The password bytes. Must not be empty or
057         *                 {@code null}.
058         */
059        public PasswordBasedDecrypter(final byte[] password) {
060
061                super(password);
062        }
063
064
065        /**
066         * Creates a new password-based decrypter.
067         *
068         * @param password The password, as a UTF-8 encoded string. Must not be
069         *                 empty or {@code null}.
070         */
071        public PasswordBasedDecrypter(final String password) {
072
073                super(password.getBytes(Charset.forName("UTF-8")));
074        }
075
076
077        @Override
078        public Set<String> getProcessedCriticalHeaderParams() {
079
080                return critPolicy.getProcessedCriticalHeaderParams();
081        }
082
083
084        @Override
085        public Set<String> getDeferredCriticalHeaderParams() {
086
087                return critPolicy.getProcessedCriticalHeaderParams();
088        }
089
090
091        @Override
092        public byte[] decrypt(final JWEHeader header,
093                              final Base64URL encryptedKey,
094                              final Base64URL iv,
095                              final Base64URL cipherText,
096                              final Base64URL authTag)
097                throws JOSEException {
098
099                // Validate required JWE parts
100                if (encryptedKey == null) {
101                        throw new JOSEException("Missing JWE encrypted key");
102                }
103
104                if (iv == null) {
105                        throw new JOSEException("Missing JWE initialization vector (IV)");
106                }
107
108                if (authTag == null) {
109                        throw new JOSEException("Missing JWE authentication tag");
110                }
111
112                if (header.getPBES2Salt() == null) {
113                        throw new JOSEException("Missing JWE \"p2s\" header parameter");
114                }
115
116                final byte[] salt = header.getPBES2Salt().decode();
117
118                if (header.getPBES2Count() < 1) {
119                        throw new JOSEException("Missing JWE \"p2c\" header parameter");
120                }
121
122                final int iterationCount = header.getPBES2Count();
123
124                critPolicy.ensureHeaderPasses(header);
125
126                final JWEAlgorithm alg = header.getAlgorithm();
127                final byte[] formattedSalt = PBKDF2.formatSalt(alg, salt);
128                final PRFParams prfParams = PRFParams.resolve(alg, getJCAContext().getMACProvider());
129                final SecretKey psKey = PBKDF2.deriveKey(getPassword(), formattedSalt, iterationCount, prfParams);
130
131                final SecretKey cek = AESKW.unwrapCEK(psKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider());
132
133                return ContentCryptoProvider.decrypt(header, encryptedKey, iv, cipherText, authTag, cek, getJCAContext());
134        }
135}