Skip to content

Commit c1a04fa

Browse files
authored
Fix the encoding formats for X25519 and X448 (#169)
This commit fixes the XDH Private Key and Public Key encoding formats issues. According to the PKCS#8 Private-Key Specification, the new format privateKey is an octet string whose contents are the value of the private key. So, adding the octet string before the private key when passing the private key object to PKCS8Key key object for 17 and after version. According to Sun old versions, 11 and before, the new XDH format is not supported. So, adding a DER "null" value on the OID sequence only for 11 and before versions. Signed-off-by: Tao Liu <tao.liu@ibm.com>
1 parent 979edaa commit c1a04fa

File tree

6 files changed

+100
-21
lines changed

6 files changed

+100
-21
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public abstract class OpenJCEPlusProvider extends java.security.Provider {
2222

2323
private static final String PROVIDER_VER = System.getProperty("java.specification.version");
2424

25+
private static final String JAVA_VER = System.getProperty("java.specification.version");
26+
2527
// Are we debugging? -- for developers
2628
static final boolean debug2 = false;
2729

@@ -67,6 +69,12 @@ boolean isFIPS() {
6769
return getOCKContext().isFIPS();
6870
}
6971

72+
// Return the Java version.
73+
//
74+
String getJavaVersionStr() {
75+
return JAVA_VER;
76+
}
77+
7078
abstract ProviderException providerException(String message, Throwable ockException);
7179

7280
abstract void setOCKExceptionCause(Exception exception, Throwable ockException);

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

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey, Seriali
4343
private transient Optional<byte[]> scalar;
4444
private transient NamedParameterSpec params;
4545
private CURVE curve;
46+
private byte[] k; // The raw key bytes, without OctetString or DER encoded
4647
BigInteger bi1; // parameter used in FFDHE
4748
BigInteger bi2; // parameter used in FFDHE
4849
BigInteger bi3; // parameter used in FFDHE
@@ -55,9 +56,10 @@ final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey, Seriali
5556
private transient XECKey xecKey = null;
5657

5758
private void setFieldsFromXeckey() throws Exception {
58-
if (this.key == null) {
59-
this.key = extractPrivateKeyFromOCK(xecKey.getPrivateKeyBytes()); // Extract key from GSKit and sets params
60-
this.scalar = Optional.of(key);
59+
if (k == null) {
60+
k = extractPrivateKeyFromOCK(xecKey.getPrivateKeyBytes()); // Extract key from GSKit and sets params
61+
setPKCS8KeyByte(k);
62+
this.scalar = Optional.of(k);
6163
this.algid = CurveUtil.getAlgId(this.params.getName());
6264
}
6365
}
@@ -97,7 +99,7 @@ public XDHPrivateKeyImpl(OpenJCEPlusProvider provider, byte[] encoded)
9799
// to fit with GSKit and sets params
98100
int encodingSize = CurveUtil.getDEREncodingSize(curve);
99101
this.xecKey = XECKey.createPrivateKey(provider.getOCKContext(), alteredEncoded, encodingSize);
100-
this.scalar = Optional.of(this.key);
102+
this.scalar = Optional.of(k);
101103
} catch (Exception exception) {
102104
InvalidKeyException ike = new InvalidKeyException("Failed to create XEC private key");
103105
provider.setOCKExceptionCause(ike, exception);
@@ -138,9 +140,9 @@ public XDHPrivateKeyImpl(OpenJCEPlusProvider provider, AlgorithmParameterSpec pa
138140
this.provider = provider;
139141
this.scalar = scalar;
140142
if (scalar != null)
141-
this.key = scalar.get();
143+
k = scalar.get();
142144
try {
143-
if (this.key == null) {
145+
if (k == null) {
144146
int keySize = CurveUtil.getCurveSize(curve);
145147
this.xecKey = XECKey.generateKeyPair(provider.getOCKContext(), this.curve.ordinal(), keySize);
146148
} else {
@@ -149,6 +151,7 @@ public XDHPrivateKeyImpl(OpenJCEPlusProvider provider, AlgorithmParameterSpec pa
149151
int encodingSize = CurveUtil.getDEREncodingSize(curve);
150152
this.xecKey = XECKey.createPrivateKey(provider.getOCKContext(), der, encodingSize);
151153
}
154+
setPKCS8KeyByte(k);
152155
} catch (Exception exception) {
153156
InvalidParameterException ike = new InvalidParameterException(
154157
"Failed to create XEC private key");
@@ -178,7 +181,7 @@ private byte[] buildOCKPrivateKeyBytes() throws IOException {
178181

179182
// Adding Key
180183
DerOutputStream keyOctetString = new DerOutputStream();
181-
keyOctetString.putOctetString(key);
184+
keyOctetString.putOctetString(k);
182185
mainSeq.putOctetString(keyOctetString.toByteArray());
183186

184187
// Wrapping up in a sequence
@@ -316,20 +319,21 @@ private byte[] processEncodedPrivateKey(byte[] encoded) throws IOException {
316319
// XDH private key in SunEC new Java 17 design requires [octet-string[octet-string[key-bytes]]] format,
317320
// otherwise, it causes interop issue.
318321
if (isCorrectlyFormedOctetString(keyBytes)) {
319-
this.key = derStream.getOctetString(); // We know we are working with the format [octet-string[octet-string[key-bytes]]]
322+
k = derStream.getOctetString(); // We know we are working with the format [octet-string[octet-string[key-bytes]]]
320323
} else {
321-
this.key = keyBytes; // Try J11 format [octet-string[key-bytes]]
324+
k = keyBytes; // Try J11 format [octet-string[key-bytes]]
322325
}
323326
} catch (Exception e) {
324327
//e.printStackTrace();
325-
this.key = keyBytes; // Try J11 format [octet-string[key-bytes]]
328+
k = keyBytes; // Try J11 format [octet-string[key-bytes]]
326329
}
330+
setPKCS8KeyByte(k);
327331
try (DerOutputStream encodedKey = new DerOutputStream()) {
328332
if (CurveUtil.isFFDHE(this.curve)) {
329-
BigInteger octetStringAsBigInt = new BigInteger(this.key);
333+
BigInteger octetStringAsBigInt = new BigInteger(k);
330334
encodedKey.putInteger(octetStringAsBigInt); // Put in another octet string
331335
} else {
332-
encodedKey.putOctetString(this.key); // Put in another octet string
336+
encodedKey.putOctetString(k); // Put in another octet string
333337
}
334338
outStream.putOctetString(encodedKey.toByteArray());
335339
}
@@ -399,7 +403,7 @@ public byte[] getKeyBytes() {
399403
} catch (Exception exception) {
400404
this.exception = exception;
401405
}
402-
return this.key.clone();
406+
return k.clone();
403407
}
404408

405409
@Override
@@ -491,10 +495,10 @@ public void encode(OutputStream os) throws IOException {
491495
bytes.write(oidTmp.toByteArray());
492496

493497
// encode encrypted key
494-
if (this.key != null) {
498+
if (k != null) {
495499
// XDH private key in SunEC and new Java 17 design requires [octet-string[octer-string[key-bytes]]] format,
496500
// otherwise, it causes interop issue. JCK issue 569
497-
bytes.putOctetString(new DerValue(DerValue.tag_OctetString, this.key).toByteArray());
501+
bytes.putOctetString(new DerValue(DerValue.tag_OctetString, k).toByteArray());
498502
}
499503

500504
// wrap everything into a SEQUENCE
@@ -521,6 +525,8 @@ public void destroy() throws DestroyFailedException {
521525
}
522526
if (!destroyed) {
523527
destroyed = true;
528+
if (k != null)
529+
Arrays.fill(k, (byte) 0x00);
524530
if (this.key != null)
525531
Arrays.fill(this.key, (byte) 0x00);
526532
this.xecKey = null;
@@ -545,4 +551,18 @@ private void checkDestroyed() {
545551
protected Object writeReplace() throws java.io.ObjectStreamException {
546552
return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), getEncoded());
547553
}
554+
555+
/**
556+
* Set the PKCS8Key key object.
557+
*
558+
* @param k The raw key bytes, without OctetString or DER encoded.
559+
* @throws IOException
560+
*/
561+
private void setPKCS8KeyByte(byte[] k) throws IOException {
562+
if (Integer.parseInt(provider.getJavaVersionStr()) <= 11) {
563+
this.key = k;
564+
} else {
565+
this.key = new DerValue(DerValue.tag_OctetString, k).toByteArray();
566+
}
567+
}
548568
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ public byte[] getEncoded() {
411411
oidSeq.putOID(this.algid.getOID());
412412
if ((oidSubSeq != null)) {
413413
oidSeq.write(DerValue.tag_Sequence, oidSubSeq);
414-
} else {
414+
} else if (Integer.parseInt(provider.getJavaVersionStr()) <= 11) {
415415
// Encode as old J8 format
416416
// Sun old versions, 11 and before, are not supporting new XDH format,
417417
// otherwise, it causes interop issue -> Ex. J11#1834

src/test/java/ibm/jceplus/junit/base/BaseTestXDHKeyImport.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public void createKeyPairXDHParamImport(String alg) throws Exception {
185185
}
186186

187187
NamedParameterSpec paramSpec = new NamedParameterSpec(alg);
188-
KeyFactory kf = KeyFactory.getInstance("XDH");
188+
KeyFactory kf = KeyFactory.getInstance("XDH", providerName);
189189

190190
XECPublicKeySpec xdhPublic = new XECPublicKeySpec(paramSpec, u);
191191
XECPrivateKeySpec xdhPrivate = new XECPrivateKeySpec(paramSpec, scalar);
@@ -206,7 +206,7 @@ public void createKeyPairXDHParamImport(String alg) throws Exception {
206206
* @throws Exception
207207
*/
208208
void createKeyPairLocalParamImport(String alg) throws Exception {
209-
KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg);
209+
KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg, providerName);
210210
// KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
211211
NamedParameterSpec paramSpec = new NamedParameterSpec(alg);
212212
System.out.println("Alg = " + alg);
@@ -216,7 +216,7 @@ void createKeyPairLocalParamImport(String alg) throws Exception {
216216
PrivateKey pvk = kp.getPrivate();
217217
PublicKey pbk = kp.getPublic();
218218

219-
KeyFactory kf = KeyFactory.getInstance(alg);
219+
KeyFactory kf = KeyFactory.getInstance(alg, providerName);
220220
// KeyFactory kf = KeyFactory.getInstance("XDH");
221221
XECPublicKeySpec xdhPublic = kf.getKeySpec(kp.getPublic(), XECPublicKeySpec.class);
222222
XECPrivateKeySpec xdhPrivate = kf.getKeySpec(kp.getPrivate(), XECPrivateKeySpec.class);
@@ -232,7 +232,7 @@ void createKeyPairLocalParamImport(String alg) throws Exception {
232232
PublicKey pbk2 = kf.generatePublic(xdhPublic);
233233
PrivateKey pvk2 = kf.generatePrivate(xdhPrivate);
234234

235-
System.out.println(" pbk =" + BaseUtils.bytesToHex(pbk2.getEncoded()));
235+
System.out.println(" pbk = " + BaseUtils.bytesToHex(pbk2.getEncoded()));
236236
System.out.println(" pbk2 = " + BaseUtils.bytesToHex(pbk.getEncoded()));
237237

238238
// Verify that keys match

src/test/java/ibm/jceplus/junit/openjceplus/TestAll.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
TestSHA512_224.class, TestSHA512_256.class, TestSHA3_224.class, TestSHA3_256.class,
4848
TestSHA3_384.class, TestSHA3_512.class, TestRSAKeyInterop.class, TestRSAKeyInteropBC.class,
4949
TestEdDSASignature.class, TestEdDSASignatureInterop.class, TestXDH.class,
50-
TestXDHInterop.class, TestXDHMultiParty.class, TestXDHKeyPairGenerator.class,
50+
TestXDHInterop.class, TestXDHInteropBC.class, TestXDHMultiParty.class, TestXDHKeyPairGenerator.class,
5151
TestXDHKeyImport.class, TestIsAssignableFromOrder.class
5252

5353
})
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright IBM Corp. 2023
3+
*
4+
* Licensed under the Apache License 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. You can obtain a copy
6+
* in the file LICENSE in the source distribution.
7+
*/
8+
9+
package ibm.jceplus.junit.openjceplus;
10+
11+
import junit.framework.Test;
12+
import junit.framework.TestSuite;
13+
14+
public class TestXDHInteropBC extends ibm.jceplus.junit.base.BaseTestXDHInterop {
15+
16+
// --------------------------------------------------------------------------
17+
//
18+
//
19+
static {
20+
Utils.loadProviderTestSuite();
21+
22+
try {
23+
Utils.loadProviderBC();
24+
} catch (Exception e) {
25+
e.printStackTrace(System.out);
26+
System.exit(1);
27+
}
28+
}
29+
30+
// --------------------------------------------------------------------------
31+
//
32+
//
33+
public TestXDHInteropBC() {
34+
super(Utils.TEST_SUITE_PROVIDER_NAME, Utils.PROVIDER_BC);
35+
}
36+
37+
// --------------------------------------------------------------------------
38+
//
39+
//
40+
public static void main(String[] args) throws Exception {
41+
junit.textui.TestRunner.run(suite());
42+
}
43+
44+
// --------------------------------------------------------------------------
45+
//
46+
//
47+
public static Test suite() {
48+
TestSuite suite = new TestSuite(TestXDHInteropBC.class);
49+
return suite;
50+
}
51+
}

0 commit comments

Comments
 (0)