Skip to content

Commit

Permalink
Add support for PBKDF2 algorithms
Browse files Browse the repository at this point in the history
The following algorithms will be added to the OpenJCEPlusFIPS provider:
- PBKDF2WithHmacSHA224
- PBKDF2WithHmacSHA256
- PBKDF2WithHmacSHA384
- PBKDF2WithHmacSHA512

The following algorithms will be added to the OpenJCEPlus provider:
- PBKDF2WithHmacSHA1
- PBKDF2WithHmacSHA224
- PBKDF2WithHmacSHA256
- PBKDF2WithHmacSHA384
- PBKDF2WithHmacSHA512

Updates required include:
- Adding a new PBKDF2Core class based on OpenJDK.
- Adding a new PBKDF2KeyImpl class based on OpenJDK yet modified to
make use of the OCKC library to perform PBKDF2 key derivations.
- New JNI code to call a new native method to derive a key using PBKDF2
from a given password, salt, iteration count, and desired key length.
- Updates in makefiles to allow the new PBKDF.c file to be built into
the JNI dll.
- A new set of interoperability test was created to enforce that we get the same
results from SunJCE, OpenJCEPlus, OpenJCEPlusFIPS for the KeyFactory
methods `generateSecret`, `translateKey`, and `getKeySpec`.
- A new test was introduce to drive various KAT and error paths.

Signed-off-by: Jason Katonica <[email protected]>
  • Loading branch information
jasonkatonica committed Feb 6, 2025
1 parent 864e9a4 commit ef4f13c
Show file tree
Hide file tree
Showing 20 changed files with 1,317 additions and 16 deletions.
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

0 comments on commit ef4f13c

Please sign in to comment.