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}