Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PBKDF2 algorithms #444

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ KeyFactory | X25519 | |X
KeyFactory | X448 | |X |
KeyFactory | XDH | |X |
KeyGenerator | AES |X |X |
KeyGenerator | ChaCha20 | |X |
KeyGenerator | ChaCha20 | |X |
KeyGenerator | DESede | |X |
KeyGenerator | HmacMD5 | |X |
KeyGenerator | HmacSHA1 | |X |
Expand Down Expand Up @@ -314,6 +314,11 @@ MessageDigest | SHA3-512 |X |X
SecretKeyFactory | AES |X |X |
SecretKeyFactory | ChaCha20 | |X |
SecretKeyFactory | DESede | |X |
SecretKeyFactory | PBKDF2WithHmacSHA1 | |X |
SecretKeyFactory | PBKDF2WithHmacSHA224 |X |X |
SecretKeyFactory | PBKDF2WithHmacSHA256 |X |X |
SecretKeyFactory | PBKDF2WithHmacSHA384 |X |X |
SecretKeyFactory | PBKDF2WithHmacSHA512 |X |X |
SecureRandom | SHA256DRBG |X |X |
SecureRandom | SHA512DRBG |X |X |
Signature | Ed25519 | |X |
Expand Down
37 changes: 35 additions & 2 deletions src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlus.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright IBM Corp. 2023, 2024
* Copyright IBM Corp. 2023, 2025
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
Expand Down Expand Up @@ -46,7 +46,7 @@ public final class OpenJCEPlus extends OpenJCEPlusProvider {
+ "Message authentication code (MAC) : HmacMD5, HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384, HmacSHA512\n"
+ " , HmacSHA3-224, HmacSHA3-256, HmacSHA3-384, HmacSHA3-512\n"
+ "Message digest : MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256, SHA3-224, SHA3-256, SHA3-384, SHA3-512\n"
+ "Secret key factory : AES, ChaCha20, DESede\n"
+ "Secret key factory : AES, ChaCha20, DESede, PBKDF2WithHmacSHA1, PBKDF2WithHmacSHA224, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA384, PBKDF2WithHmacSHA512\n"
+ "Secure random : HASHDRBG, SHA256DRBG, SHA512DRBG\n"
+ "Signature algorithms : NONEwithDSA, SHA1withDSA, SHA224withDSA, SHA256withDSA,\n"
+ " SHA3-224withDSA, SHA3-256withDSA, SHA3-384withDSA, SHA3-512withDSA,\n"
Expand Down Expand Up @@ -597,6 +597,39 @@ private void registerAlgorithms(Provider jce) {
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "AES",
"com.ibm.crypto.plus.provider.AESKeyFactory", aliases));

aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA1",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA1",
aliases));

aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA224",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA224",
aliases));

aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA256",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA256",
aliases));
aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA384",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA384",
aliases));
aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA512",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA512",
aliases));

aliases = new String[] {"TripleDES", "3DES"};
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "DESede",
"com.ibm.crypto.plus.provider.DESedeKeyFactory", aliases));
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlusFIPS.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright IBM Corp. 2023, 2024
* Copyright IBM Corp. 2023, 2025
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
Expand Down Expand Up @@ -48,7 +48,7 @@ public final class OpenJCEPlusFIPS extends OpenJCEPlusProvider {
+ " HmacSHA384, HmacSHA512\n"
+ " HmacSHA3-224, HmacSHA3-256, HmacSHA3-384, HmacSHA3-512\n"
+ "Message digest : SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256, SHA3-224, SHA3-256, SHA3-384, SHA3-512\n"
+ "Secret key factory : AES\n"
+ "Secret key factory : AES, PBKDF2WithHmacSHA224, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA384, PBKDF2WithHmacSHA512\n"
+ "Secure random : HASHDRBG, SHA256DRBG, SHA512DRBG\n"
+ "Signature algorithms : NONEwithDSA, SHA224withDSA, SHA256withDSA,\n"
+ " NONEwithECDSA, SHA224withECDSA,\n"
Expand Down Expand Up @@ -510,6 +510,31 @@ private void registerAlgorithms(Provider jce) {
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "AES",
"com.ibm.crypto.plus.provider.AESKeyFactory", aliases));

aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA224",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA224",
aliases));

aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA256",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA256",
aliases));
aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA384",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA384",
aliases));
aliases = null;
putService(new OpenJCEPlusService(jce,
"SecretKeyFactory",
"PBKDF2WithHmacSHA512",
"com.ibm.crypto.plus.provider.PBKDF2Core$HmacSHA512",
aliases));

/* Not yet supported in FIPS mode
* aliases = null;
Expand Down
177 changes: 177 additions & 0 deletions src/main/java/com/ibm/crypto/plus/provider/PBKDF2Core.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright IBM Corp. 2025
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution.
*/

package com.ibm.crypto.plus.provider;

import java.security.InvalidKeyException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.PBEKeySpec;

