001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto; 019 020 021import com.nimbusds.jose.*; 022import com.nimbusds.jose.crypto.impl.*; 023import com.nimbusds.jose.crypto.opts.OptionUtils; 024import com.nimbusds.jose.util.Base64URL; 025import com.nimbusds.jose.util.StandardCharset; 026import net.jcip.annotations.ThreadSafe; 027 028import javax.crypto.SecretKey; 029import java.util.Collections; 030import java.util.Set; 031 032 033/** 034 * Password-based decrypter of {@link com.nimbusds.jose.JWEObject JWE objects}. 035 * Expects a password. 036 * 037 * <p>See RFC 7518 038 * <a href="https://tools.ietf.org/html/rfc7518#section-4.8">section 4.8</a> 039 * for more information. 040 * 041 * <p>This class is thread-safe. 042 * 043 * <p>Supports the following key management algorithms: 044 * 045 * <ul> 046 * <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS256_A128KW} 047 * <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS384_A192KW} 048 * <li>{@link com.nimbusds.jose.JWEAlgorithm#PBES2_HS512_A256KW} 049 * </ul> 050 * 051 * <p>Supports the following content encryption algorithms: 052 * 053 * <ul> 054 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 055 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 056 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 057 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 058 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 059 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 060 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED} 061 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED} 062 * <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P} 063 * </ul> 064 * 065 * @author Vladimir Dzhuvinov 066 * @author Egor Puzanov 067 * @version 2026-09-16 068 */ 069@ThreadSafe 070public class PasswordBasedDecrypter extends PasswordBasedCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware { 071 072 073 /** 074 * The maximum allowed iteration count (1 million). 075 */ 076 public static final int MAX_ALLOWED_ITERATION_COUNT = 1_000_000; 077 078 079 /** 080 * The critical header policy. 081 */ 082 private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral(); 083 084 085 /** 086 * The configured options, empty set if none. 087 */ 088 private final Set<JWEDecrypterOption> opts; 089 090 091 /** 092 * Creates a new password-based decrypter. 093 * 094 * @param password The password bytes. Must not be empty or 095 * {@code null}. 096 */ 097 public PasswordBasedDecrypter(final byte[] password) { 098 099 this(password, null, Collections.<JWEDecrypterOption>emptySet()); 100 } 101 102 103 /** 104 * Creates a new password-based decrypter. 105 * 106 * @param password The password, as a UTF-8 encoded string. Must not be 107 * empty or {@code null}. 108 */ 109 public PasswordBasedDecrypter(final String password) { 110 111 this(password.getBytes(StandardCharset.UTF_8), null, Collections.<JWEDecrypterOption>emptySet()); 112 } 113 114 115 /** 116 * Creates a new password-based decrypter. 117 * 118 * @param password The password, as a UTF-8 encoded string. Must not be 119 * empty or {@code null}. 120 * @param opts The decryption options, empty or {@code null} if 121 * none. 122 */ 123 public PasswordBasedDecrypter(final String password, final Set<JWEDecrypterOption> opts) { 124 125 this(password.getBytes(StandardCharset.UTF_8), null, opts); 126 } 127 128 129 /** 130 * Creates a new password-based decrypter. 131 * 132 * @param password The password bytes. Must not be empty or 133 * {@code null}. 134 * @param defCritHeaders The names of the critical header parameters 135 * that are deferred to the application for 136 * processing, empty set or {@code null} if none. 137 */ 138 public PasswordBasedDecrypter(final byte[] password, 139 final Set<String> defCritHeaders) { 140 141 this(password, defCritHeaders, Collections.<JWEDecrypterOption>emptySet()); 142 } 143 144 145 /** 146 * Creates a new password-based decrypter. 147 * 148 * @param password The password bytes. Must not be empty or 149 * {@code null}. 150 * @param defCritHeaders The names of the critical header parameters 151 * that are deferred to the application for 152 * processing, empty set or {@code null} if none. 153 * @param opts The decryption options, empty or {@code null} 154 * if none. 155 */ 156 public PasswordBasedDecrypter(final byte[] password, 157 final Set<String> defCritHeaders, 158 final Set<JWEDecrypterOption> opts) { 159 160 super(password); 161 critPolicy.setDeferredCriticalHeaderParams(defCritHeaders); 162 this.opts = opts != null ? opts : Collections.<JWEDecrypterOption>emptySet(); 163 } 164 165 166 /** 167 * Creates a new password-based decrypter. 168 * 169 * @param password The password, as a UTF-8 encoded string. Must 170 * not be empty or {@code null}. 171 * @param defCritHeaders The names of the critical header parameters 172 * that are deferred to the application for 173 * processing, empty set or {@code null} if none. 174 * @param opts The decryption options, empty or {@code null} 175 * if none. 176 */ 177 public PasswordBasedDecrypter(final String password, 178 final Set<String> defCritHeaders, 179 final Set<JWEDecrypterOption> opts) { 180 181 this(password.getBytes(StandardCharset.UTF_8), defCritHeaders, opts); 182 } 183 184 185 @Override 186 public Set<String> getProcessedCriticalHeaderParams() { 187 188 return critPolicy.getProcessedCriticalHeaderParams(); 189 } 190 191 192 @Override 193 public Set<String> getDeferredCriticalHeaderParams() { 194 195 return critPolicy.getDeferredCriticalHeaderParams(); 196 } 197 198 199 /** 200 * Decrypts the specified cipher text of a {@link JWEObject JWE Object}. 201 * 202 * @param header The JSON Web Encryption (JWE) header. Must 203 * specify a supported JWE algorithm and method. 204 * Must not be {@code null}. 205 * @param encryptedKey The encrypted key, {@code null} if not required 206 * by the JWE algorithm. 207 * @param iv The initialisation vector, {@code null} if not 208 * required by the JWE algorithm. 209 * @param cipherText The cipher text to decrypt. Must not be 210 * {@code null}. 211 * @param authTag The authentication tag, {@code null} if not 212 * required. 213 * 214 * @return The clear text. 215 * 216 * @throws JOSEException If the JWE algorithm or method is not 217 * supported, if a critical header parameter is 218 * not supported or marked for deferral to the 219 * application, or if decryption failed for some 220 * other reason. 221 */ 222 @Deprecated 223 public byte[] decrypt(final JWEHeader header, 224 final Base64URL encryptedKey, 225 final Base64URL iv, 226 final Base64URL cipherText, 227 final Base64URL authTag) 228 throws JOSEException { 229 230 return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header)); 231 } 232 233 234 @Override 235 public byte[] decrypt(final JWEHeader header, 236 final Base64URL encryptedKey, 237 final Base64URL iv, 238 final Base64URL cipherText, 239 final Base64URL authTag, 240 final byte[] aad) 241 throws JOSEException { 242 243 // Validate required JWE parts 244 if (encryptedKey == null) { 245 throw new JOSEException("Missing JWE encrypted key"); 246 } 247 248 if (iv == null) { 249 throw new JOSEException("Missing JWE initialization vector (IV)"); 250 } 251 252 if (authTag == null) { 253 throw new JOSEException("Missing JWE authentication tag"); 254 } 255 256 if (header.getPBES2Salt() == null) { 257 throw new JOSEException("Missing JWE p2s header parameter"); 258 } 259 260 final byte[] salt = header.getPBES2Salt().decode(); 261 262 if (header.getPBES2Count() < 1) { 263 throw new JOSEException("Missing JWE p2c header parameter"); 264 } 265 266 final int iterationCount = header.getPBES2Count(); 267 268 if (iterationCount > MAX_ALLOWED_ITERATION_COUNT) { 269 throw new JOSEException("The JWE p2c header exceeds the maximum allowed " + MAX_ALLOWED_ITERATION_COUNT + " count"); 270 } 271 272 critPolicy.ensureHeaderPasses(header); 273 274 final JWEAlgorithm alg = JWEHeaderValidation.getAlgorithmAndEnsureNotNull(header); 275 final byte[] formattedSalt = PBKDF2.formatSalt(alg, salt); 276 final PRFParams prfParams = PRFParams.resolve(alg, getJCAContext().getMACProvider()); 277 final SecretKey psKey = PBKDF2.deriveKey(getPassword(), formattedSalt, iterationCount, prfParams, getJCAContext().getProvider()); 278 279 final SecretKey cek = AESKW.unwrapCEK(psKey, encryptedKey.decode(), getJCAContext().getKeyEncryptionProvider()); 280 281 int maxDecompressedLength = OptionUtils.resolveMaxDecompressedPlainTextLength(opts); 282 283 return ContentCryptoProvider.decrypt(header, aad, encryptedKey, iv, cipherText, authTag, cek, getJCAContext(), maxDecompressedLength); 284 } 285}