001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 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.impl; 019 020 021import com.nimbusds.jose.JOSEException; 022import com.nimbusds.jose.JWEAlgorithm; 023import com.nimbusds.jose.util.ByteUtils; 024import com.nimbusds.jose.util.IntegerUtils; 025import com.nimbusds.jose.util.StandardCharset; 026 027import javax.crypto.Mac; 028import javax.crypto.SecretKey; 029import javax.crypto.SecretKeyFactory; 030import javax.crypto.spec.PBEKeySpec; 031import javax.crypto.spec.SecretKeySpec; 032import java.io.ByteArrayOutputStream; 033import java.io.IOException; 034import java.nio.charset.StandardCharsets; 035import java.security.NoSuchAlgorithmException; 036import java.security.Provider; 037import java.security.spec.InvalidKeySpecException; 038 039 040/** 041 * Password-Based Key Derivation Function 2 (PBKDF2) utilities. 042 * 043 * @author Brian Campbell 044 * @author Pere Bueno Yerbes 045 * @author Vladimir Dzhuvinov 046 * @version 2024-09-10 047 */ 048public class PBKDF2 { 049 050 051 /** 052 * The minimum salt length (8 bytes). 053 */ 054 public static final int MIN_SALT_LENGTH = 8; 055 056 057 /** 058 * Zero byte array of length one. 059 */ 060 static final byte[] ZERO_BYTE = { 0 };// value of (long) Math.pow(2, 32) - 1; 061 062 063 /** 064 * Value of {@code (long) Math.pow(2, 32) - 1;} 065 */ 066 static final long MAX_DERIVED_KEY_LENGTH = 4294967295L; 067 068 069 /** 070 * Formats the specified cryptographic salt for use in PBKDF2. 071 * 072 * <pre> 073 * UTF8(JWE-alg) || 0x00 || Salt Input 074 * </pre> 075 * 076 * @param alg The JWE algorithm. Must not be {@code null}. 077 * @param salt The cryptographic salt. Must be at least 8 bytes long. 078 * 079 * @return The formatted salt for use in PBKDF2. 080 * 081 * @throws JOSEException If formatting failed. 082 */ 083 public static byte[] formatSalt(final JWEAlgorithm alg, final byte[] salt) 084 throws JOSEException { 085 086 byte[] algBytes = alg.toString().getBytes(StandardCharset.UTF_8); 087 088 if (salt == null) { 089 throw new JOSEException("The salt must not be null"); 090 } 091 092 if (salt.length < MIN_SALT_LENGTH) { 093 throw new JOSEException("The salt must be at least " + MIN_SALT_LENGTH + " bytes long"); 094 } 095 096 ByteArrayOutputStream out = new ByteArrayOutputStream(); 097 try { 098 out.write(algBytes); 099 out.write(ZERO_BYTE); 100 out.write(salt); 101 } catch (IOException e) { 102 throw new JOSEException(e.getMessage(), e); 103 } 104 105 return out.toByteArray(); 106 } 107 108 109 /** 110 * Derives a PBKDF2 key from the specified password and parameters. 111 * 112 * @param password The password. Must not be {@code null}. 113 * @param formattedSalt The formatted cryptographic salt. Must not be 114 * {@code null}. 115 * @param iterationCount The iteration count. Must be a positive 116 * integer. 117 * @param prfParams The Pseudo-Random Function (PRF) parameters. 118 * Must not be {@code null}. 119 * @param jcaProvider The JCA provider, {@code null} if not 120 * specified. 121 * 122 * @return The derived secret key (with "AES" algorithm). 123 * 124 * @throws JOSEException If the key derivation failed. 125 */ 126 public static SecretKey deriveKey(final byte[] password, 127 final byte[] formattedSalt, 128 final int iterationCount, 129 final PRFParams prfParams, 130 final Provider jcaProvider) 131 throws JOSEException { 132 133 if (formattedSalt == null) { 134 throw new JOSEException("The formatted salt must not be null"); 135 } 136 137 if (iterationCount < 1) { 138 throw new JOSEException("The iteration count must be greater than 0"); 139 } 140 int keyLengthInBits = ByteUtils.bitLength(prfParams.getDerivedKeyByteLength()); 141 PBEKeySpec spec = new PBEKeySpec(new String(password, StandardCharsets.UTF_8).toCharArray(), formattedSalt, iterationCount, keyLengthInBits); 142 try { 143 final SecretKeyFactory skf; 144 if (jcaProvider != null) { 145 skf = SecretKeyFactory.getInstance("PBKDF2With" + prfParams.getMACAlgorithm(), jcaProvider); 146 } else { 147 skf = SecretKeyFactory.getInstance("PBKDF2With" + prfParams.getMACAlgorithm()); 148 } 149 return new SecretKeySpec(skf.generateSecret(spec).getEncoded(), "AES"); 150 } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { 151 throw new JOSEException(ex.getLocalizedMessage(), ex); 152 } 153 } 154 155 156 /** 157 * Block extraction iteration. 158 * 159 * @param formattedSalt The formatted salt. Must not be {@code null}. 160 * @param iterationCount The iteration count. Must be a positive 161 * integer. 162 * @param blockIndex The block index. 163 * @param prf The pseudo-random function (HMAC). Must not be 164 * {@code null}. 165 * 166 * @return The block. 167 * 168 * @throws JOSEException If the block extraction failed. 169 */ 170 static byte[] extractBlock(final byte[] formattedSalt, final int iterationCount, final int blockIndex, final Mac prf) 171 throws JOSEException { 172 173 if (formattedSalt == null) { 174 throw new JOSEException("The formatted salt must not be null"); 175 } 176 177 if (iterationCount < 1) { 178 throw new JOSEException("The iteration count must be greater than 0"); 179 } 180 181 byte[] currentU; 182 byte[] lastU = null; 183 byte[] xorU = null; 184 185 for (int i = 1; i <= iterationCount; i++) 186 { 187 byte[] inputBytes; 188 if (i == 1) 189 { 190 inputBytes = ByteUtils.concat(formattedSalt, IntegerUtils.toBytes(blockIndex)); 191 currentU = prf.doFinal(inputBytes); 192 xorU = currentU; 193 } 194 else 195 { 196 currentU = prf.doFinal(lastU); 197 for (int j = 0; j < currentU.length; j++) 198 { 199 xorU[j] = (byte) (currentU[j] ^ xorU[j]); 200 } 201 } 202 203 lastU = currentU; 204 } 205 return xorU; 206 } 207 208 209 /** 210 * Prevents public instantiation. 211 */ 212 private PBKDF2() {} 213}