wolfcrypt-jni/src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java

712 lines
22 KiB
Java

/* WolfCryptSecretKeyFactory.java
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
package com.wolfssl.provider.jce;
import java.util.Arrays;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.interfaces.PBEKey;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import com.wolfssl.wolfcrypt.WolfCrypt;
import com.wolfssl.wolfcrypt.Pwdbased;
/**
* wolfCrypt JCE SecretKeyFactory implementation.
*/
public class WolfCryptSecretKeyFactory extends SecretKeyFactorySpi {
private enum FactoryType {
WC_SKF_PBKDF2_HMAC_SHA1,
WC_SKF_PBKDF2_HMAC_SHA224,
WC_SKF_PBKDF2_HMAC_SHA256,
WC_SKF_PBKDF2_HMAC_SHA384,
WC_SKF_PBKDF2_HMAC_SHA512,
WC_SKF_PBKDF2_HMAC_SHA3_224,
WC_SKF_PBKDF2_HMAC_SHA3_256,
WC_SKF_PBKDF2_HMAC_SHA3_384,
WC_SKF_PBKDF2_HMAC_SHA3_512
}
/* PBKDF2/HMAC type of this factory */
private FactoryType factoryType;
/* String representation of this factory type */
private String typeString;
/* wolfCrypt int representing hash used in this factory */
private int hashType;
private WolfCryptSecretKeyFactory(FactoryType type)
throws NoSuchAlgorithmException {
this.factoryType = type;
switch (type) {
case WC_SKF_PBKDF2_HMAC_SHA1:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA;
break;
case WC_SKF_PBKDF2_HMAC_SHA224:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA224;
break;
case WC_SKF_PBKDF2_HMAC_SHA256:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA256;
break;
case WC_SKF_PBKDF2_HMAC_SHA384:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA384;
break;
case WC_SKF_PBKDF2_HMAC_SHA512:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA512;
break;
case WC_SKF_PBKDF2_HMAC_SHA3_224:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_224;
break;
case WC_SKF_PBKDF2_HMAC_SHA3_256:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_256;
break;
case WC_SKF_PBKDF2_HMAC_SHA3_384:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_384;
break;
case WC_SKF_PBKDF2_HMAC_SHA3_512:
this.hashType = WolfCrypt.WC_HASH_TYPE_SHA3_512;
break;
default:
throw new NoSuchAlgorithmException(
"Unsupported SecretKeyFactory type");
}
typeString = typeToString(type);
log("created new SecretKeyFactory");
}
/**
* Internal method for logging output.
*
* @param msg message to be logged
*/
private void log(String msg) {
WolfCryptDebug.log(getClass(), WolfCryptDebug.INFO,
() -> "[" + typeString + "] " + msg);
}
/**
* Return String name of provided FactoryType.
*
* @param type FactoryType to return corresponding name String for
*
* @return String name matching provided FactoryType
*/
private String typeToString(FactoryType type) {
switch (type) {
case WC_SKF_PBKDF2_HMAC_SHA1:
return "PBKDF2WithHmacSHA1";
case WC_SKF_PBKDF2_HMAC_SHA224:
return "PBKDF2WithHmacSHA224";
case WC_SKF_PBKDF2_HMAC_SHA256:
return "PBKDF2WithHmacSHA256";
case WC_SKF_PBKDF2_HMAC_SHA384:
return "PBKDF2WithHmacSHA384";
case WC_SKF_PBKDF2_HMAC_SHA512:
return "PBKDF2WithHmacSHA512";
case WC_SKF_PBKDF2_HMAC_SHA3_224:
return "PBKDF2WithHmacSHA3-224";
case WC_SKF_PBKDF2_HMAC_SHA3_256:
return "PBKDF2WithHmacSHA3-256";
case WC_SKF_PBKDF2_HMAC_SHA3_384:
return "PBKDF2WithHmacSHA3-384";
case WC_SKF_PBKDF2_HMAC_SHA3_512:
return "PBKDF2WithHmacSHA3-512";
default:
return "None";
}
}
/**
* Test if this SecretKeyFactory is PBKDF2.
*
* @return true if PBKDF2 factory, otherwise false
*/
private boolean isFactoryPBKDF() {
switch (this.factoryType) {
case WC_SKF_PBKDF2_HMAC_SHA1:
case WC_SKF_PBKDF2_HMAC_SHA224:
case WC_SKF_PBKDF2_HMAC_SHA256:
case WC_SKF_PBKDF2_HMAC_SHA384:
case WC_SKF_PBKDF2_HMAC_SHA512:
case WC_SKF_PBKDF2_HMAC_SHA3_224:
case WC_SKF_PBKDF2_HMAC_SHA3_256:
case WC_SKF_PBKDF2_HMAC_SHA3_384:
case WC_SKF_PBKDF2_HMAC_SHA3_512:
return true;
default:
return false;
}
}
/**
* Test if provided algorithm String is supported by this
* SecretKeyFactory.
*
* @param algorithm String to test for support
*
* @return true if supported, otherwise false
*/
private boolean isAlgorithmSupported(String algo) {
if (algo == null) {
return false;
}
switch (algo) {
case "PBKDF2WithHmacSHA1":
case "PBKDF2WithHmacSHA224":
case "PBKDF2WithHmacSHA256":
case "PBKDF2WithHmacSHA384":
case "PBKDF2WithHmacSHA512":
case "PBKDF2WithHmacSHA3-224":
case "PBKDF2WithHmacSHA3-256":
case "PBKDF2WithHmacSHA3-384":
case "PBKDF2WithHmacSHA3-512":
return true;
default:
return false;
}
}
/**
* Check if provided KeySpec is supported by this SecretKeyFactory.
*
* @throws InvalidKeySpecException if KeySpec is invalid or incompatible
* with this factory
*/
private void checkKeySpecSupported(KeySpec spec)
throws InvalidKeySpecException {
if (spec == null) {
throw new InvalidKeySpecException("KeySpec cannot be null");
}
if (isFactoryPBKDF()) {
if (!(spec instanceof PBEKeySpec)) {
throw new InvalidKeySpecException(
"KeySpec must be type PBEKeySpec");
}
} else {
throw new InvalidKeySpecException(
"Unsupported SecretKeyFactory type");
}
}
/**
* Convert password from char[] to byte[].
*
* RFC 2898 (PBKDF2) considers password to be an octet string and
* recommends for interop ASCII or UTF-8 encoding is used. SunJCE uses
* UTF-8 for PBKDF2 SecretKeyFactory, so we do the same here for interop
* compatibility.
*
* @param pass password as char array
*
* @return password as UTF-8 encoded byte array, or null if input password
* is null or zero length
*/
protected static byte[] passwordToByteArray(char[] pass) {
byte[] passBytes = null;
CharBuffer passBuf = null;
ByteBuffer utf8Buf = null;
if (pass == null || pass.length == 0) {
return null;
}
passBuf = CharBuffer.wrap(pass);
utf8Buf = StandardCharsets.UTF_8.encode(passBuf);
passBytes = new byte[utf8Buf.limit()];
utf8Buf.get(passBytes);
return passBytes;
}
/**
* Generate SecretKey (PBEKey) from provided PBEKeySpec.
*
* @param spec PBEKeySpec to use for generating SecretKey
*
* @throws InvalidKeySpecException if SecretKey generation fails
*/
private SecretKey genSecretKeyFromPBEKeySpec(PBEKeySpec spec)
throws InvalidKeySpecException {
int iterations;
int kLen;
byte[] salt = null;
char[] pass = null;
byte[] derivedKey = null;
SecretKey key = null;
try {
iterations = spec.getIterationCount();
kLen = spec.getKeyLength();
salt = spec.getSalt();
pass = spec.getPassword();
if (salt == null || salt.length == 0) {
throw new InvalidKeySpecException(
"Null or zero length salt not allowed");
}
if (kLen < 8) {
throw new InvalidKeySpecException(
"Key length must be at least one byte (8 bits)");
}
if ((kLen % 8) != 0) {
throw new InvalidKeySpecException(
"Key length bits is not divisible by 8 (byte conversion)");
}
/* Key length is given in bits, convert to bytes */
kLen = kLen / 8;
log("generating PBEKey (iterations: " + iterations +
", key len: " + kLen + " bytes)");
derivedKey = Pwdbased.PBKDF2(passwordToByteArray(pass),
salt, iterations, kLen, this.hashType);
if (derivedKey == null || derivedKey.length == 0) {
throw new InvalidKeySpecException(
"Error deriving key with PBKDF2");
}
key = new WolfCryptPBEKey(pass, salt, iterations,
this.typeString, derivedKey);
} finally {
iterations = 0;
kLen = 0;
if (salt != null) {
Arrays.fill(salt, (byte)0);
}
if (pass != null) {
Arrays.fill(pass, (char)0);
}
if (derivedKey != null) {
Arrays.fill(derivedKey, (byte)0);
}
}
return key;
}
/**
* Generate a SecretKey object from the provided KeySpec.
*
* @param spec specification of the secret key
*
* @return SecretKey generated from KeySpec
*
* @throws InvalidKeySpecException if provided KeySpec is incorrect
* or incomplete for generating a SecretKey
*/
@Override
protected synchronized SecretKey engineGenerateSecret(KeySpec spec)
throws InvalidKeySpecException {
log("generating SecretKey from KeySpec");
checkKeySpecSupported(spec);
return genSecretKeyFromPBEKeySpec((PBEKeySpec)spec);
}
/**
* Return a KeySpec from the provided PBEKey in the requested format.
*
* Called by engineGetKeySpec().
*
* @param key PBEKey for which to return KeySpec
* @param keSpec the requested format that the KeySpec should be returned in
*
* @return KeySpec for the PBEKey, in the requested format
*
* @throws InvalidKeySpecException if the requested format is not
* appropriate for the given key, or the provided PBEKey
* cannot be used.
*/
private KeySpec getKeySpecFromPBEKeyByType(PBEKey key, Class<?> keySpec)
throws InvalidKeySpecException {
int iterations = 0;
char[] password = null;
byte[] salt = null;
byte[] encoded = null;
PBEKeySpec pbSpec = null;
if (key != null && keySpec != null) {
if (keySpec.isAssignableFrom(PBEKeySpec.class)) {
try {
password = key.getPassword();
salt = key.getSalt();
iterations = key.getIterationCount();
encoded = key.getEncoded();
if (encoded == null) {
throw new InvalidKeySpecException(
"Error getting encoded key from PBEKey");
}
pbSpec = new PBEKeySpec(password, salt, iterations,
encoded.length);
} finally {
if (password != null) {
Arrays.fill(password, (char)0);
}
if (salt != null) {
Arrays.fill(salt, (byte)0);
}
if (encoded != null) {
Arrays.fill(encoded, (byte)0);
}
}
}
}
return pbSpec;
}
/**
* Return a KeySpec (key material) of the provided SecretKey in the
* requested format.
*
* @param key SecretKey for which to return KeySpec
* @param keySpec the requested format that the KeySpec should be
* returned in
*
* @return the KeySpec for the SecretKey in the requested format
*
* @throws InvalidKeySpecException if the requested format is not
* appropriate for the given key, or the provided SecretKey
* cannot be used.
*/
@Override
protected synchronized KeySpec engineGetKeySpec(SecretKey key,
Class<?> keySpec) throws InvalidKeySpecException {
log("returning KeySpec from SecretKey in requested type");
if (key == null) {
throw new InvalidKeySpecException("SecretKey cannot be null");
}
if (keySpec == null) {
throw new InvalidKeySpecException(
"Requested KeySpec format cannot be null");
}
if (key instanceof PBEKey) {
return getKeySpecFromPBEKeyByType((PBEKey)key, keySpec);
}
else {
throw new InvalidKeySpecException(
"Only SecretKey of type PBEKey currently supported");
}
}
/**
* Translates PBEKey to one generated by this SecretKeyFactory.
*
* Called by engineTranslateKey().
*
* @param PBEKey (SecretKey) to translate
*
* @return New/translated SecretKey (PBEKey) generated by this
* SecretKeyFactory.
*
* @throws InvalidKeyException if the provided SecretKey can not be
* used or converted
*/
private SecretKey translatePBEKey(PBEKey key)
throws InvalidKeyException {
char[] password = null;
byte[] salt = null;
byte[] enc = null;
int iterations = 0;
PBEKeySpec spec = null;
SecretKey sKey = null;
if (key != null) {
if (!isAlgorithmSupported(key.getAlgorithm())) {
throw new InvalidKeyException(
"SecretKey algorithm not supported: " + key.getAlgorithm());
}
try {
iterations = key.getIterationCount();
salt = key.getSalt();
password = key.getPassword();
enc = key.getEncoded();
if (enc == null) {
throw new InvalidKeySpecException(
"Error getting encoded key from PBEKey");
}
/* PBEKeySpec holds key length in bits */
spec = new PBEKeySpec(password, salt, iterations,
enc.length * 8);
sKey = genSecretKeyFromPBEKeySpec(spec);
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException(e);
} finally {
spec.clearPassword();
if (password != null) {
Arrays.fill(password, (char)0);
}
if (salt != null) {
Arrays.fill(salt, (byte)0);
}
if (enc != null) {
Arrays.fill(enc, (byte)0);
}
}
}
return sKey;
}
/**
* Translate a SecretKey object from another provider (or unknown source)
* into a SecretKey object from this SecretKeyFactory.
*
* This method will extract necessary parameters from the original
* SecretKey then re-generate the SecretKey using this factory.
*
* @param key SecretKey to translate
*
* @return Translated SecretKey object from this SecretKeyFactory
*
* @throws InvalidKeyException if the provided SecretKey can not be
* used or converted
*/
@Override
protected synchronized SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException {
log("translating SecretKey to wolfJCE SecretKeyFactory type");
if (key == null) {
throw new InvalidKeyException("SecretKey cannot be null");
}
if (key instanceof PBEKey) {
return translatePBEKey((PBEKey)key);
}
else {
throw new InvalidKeyException(
"Only SecretKey of type PBEKey currently supported");
}
}
/**
* wolfJCE PBKDF2WithHmacSHA1 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA1
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA1 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA1 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA1() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA1);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA224 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA224
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA224 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA224 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA224() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA224);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA256 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA256
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA256 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA256 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA256() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA256);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA384 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA384
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA384 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA384 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA384() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA384);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA512 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA512
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA512 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA512 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA512() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA512);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA3_224 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA3_224
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA3_224 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-224 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA3_224() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_224);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA3_256 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA3_256
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA3_256 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-256 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA3_256() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_256);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA3_384 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA3_384
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA3_384 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-384 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA3_384() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_384);
}
}
/**
* wolfJCE PBKDF2WithHmacSHA3_512 SecretKeyFactory class.
*/
public static final class wcPBKDF2WithHmacSHA3_512
extends WolfCryptSecretKeyFactory {
/**
* Create new wcPBKDF2WithHmacSHA3_512 object.
*
* @throws NoSuchAlgorithmException if PBKDF2-HMAC-SHA3-512 is not
* available in native wolfCrypt.
*/
public wcPBKDF2WithHmacSHA3_512() throws NoSuchAlgorithmException {
super(FactoryType.WC_SKF_PBKDF2_HMAC_SHA3_512);
}
}
}