001package com.nimbusds.jose.crypto;
002
003
004import java.nio.charset.Charset;
005
006import javax.crypto.SecretKey;
007
008import net.jcip.annotations.ThreadSafe;
009
010import com.nimbusds.jose.*;
011import com.nimbusds.jose.util.Base64URL;
012
013
014/**
015 * Password-based encrypter of {@link com.nimbusds.jose.JWEObject JWE objects}.
016 * This class is thread-safe.
017 *
018 * <p>Supports the following key management algorithms:
019 *
020 * <ul>
021 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS256_A128KW}
022 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS384_A192KW}
023 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS512_A256KW}
024 * </ul>
025 *
026 * <p>Supports the following content encryption algorithms:
027 *
028 * <ul>
029 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
030 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
031 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
032 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
033 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
034 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
035 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
036 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
037 * </ul>
038 *
039 * @author Vladimir Dzhuvinov
040 * @version 2015-06-08
041 */
042@ThreadSafe
043public class PasswordBasedEncrypter extends PasswordBasedCryptoProvider implements JWEEncrypter {
044
045
046        /**
047         * The minimum salt length (8 bytes).
048         */
049        public static final int MIN_SALT_LENGTH = 8;
050
051
052        /**
053         * The cryptographic salt length, in bytes.
054         */
055        private final int saltLength;
056
057
058        /**
059         * The minimum recommended iteration count (1000).
060         */
061        public static final int MIN_RECOMMENDED_ITERATION_COUNT = 1000;
062
063
064        /**
065         * The iteration count.
066         */
067        private final int iterationCount;
068
069
070        /**
071         * Creates a new password-based encrypter.
072         *
073         * @param password       The password bytes. Must not be empty or
074         *                       {@code null}.
075         * @param saltLength     The length of the generated cryptographic
076         *                       salts, in bytes. Must be at least 8 bytes.
077         * @param iterationCount The pseudo-random function (PRF) iteration
078         *                       count. Must be at least 1000.
079         */
080        public PasswordBasedEncrypter(final byte[] password,
081                                      final int saltLength,
082                                      final int iterationCount) {
083
084                super(password);
085
086                if (saltLength < MIN_SALT_LENGTH) {
087                        throw new IllegalArgumentException("The minimum salt length (p2s) is " + MIN_SALT_LENGTH + " bytes");
088                }
089
090                this.saltLength = saltLength;
091
092                if (iterationCount < MIN_RECOMMENDED_ITERATION_COUNT) {
093                        throw new IllegalArgumentException("The minimum recommended iteration count (p2c) is " + MIN_RECOMMENDED_ITERATION_COUNT);
094                }
095
096                this.iterationCount = iterationCount;
097        }
098
099
100        /**
101         * Creates a new password-based encrypter.
102         *
103         * @param password       The password, as a UTF-8 encoded string. Must
104         *                       not be empty or {@code null}.
105         * @param saltLength     The length of the generated cryptographic
106         *                       salts, in bytes. Must be at least 8 bytes.
107         * @param iterationCount The pseudo-random function (PRF) iteration
108         *                       count. Must be at least 1000.
109         */
110        public PasswordBasedEncrypter(final String password,
111                                      final int saltLength,
112                                      final int iterationCount) {
113
114                this(password.getBytes(Charset.forName("UTF-8")), saltLength, iterationCount);
115        }
116
117
118        @Override
119        public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
120                throws JOSEException {
121
122                final JWEAlgorithm alg = header.getAlgorithm();
123                final EncryptionMethod enc = header.getEncryptionMethod();
124
125                final byte[] salt = new byte[saltLength];
126                getJCAContext().getSecureRandom().nextBytes(salt);
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                // We need to work on the header
132                final JWEHeader updatedHeader = new JWEHeader.Builder(header).
133                        pbes2Salt(Base64URL.encode(salt)).
134                        pbes2Count(iterationCount).
135                        build();
136
137                final SecretKey cek = ContentCryptoProvider.generateCEK(enc, getJCAContext().getSecureRandom());
138
139                // The second JWE part
140                final Base64URL encryptedKey = Base64URL.encode(AESKW.wrapCEK(cek, psKey, getJCAContext().getKeyEncryptionProvider()));
141
142                return  ContentCryptoProvider.encrypt(updatedHeader, clearText, cek, encryptedKey, getJCAContext());
143        }
144
145
146        /**
147         * Returns the length of the generated cryptographic salts.
148         *
149         * @return The length of the generated cryptographic salts, in bytes.
150         */
151        public int getSaltLength() {
152
153                return saltLength;
154        }
155
156
157        /**
158         * Returns the pseudo-random function (PRF) iteration count.
159         *
160         * @return The iteration count.
161         */
162        public int getIterationCount() {
163
164                return iterationCount;
165        }
166}