Skip to content

Commit e40e967

Browse files
committed
Support for PBES2, PBEKeyfactory, PBEKey and PBEParameters
This update adds support for PBES2, PBEKeyFactory, PBEKey, and PBEParameters for PBE algorithms with Hmac and AES for OpenJCEPlus provider. Signed-off-by: Dev Agarwal <[email protected]>
1 parent 8530e03 commit e40e967

20 files changed

+2410
-165
lines changed

JenkinsfilePerformance

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ pipeline {
357357
'ibm.jceplus.jmh.MessageDigestInstanceBenchmark', \
358358
'ibm.jceplus.jmh.MLDSABenchmark', \
359359
'ibm.jceplus.jmh.MLKEMBenchmark', \
360+
'ibm.jceplus.jmh.PBEBenchmark', \
360361
'ibm.jceplus.jmh.PBKDF2Benchmark', \
361362
'ibm.jceplus.jmh.RandomBenchmark', \
362363
'ibm.jceplus.jmh.RSAKeyGeneratorBenchmark', \

README.md

Lines changed: 194 additions & 164 deletions
Large diffs are not rendered by default.

src/main/java/com/ibm/crypto/plus/provider/OpenJCEPlus.java

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ public final class OpenJCEPlus extends OpenJCEPlusProvider {
3232

3333
private static final String info = "OpenJCEPlus Provider implements the following:\n"
3434
+ "Algorithm parameter : AES, ChaCha20, ChaCha20-Poly1305, DESede, DiffieHellman, DSA, EC, XEC, GCM, CCM, OAEP, RSAPSS\n"
35+
+ " PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256\n"
36+
+ " PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256\n"
37+
+ " PBEWithHmacSHA512AndAES_128, PBEWithHmacSHA512AndAES_256\n"
3538
+ "Algorithm parameter generator : DiffieHellman, DSA, EC, XEC, GCM, CCM\n"
3639
+ "Cipher algorithms : AES, ChaCha20, ChaCha20-Poly1305, DESede, RSA\n"
40+
+ " PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256\n"
41+
+ " PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256\n"
42+
+ " PBEWithHmacSHA512AndAES_128, PBEWithHmacSHA512AndAES_256\n"
3743
+ "Key agreement algorithms : DiffieHellman, ECDH, XDH\n"
3844
+ "Key Encapsulation Mechanisms : ML-KEM-512, ML-KEM-768, ML-KEM-1024\n"
3945
+ "Key factory : DiffieHellman, DSA, EC, XEC, RSA, RSAPSS, ML-KEM-512, ML-KEM-768, ML-KEM-1024\n"
@@ -50,6 +56,9 @@ public final class OpenJCEPlus extends OpenJCEPlusProvider {
5056
+ "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"
5157
+ "Secret key factory : AES, ChaCha20, DESede, PBKDF2WithHmacSHA1, PBKDF2WithHmacSHA224, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA384, PBKDF2WithHmacSHA512\n"
5258
+ " PBKDF2WithHmacSHA512/224, PBKDF2WithHmacSHA512/256\n"
59+
+ " PBEWithHmacSHA1AndAES_128, PBEWithHmacSHA1AndAES_256, PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA224AndAES_256\n"
60+
+ " PBEWithHmacSHA256AndAES_128, PBEWithHmacSHA256AndAES_256, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA384AndAES_256\n"
61+
+ " PBEWithHmacSHA512AndAES_128, PBEWithHmacSHA512AndAES_256\n"
5362
+ "Secure random : HASHDRBG, SHA256DRBG, SHA512DRBG\n"
5463
+ "Signature algorithms : NONEwithDSA, SHA1withDSA, SHA224withDSA, SHA256withDSA,\n"
5564
+ " SHA3-224withDSA, SHA3-256withDSA, SHA3-384withDSA, SHA3-512withDSA,\n"
@@ -154,7 +163,47 @@ private void registerAlgorithms(Provider jce) {
154163
aliases = null;
155164
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "OAEP",
156165
"com.ibm.crypto.plus.provider.OAEPParameters", aliases));
166+
167+
aliases = null;
168+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA1AndAES_128",
169+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA1AndAES_128", aliases));
170+
171+
aliases = null;
172+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA1AndAES_256",
173+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA1AndAES_256", aliases));
174+
175+
aliases = null;
176+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA224AndAES_128",
177+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA224AndAES_128", aliases));
178+
179+
aliases = null;
180+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA224AndAES_256",
181+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA224AndAES_256", aliases));
182+
183+
aliases = null;
184+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA256AndAES_128",
185+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA256AndAES_128", aliases));
186+
187+
aliases = null;
188+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA256AndAES_256",
189+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA256AndAES_256", aliases));
190+
191+
aliases = null;
192+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA384AndAES_128",
193+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA384AndAES_128", aliases));
157194

195+
aliases = null;
196+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA384AndAES_256",
197+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA384AndAES_256", aliases));
198+
199+
aliases = null;
200+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512AndAES_128",
201+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512AndAES_128", aliases));
202+
203+
aliases = null;
204+
putService(new OpenJCEPlusService(jce, "AlgorithmParameters", "PBEWithHmacSHA512AndAES_256",
205+
"com.ibm.crypto.plus.provider.PBES2Parameters$HmacSHA512AndAES_256", aliases));
206+
158207
/*aliases = null;
159208
putService(new OpenJCEPlusService(jce,
160209
"AlgorithmParameters",
@@ -269,6 +318,47 @@ private void registerAlgorithms(Provider jce) {
269318
putService(new OpenJCEPlusService(jce, "Cipher", "ChaCha20-Poly1305",
270319
"com.ibm.crypto.plus.provider.ChaCha20Poly1305Cipher", aliases));
271320

321+
aliases = null;
322+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA1AndAES_128",
323+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA1AndAES_128", aliases));
324+
325+
aliases = null;
326+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA1AndAES_256",
327+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA1AndAES_256", aliases));
328+
329+
aliases = null;
330+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA224AndAES_128",
331+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA224AndAES_128", aliases));
332+
333+
aliases = null;
334+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA224AndAES_256",
335+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA224AndAES_256", aliases));
336+
337+
aliases = null;
338+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA256AndAES_128",
339+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA256AndAES_128", aliases));
340+
341+
aliases = null;
342+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA256AndAES_256",
343+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA256AndAES_256", aliases));
344+
345+
aliases = null;
346+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA384AndAES_128",
347+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA384AndAES_128", aliases));
348+
349+
aliases = null;
350+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA384AndAES_256",
351+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA384AndAES_256", aliases));
352+
353+
aliases = null;
354+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512AndAES_128",
355+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512AndAES_128", aliases));
356+
357+
aliases = null;
358+
putService(new OpenJCEPlusService(jce, "Cipher", "PBEWithHmacSHA512AndAES_256",
359+
"com.ibm.crypto.plus.provider.PBES2Core$HmacSHA512AndAES_256", aliases));
360+
361+
272362
/* =======================================================================
273363
* Key agreement
274364
* =======================================================================
@@ -785,6 +875,47 @@ private void registerAlgorithms(Provider jce) {
785875
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "ChaCha20",
786876
"com.ibm.crypto.plus.provider.ChaCha20KeyFactory", aliases));
787877

878+
aliases = null;
879+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA1AndAES_128",
880+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_128", aliases));
881+
882+
aliases = null;
883+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA1AndAES_256",
884+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_256", aliases));
885+
886+
aliases = null;
887+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA224AndAES_128",
888+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_128", aliases));
889+
890+
aliases = null;
891+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA224AndAES_256",
892+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_256", aliases));
893+
894+
aliases = null;
895+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA256AndAES_128",
896+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_128", aliases));
897+
898+
aliases = null;
899+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA256AndAES_256",
900+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_256", aliases));
901+
902+
aliases = null;
903+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA384AndAES_128",
904+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_128", aliases));
905+
906+
aliases = null;
907+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA384AndAES_256",
908+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_256", aliases));
909+
910+
aliases = null;
911+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512AndAES_128",
912+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_128", aliases));
913+
914+
aliases = null;
915+
putService(new OpenJCEPlusService(jce, "SecretKeyFactory", "PBEWithHmacSHA512AndAES_256",
916+
"com.ibm.crypto.plus.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_256", aliases));
917+
918+
788919
/* =======================================================================
789920
* SecureRandom
790921
* =======================================================================
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Copyright IBM Corp. 2025
3+
*
4+
* This code is free software; you can redistribute it and/or modify it
5+
* under the terms provided by IBM in the LICENSE file that accompanied
6+
* this code, including the "Classpath" Exception described therein.
7+
*/
8+
9+
package com.ibm.crypto.plus.provider;
10+
11+
import java.io.IOException;
12+
import java.io.InvalidObjectException;
13+
import java.lang.ref.Reference;
14+
import java.nio.ByteBuffer;
15+
import java.nio.CharBuffer;
16+
import java.nio.charset.StandardCharsets;
17+
import java.security.MessageDigest;
18+
import java.security.spec.InvalidKeySpecException;
19+
import java.util.Arrays;
20+
import java.util.Locale;
21+
import javax.crypto.SecretKey;
22+
import javax.crypto.spec.PBEKeySpec;
23+
24+
final class PBEKey implements SecretKey {
25+
26+
@java.io.Serial
27+
private static final long serialVersionUID = 9223372036854775807L;
28+
29+
private byte[] key;
30+
31+
private final String type;
32+
33+
private boolean destroyed = false;
34+
35+
private OpenJCEPlusProvider provider = null;
36+
37+
/**
38+
* Creates a PBE key from a given PBE key specification.
39+
*
40+
* @param keytype the given PBE key specification
41+
*/
42+
PBEKey(OpenJCEPlusProvider provider, PBEKeySpec keySpec, String keytype) throws InvalidKeySpecException {
43+
char[] passwd = keySpec.getPassword();
44+
45+
if (passwd == null || passwd.length == 0) {
46+
// Should allow an empty password.
47+
passwd = new char[0];
48+
}
49+
50+
for (char c : passwd) {
51+
if (Character.isISOControl(c))
52+
throw new InvalidKeySpecException("Invalid Password.");
53+
}
54+
55+
this.key = encodePassword(passwd);
56+
Arrays.fill(passwd, '\0');
57+
type = keytype;
58+
this.provider = provider;
59+
}
60+
61+
public byte[] getEncoded() {
62+
try {
63+
return key.clone();
64+
} finally {
65+
// prevent this from being cleaned for the above block
66+
Reference.reachabilityFence(this);
67+
}
68+
}
69+
70+
public String getAlgorithm() {
71+
return type;
72+
}
73+
74+
public String getFormat() {
75+
return "RAW";
76+
}
77+
78+
@Override
79+
public int hashCode() {
80+
try {
81+
int retval = 0;
82+
for (int i = 1; i < this.key.length; i++) {
83+
retval += this.key[i] * i;
84+
}
85+
return(retval ^ getAlgorithm().toLowerCase(Locale.ENGLISH).hashCode());
86+
} finally {
87+
// prevent this from being cleaned for the above block
88+
Reference.reachabilityFence(this);
89+
}
90+
}
91+
92+
@Override
93+
public boolean equals(Object obj) {
94+
try {
95+
if (obj == this)
96+
return true;
97+
98+
if (!(obj instanceof SecretKey that))
99+
return false;
100+
101+
if (isDestroyed() || that.isDestroyed()) {
102+
return false;
103+
}
104+
105+
if (!(that.getAlgorithm().equalsIgnoreCase(type)))
106+
return false;
107+
108+
byte[] thatEncoded = that.getEncoded();
109+
boolean ret = MessageDigest.isEqual(this.key, thatEncoded);
110+
Arrays.fill(thatEncoded, (byte) 0x00);
111+
return ret;
112+
} finally {
113+
// prevent this from being cleaned for the above block
114+
Reference.reachabilityFence(this);
115+
}
116+
}
117+
118+
@Override
119+
public void destroy() {
120+
if (this.key != null) {
121+
Arrays.fill(this.key, (byte) 0x00);
122+
destroyed = true;
123+
}
124+
}
125+
126+
@Override
127+
public boolean isDestroyed() {
128+
return destroyed;
129+
}
130+
131+
/**
132+
* Restores the state of this object from the stream.
133+
*
134+
* @param s the {@code ObjectInputStream} from which data is read
135+
* @throws IOException if an I/O error occurs
136+
* @throws ClassNotFoundException if a serialized class cannot be loaded
137+
*/
138+
@java.io.Serial
139+
private void readObject(java.io.ObjectInputStream s)
140+
throws IOException, ClassNotFoundException
141+
{
142+
s.defaultReadObject();
143+
if (key == null) {
144+
throw new InvalidObjectException(
145+
"PBEKey couldn't be deserialized");
146+
}
147+
byte[] temp = key;
148+
key = temp.clone();
149+
Arrays.fill(temp, (byte) 0x00);
150+
151+
// Accept "\0" to signify "zero-length password with no terminator".
152+
if (!(key.length == 1 && key[0] == 0)) {
153+
for (int i = 0; i < key.length; i++) {
154+
if ((key[i] < '\u0020') || (key[i] > '\u007E')) {
155+
throw new InvalidObjectException(
156+
"PBEKey had non-ASCII chars");
157+
}
158+
}
159+
}
160+
}
161+
162+
163+
/**
164+
* Replace the PBE key to be serialized.
165+
*
166+
* @return the standard KeyRep object to be serialized
167+
*
168+
* @throws java.io.ObjectStreamException if a new object representing
169+
* this PBE key could not be created
170+
*/
171+
@java.io.Serial
172+
private Object writeReplace() throws java.io.ObjectStreamException {
173+
try {
174+
return new JCEPlusKeyRep(JCEPlusKeyRep.Type.SECRET,
175+
getAlgorithm(), getFormat(), getEncoded(), provider.getName());
176+
} finally {
177+
// prevent this from being cleaned for the above block
178+
Reference.reachabilityFence(this);
179+
}
180+
}
181+
182+
private byte[] encodePassword(char[] passwd) {
183+
ByteBuffer bb = StandardCharsets.UTF_8.encode(CharBuffer.wrap(passwd));
184+
int len = bb.limit();
185+
byte[] passwdBytes = new byte[len];
186+
bb.get(passwdBytes, 0, len);
187+
bb.clear().put(new byte[len]);
188+
189+
return passwdBytes;
190+
}
191+
192+
protected void finalize() throws Throwable {
193+
try {
194+
destroy();
195+
} finally {
196+
super.finalize();
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)