Skip to content

Commit 6ddbcc3

Browse files
martinuyfranferrax
andcommitted
8328119: Support HKDF in SunPKCS11 (Preview)
8346720: Support Generic keys in SunPKCS11 SecretKeyFactory Co-authored-by: Francisco Ferrari Bihurriet <[email protected]> Co-authored-by: Martin Balao <[email protected]> Reviewed-by: valeriep, kdriver, weijun
1 parent 28e744d commit 6ddbcc3

File tree

16 files changed

+1582
-66
lines changed

16 files changed

+1582
-66
lines changed

src/java.base/share/classes/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
java.compiler,
157157
java.desktop, // for ScopedValue
158158
jdk.compiler,
159+
jdk.crypto.cryptoki, // participates in preview features
159160
jdk.incubator.vector, // participates in preview features
160161
jdk.jartool, // participates in preview features
161162
jdk.jdeps, // participates in preview features

src/jdk.crypto.cryptoki/share/classes/module-info.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,6 +23,8 @@
2323
* questions.
2424
*/
2525

26+
import jdk.internal.javac.ParticipatesInPreview;
27+
2628
/**
2729
* Provides the implementation of the SunPKCS11 security provider.
2830
*
@@ -31,6 +33,7 @@
3133
* @moduleGraph
3234
* @since 9
3335
*/
36+
@ParticipatesInPreview
3437
module jdk.crypto.cryptoki {
3538
provides java.security.Provider with sun.security.pkcs11.SunPKCS11;
3639
}
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
/*
2+
* Copyright (c) 2025, Red Hat, Inc.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package sun.security.pkcs11;
27+
28+
import javax.crypto.KDFParameters;
29+
import javax.crypto.KDFSpi;
30+
import javax.crypto.SecretKey;
31+
import javax.crypto.spec.HKDFParameterSpec;
32+
import javax.crypto.spec.SecretKeySpec;
33+
import java.security.*;
34+
import java.security.spec.*;
35+
import java.util.Arrays;
36+
import java.util.List;
37+
38+
import static sun.security.pkcs11.TemplateManager.*;
39+
import sun.security.pkcs11.wrapper.*;
40+
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
41+
42+
final class P11HKDF extends KDFSpi {
43+
private final Token token;
44+
private final P11SecretKeyFactory.HKDFKeyInfo svcKi;
45+
private static final SecretKey EMPTY_KEY = new SecretKey() {
46+
@Override
47+
public String getAlgorithm() {
48+
return "Generic";
49+
}
50+
51+
@Override
52+
public String getFormat() {
53+
return "RAW";
54+
}
55+
56+
@Override
57+
public byte[] getEncoded() {
58+
return new byte[0];
59+
}
60+
};
61+
62+
private static KDFParameters requireNull(KDFParameters kdfParameters,
63+
String message) throws InvalidAlgorithmParameterException {
64+
if (kdfParameters != null) {
65+
throw new InvalidAlgorithmParameterException(message);
66+
}
67+
return null;
68+
}
69+
70+
private void checkMechanismEnabled(long mechanism) {
71+
if (!token.provider.config.isEnabled(mechanism)) {
72+
throw new ProviderException("Mechanism " +
73+
Functions.getMechanismName(mechanism) +
74+
" is disabled through 'enabledMechanisms' or " +
75+
"'disabledMechanisms' in " + token.provider.getName() +
76+
" configuration.");
77+
}
78+
}
79+
80+
P11HKDF(Token token, String algorithm, KDFParameters kdfParameters)
81+
throws InvalidAlgorithmParameterException {
82+
super(requireNull(kdfParameters,
83+
algorithm + " does not support parameters"));
84+
this.token = token;
85+
this.svcKi = P11SecretKeyFactory.getHKDFKeyInfo(algorithm);
86+
assert this.svcKi != null : "Unsupported HKDF algorithm " + algorithm;
87+
}
88+
89+
@Override
90+
protected KDFParameters engineGetParameters() {
91+
return null;
92+
}
93+
94+
@Override
95+
protected SecretKey engineDeriveKey(String alg,
96+
AlgorithmParameterSpec derivationSpec)
97+
throws InvalidAlgorithmParameterException,
98+
NoSuchAlgorithmException {
99+
if (alg == null) {
100+
throw new NullPointerException("the algorithm for the " +
101+
"SecretKey return value must not be null");
102+
}
103+
if (alg.isEmpty()) {
104+
throw new NoSuchAlgorithmException("the algorithm for the " +
105+
"SecretKey return value must not be empty");
106+
}
107+
return derive(alg, derivationSpec, SecretKey.class);
108+
}
109+
110+
@Override
111+
protected byte[] engineDeriveData(AlgorithmParameterSpec derivationSpec)
112+
throws InvalidAlgorithmParameterException {
113+
return derive("Generic", derivationSpec, byte[].class);
114+
}
115+
116+
private <T> T derive(String alg, AlgorithmParameterSpec derivationSpec,
117+
Class<T> retType) throws InvalidAlgorithmParameterException {
118+
SecretKey baseKey;
119+
SecretKey salt = EMPTY_KEY;
120+
byte[] info = null;
121+
int outLen;
122+
boolean isExtract = false, isExpand = false;
123+
boolean isData = retType == byte[].class;
124+
assert isData || retType == SecretKey.class : "Invalid return type.";
125+
assert alg != null : "The algorithm cannot be null.";
126+
127+
long mechanism = isData ? CKM_HKDF_DATA : CKM_HKDF_DERIVE;
128+
checkMechanismEnabled(mechanism);
129+
130+
switch (derivationSpec) {
131+
case HKDFParameterSpec.Extract anExtract -> {
132+
isExtract = true;
133+
baseKey = consolidateKeyMaterial(anExtract.ikms());
134+
salt = consolidateKeyMaterial(anExtract.salts());
135+
outLen = svcKi.prkLen / 8;
136+
assert outLen * 8 == svcKi.prkLen : "Invalid PRK length.";
137+
}
138+
case HKDFParameterSpec.Expand anExpand -> {
139+
isExpand = true;
140+
baseKey = anExpand.prk();
141+
outLen = anExpand.length();
142+
info = anExpand.info();
143+
}
144+
case HKDFParameterSpec.ExtractThenExpand anExtractExpand -> {
145+
isExtract = true;
146+
isExpand = true;
147+
baseKey = consolidateKeyMaterial(anExtractExpand.ikms());
148+
salt = consolidateKeyMaterial(anExtractExpand.salts());
149+
outLen = anExtractExpand.length();
150+
info = anExtractExpand.info();
151+
}
152+
case null -> throw new NullPointerException(
153+
"derivationSpec must be a " + HKDFParameterSpec.class +
154+
" instance, instead of null.");
155+
default -> throw new InvalidAlgorithmParameterException(
156+
"derivationSpec must be a " + HKDFParameterSpec.class +
157+
" instance, instead of " + derivationSpec.getClass());
158+
}
159+
160+
P11Key p11BaseKey = convertKey(baseKey, (isExtract ? "IKM" : "PRK") +
161+
" could not be converted to a token key for HKDF derivation.");
162+
163+
long saltType = CKF_HKDF_SALT_NULL;
164+
byte[] saltBytes = null;
165+
P11Key p11SaltKey = null;
166+
if (salt instanceof SecretKeySpec) {
167+
saltType = CKF_HKDF_SALT_DATA;
168+
saltBytes = salt.getEncoded();
169+
} else if (salt != EMPTY_KEY) {
170+
// consolidateKeyMaterial returns a salt from the token.
171+
saltType = CKF_HKDF_SALT_KEY;
172+
p11SaltKey = (P11Key.P11SecretKey) salt;
173+
assert p11SaltKey.token == token : "salt must be from the same " +
174+
"token as service.";
175+
}
176+
177+
P11SecretKeyFactory.KeyInfo ki = P11SecretKeyFactory.getKeyInfo(alg);
178+
if (ki == null) {
179+
throw new InvalidAlgorithmParameterException("A PKCS #11 key " +
180+
"type (CKK_*) was not found for a key of the algorithm '" +
181+
alg + "'.");
182+
}
183+
long derivedKeyType = ki.keyType;
184+
long derivedKeyClass = isData ? CKO_DATA : CKO_SECRET_KEY;
185+
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
186+
new CK_ATTRIBUTE(CKA_CLASS, derivedKeyClass),
187+
new CK_ATTRIBUTE(CKA_KEY_TYPE, derivedKeyType),
188+
new CK_ATTRIBUTE(CKA_VALUE_LEN, outLen)
189+
};
190+
Session session = null;
191+
long baseKeyID = p11BaseKey.getKeyID();
192+
try {
193+
session = token.getOpSession();
194+
CK_HKDF_PARAMS params = new CK_HKDF_PARAMS(isExtract, isExpand,
195+
svcKi.hmacMech, saltType, saltBytes, p11SaltKey != null ?
196+
p11SaltKey.getKeyID() : 0L, info);
197+
attrs = token.getAttributes(O_GENERATE, derivedKeyClass,
198+
derivedKeyType, attrs);
199+
long derivedObjectID = token.p11.C_DeriveKey(session.id(),
200+
new CK_MECHANISM(mechanism, params), baseKeyID, attrs);
201+
Object ret;
202+
if (isData) {
203+
try {
204+
CK_ATTRIBUTE[] dataAttr = new CK_ATTRIBUTE[] {
205+
new CK_ATTRIBUTE(CKA_VALUE)
206+
};
207+
token.p11.C_GetAttributeValue(session.id(), derivedObjectID,
208+
dataAttr);
209+
ret = dataAttr[0].getByteArray();
210+
} finally {
211+
token.p11.C_DestroyObject(session.id(), derivedObjectID);
212+
}
213+
} else {
214+
ret = P11Key.secretKey(session, derivedObjectID, alg,
215+
outLen * 8, null);
216+
}
217+
return retType.cast(ret);
218+
} catch (PKCS11Exception e) {
219+
throw new ProviderException("HKDF derivation for algorithm '" +
220+
alg + "' failed.", e);
221+
} finally {
222+
if (p11SaltKey != null) {
223+
p11SaltKey.releaseKeyID();
224+
}
225+
p11BaseKey.releaseKeyID();
226+
token.releaseSession(session);
227+
}
228+
}
229+
230+
private P11Key.P11SecretKey convertKey(SecretKey key, String errorMessage) {
231+
try {
232+
return (P11Key.P11SecretKey) P11SecretKeyFactory.convertKey(token,
233+
key, null);
234+
} catch (InvalidKeyException ike) {
235+
throw new ProviderException(errorMessage, ike);
236+
}
237+
}
238+
239+
private abstract sealed class KeyMaterialMerger permits
240+
AnyKeyMaterialMerger, KeyKeyMaterialMerger, DataKeyMaterialMerger {
241+
242+
final KeyMaterialMerger merge(SecretKey nextKeyMaterial) {
243+
if (nextKeyMaterial instanceof SecretKeySpec) {
244+
return merge(nextKeyMaterial.getEncoded());
245+
} else {
246+
return merge(convertKey(nextKeyMaterial,
247+
"Failure when merging key material."));
248+
}
249+
}
250+
251+
abstract SecretKey getKeyMaterial();
252+
253+
protected abstract KeyMaterialMerger merge(byte[] nextKeyMaterial);
254+
255+
protected abstract KeyMaterialMerger merge(
256+
P11Key.P11SecretKey nextKeyMaterial);
257+
258+
protected final P11Key.P11SecretKey p11Merge(
259+
P11Key.P11SecretKey baseKey, CK_MECHANISM ckMech,
260+
int derivedKeyLen) {
261+
checkMechanismEnabled(ckMech.mechanism);
262+
Session session = null;
263+
long baseKeyID = baseKey.getKeyID();
264+
try {
265+
session = token.getOpSession();
266+
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
267+
new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
268+
new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET),
269+
};
270+
long derivedKeyID = token.p11.C_DeriveKey(session.id(), ckMech,
271+
baseKeyID, attrs);
272+
return (P11Key.P11SecretKey) P11Key.secretKey(session,
273+
derivedKeyID, "Generic", derivedKeyLen * 8, null);
274+
} catch (PKCS11Exception e) {
275+
throw new ProviderException("Failure when merging key " +
276+
"material.", e);
277+
} finally {
278+
baseKey.releaseKeyID();
279+
token.releaseSession(session);
280+
}
281+
}
282+
}
283+
284+
private final class AnyKeyMaterialMerger extends KeyMaterialMerger {
285+
286+
protected KeyMaterialMerger merge(byte[] nextKeyMaterial) {
287+
return P11HKDF.this.new DataKeyMaterialMerger(nextKeyMaterial);
288+
}
289+
290+
protected KeyMaterialMerger merge(P11Key.P11SecretKey nextKeyMaterial) {
291+
return P11HKDF.this.new KeyKeyMaterialMerger(nextKeyMaterial);
292+
}
293+
294+
SecretKey getKeyMaterial() {
295+
return EMPTY_KEY;
296+
}
297+
}
298+
299+
private final class KeyKeyMaterialMerger extends KeyMaterialMerger {
300+
private P11Key.P11SecretKey keyMaterial;
301+
302+
KeyKeyMaterialMerger(P11Key.P11SecretKey keyMaterial) {
303+
this.keyMaterial = keyMaterial;
304+
}
305+
306+
protected KeyMaterialMerger merge(byte[] nextKeyMaterial) {
307+
keyMaterial = p11Merge(keyMaterial,
308+
new CK_MECHANISM(CKM_CONCATENATE_BASE_AND_DATA,
309+
new CK_KEY_DERIVATION_STRING_DATA(nextKeyMaterial)),
310+
keyMaterial.keyLength + nextKeyMaterial.length);
311+
return this;
312+
}
313+
314+
protected KeyMaterialMerger merge(P11Key.P11SecretKey nextKeyMaterial) {
315+
try {
316+
keyMaterial = p11Merge(keyMaterial,
317+
new CK_MECHANISM(CKM_CONCATENATE_BASE_AND_KEY,
318+
nextKeyMaterial.getKeyID()),
319+
keyMaterial.keyLength + nextKeyMaterial.keyLength);
320+
} finally {
321+
nextKeyMaterial.releaseKeyID();
322+
}
323+
return this;
324+
}
325+
326+
SecretKey getKeyMaterial() {
327+
return keyMaterial;
328+
}
329+
}
330+
331+
private final class DataKeyMaterialMerger extends KeyMaterialMerger {
332+
private byte[] keyMaterial;
333+
334+
DataKeyMaterialMerger(byte[] keyMaterial) {
335+
this.keyMaterial = keyMaterial;
336+
}
337+
338+
protected KeyMaterialMerger merge(byte[] nextKeyMaterial) {
339+
keyMaterial = Arrays.copyOf(keyMaterial,
340+
keyMaterial.length + nextKeyMaterial.length);
341+
System.arraycopy(nextKeyMaterial, 0, keyMaterial,
342+
keyMaterial.length - nextKeyMaterial.length,
343+
nextKeyMaterial.length);
344+
return this;
345+
}
346+
347+
protected KeyMaterialMerger merge(P11Key.P11SecretKey nextKeyMaterial) {
348+
return P11HKDF.this.new KeyKeyMaterialMerger(p11Merge(
349+
nextKeyMaterial, new CK_MECHANISM(
350+
CKM_CONCATENATE_DATA_AND_BASE,
351+
new CK_KEY_DERIVATION_STRING_DATA(keyMaterial)),
352+
keyMaterial.length + nextKeyMaterial.keyLength));
353+
}
354+
355+
SecretKey getKeyMaterial() {
356+
return new SecretKeySpec(keyMaterial, "Generic");
357+
}
358+
}
359+
360+
private SecretKey consolidateKeyMaterial(List<SecretKey> keys) {
361+
KeyMaterialMerger keyMerger = P11HKDF.this.new AnyKeyMaterialMerger();
362+
for (SecretKey key : keys) {
363+
keyMerger = keyMerger.merge(key);
364+
}
365+
return keyMerger.getKeyMaterial();
366+
}
367+
}

0 commit comments

Comments
 (0)