/**
* This class implements a key factory for PBE keys derived using
* PBKDF2 with HmacSHA1/HmacSHA224/HmacSHA256/HmacSHA384/HmacSHA512
* pseudo random function (PRF) as defined in PKCS#5 v2.1.
*
* @author Valerie Peng
*
* See also same named class from OpenJDK. This class makes use of similar code.
*/
abstract class PBKDF2Core extends SecretKeyFactorySpi {

private final String prfAlgo;

/**
* Provider associated with this service instance.
*/
private OpenJCEPlusProvider provider = null;

PBKDF2Core(OpenJCEPlusProvider provider, String prfAlgo) {
this.provider = provider;
this.prfAlgo = prfAlgo;
}

/**
* Generates a <code>SecretKey</code> object from the provided key
* specification (key material).
*
* @param keySpec the specification (key material) of the secret key
*
* @return the secret key
*
* @exception InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key.
*/
protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
if (keySpec instanceof PBEKeySpec ks) {
return new PBKDF2KeyImpl(this.provider, ks, prfAlgo);
} else {
throw new InvalidKeySpecException("Only PBEKeySpec is accepted");
}
}

/**
* Returns a specification (key material) of the given key
* in the requested format.
*
* @param key the key
*
* @param keySpecCl the requested format in which the key material shall be
* returned
*
* @return the underlying key specification (key material) in the
* requested format
*
* @exception InvalidKeySpecException if the requested key
* specification is inappropriate for the given key, or the
* given key cannot be processed (e.g., the given key has an
* unrecognized algorithm or format).
*/
protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpecCl)
throws InvalidKeySpecException {
if (key instanceof javax.crypto.interfaces.PBEKey pKey) {
// Check if requested key spec is amongst the valid ones
if ((keySpecCl != null) && keySpecCl.isAssignableFrom(PBEKeySpec.class)) {
char[] passwd = pKey.getPassword();
byte[] encoded = pKey.getEncoded();
try {
return new PBEKeySpec(passwd, pKey.getSalt(), pKey.getIterationCount(),
encoded.length * 8);
} finally {
if (passwd != null) {
Arrays.fill(passwd, (char) 0);
}
Arrays.fill(encoded, (byte) 0);
}
} else {
throw new InvalidKeySpecException("Only PBEKeySpec is accepted");
}
} else {
throw new InvalidKeySpecException("Only PBEKey is accepted");
}
}

/**
* Translates a <code>SecretKey</code> object, whose provider may be
* unknown or potentially untrusted, into a corresponding
* <code>SecretKey</code> object of this key factory.
*
* @param key the key whose provider is unknown or untrusted
*
* @return the translated key
*
* @exception InvalidKeyException if the given key cannot be processed by
* this key factory.
*/
protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
if ((key != null) && (key.getAlgorithm().equalsIgnoreCase("PBKDF2With" + prfAlgo))
&& (key.getFormat().equalsIgnoreCase("RAW"))) {

// Check if key originates from this factory, if true simply return it.
if (key instanceof com.ibm.crypto.plus.provider.PBKDF2KeyImpl) {
return key;
}

// Check if key implements the PBEKey
if (key instanceof javax.crypto.interfaces.PBEKey pKey) {
char[] password = pKey.getPassword();
byte[] encoding = pKey.getEncoded();
PBEKeySpec spec = new PBEKeySpec(password, pKey.getSalt(), pKey.getIterationCount(),
encoding.length * 8);
try {
return new PBKDF2KeyImpl(this.provider, spec, prfAlgo);
} catch (InvalidKeySpecException re) {
throw new InvalidKeyException("Invalid key component(s)", re);
} finally {
if (password != null) {
Arrays.fill(password, (char) 0);
spec.clearPassword();
}
Arrays.fill(encoding, (byte) 0);
}
} else {
throw new InvalidKeyException("Only PBEKey is accepted");
}
}
throw new InvalidKeyException(
"Only PBKDF2With" + prfAlgo + " key with RAW format is accepted");
}

public static final class HmacSHA1 extends PBKDF2Core {
public HmacSHA1(OpenJCEPlusProvider provider) {
super(provider, "HmacSHA1");
}
}

public static final class HmacSHA224 extends PBKDF2Core {
public HmacSHA224(OpenJCEPlusProvider provider) {
super(provider, "HmacSHA224");
}
}

public static final class HmacSHA256 extends PBKDF2Core {
public HmacSHA256(OpenJCEPlusProvider provider) {
super(provider, "HmacSHA256");
}
}

public static final class HmacSHA384 extends PBKDF2Core {
public HmacSHA384(OpenJCEPlusProvider provider) {
super(provider, "HmacSHA384");
}
}

public static final class HmacSHA512 extends PBKDF2Core {
public HmacSHA512(OpenJCEPlusProvider provider) {
super(provider, "HmacSHA512");
}
}
}
Loading