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