From 0e9bfe02c90c4264eff4ddfb0be32e2ad4e3704c Mon Sep 17 00:00:00 2001 From: Kostas Tsiounis Date: Thu, 28 Nov 2024 14:19:33 -0500 Subject: [PATCH] Implement calculatePublicKey() in classes that extend PKCS8Key PKCS8Key implements the InternalPrivateKey interface that contains the calculatePublicKey() method. If not implemented, an UnsupportedOperationException is thrown. This functionality is implemented to support this operation instead of getting an exception. Signed-off-by: Kostas Tsiounis --- .../crypto/plus/provider/ECPrivateKey.java | 12 +++++ .../plus/provider/XDHPrivateKeyImpl.java | 18 ++++++-- .../base/BaseTestECKeyImportInterop.java | 40 +++++++++++++++++ .../base/BaseTestECKeyPairGenerator.java | 21 +++++++++ .../junit/base/BaseTestXDHInterop.java | 45 +++++++++++++++++++ .../base/BaseTestXDHKeyPairGenerator.java | 21 +++++++++ 6 files changed, 154 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ibm/crypto/plus/provider/ECPrivateKey.java b/src/main/java/com/ibm/crypto/plus/provider/ECPrivateKey.java index 3b15e4409..f202a9667 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/ECPrivateKey.java +++ b/src/main/java/com/ibm/crypto/plus/provider/ECPrivateKey.java @@ -14,6 +14,8 @@ import java.security.AlgorithmParameters; import java.security.InvalidKeyException; import java.security.Key; +import java.security.ProviderException; +import java.security.PublicKey; import java.security.spec.ECParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.util.Arrays; @@ -578,6 +580,16 @@ ECKey getOCKKey() { return this.ecKey; } + @Override + public PublicKey calculatePublicKey() { + try { + return new ECPublicKey(provider, ecKey); + } catch (InvalidKeyException exc) { + throw new ProviderException( + "Unexpected error calculating public key", exc); + } + } + /** * Parse the key. Called by PKCS8Key. "key" is a byte array containing the * Der-encoded key which resides within the parent class PKCS8Key. The diff --git a/src/main/java/com/ibm/crypto/plus/provider/XDHPrivateKeyImpl.java b/src/main/java/com/ibm/crypto/plus/provider/XDHPrivateKeyImpl.java index 867485f00..644d3eea4 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/XDHPrivateKeyImpl.java +++ b/src/main/java/com/ibm/crypto/plus/provider/XDHPrivateKeyImpl.java @@ -17,6 +17,8 @@ import java.security.InvalidKeyException; import java.security.InvalidParameterException; import java.security.KeyRep; +import java.security.ProviderException; +import java.security.PublicKey; import java.security.interfaces.XECPrivateKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.NamedParameterSpec; @@ -94,8 +96,8 @@ public XDHPrivateKeyImpl(OpenJCEPlusProvider provider, byte[] encoded) try { byte[] alteredEncoded = processEncodedPrivateKey(encoded); // Sets params, key, and algid, and alters encoded // to fit with GSKit and sets params - int encodingSize = CurveUtil.getDEREncodingSize(curve); - this.xecKey = XECKey.createPrivateKey(provider.getOCKContext(), alteredEncoded, encodingSize); + int curveSize = CurveUtil.getCurveSize(curve); + this.xecKey = XECKey.createPrivateKey(provider.getOCKContext(), alteredEncoded, curveSize); this.scalar = Optional.of(k); } catch (Exception exception) { InvalidKeyException ike = new InvalidKeyException("Failed to create XEC private key"); @@ -374,7 +376,7 @@ private boolean isCorrectlyFormedOctetString(byte[] keyBytes) throws IOException return true; } - public XECKey getOCKKey() { + XECKey getOCKKey() { return this.xecKey; } @@ -424,6 +426,16 @@ public String getAlgorithm() { return "XDH"; } + @Override + public PublicKey calculatePublicKey() { + try { + return new XDHPublicKeyImpl(provider, xecKey, curve); + } catch (InvalidKeyException exc) { + throw new ProviderException( + "Unexpected error calculating public key", exc); + } + } + /** * Adds a sequence of FFDHE integers (bi1, bi2, and bi3) to the OutputStream param. * DER added: SEQUENCE[INTEGER,INTEGER,INTEGER] diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyImportInterop.java b/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyImportInterop.java index ac7c2f1cd..ebd9b6ef6 100644 --- a/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyImportInterop.java +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyImportInterop.java @@ -24,12 +24,52 @@ import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import org.junit.jupiter.api.Test; +import sun.security.util.InternalPrivateKey; import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; public class BaseTestECKeyImportInterop extends BaseTestJunit5Interop { static final byte[] origMsg = "this is the original message to be signed".getBytes(); + @Test + public void testCreateKeyPairECGenParamImportCalculatePublic() throws Exception { + doCreateKeyPairECGenParamImportCalculatePublic(getProviderName(), getInteropProviderName()); + doCreateKeyPairECGenParamImportCalculatePublic(getInteropProviderName(), getProviderName()); + } + + private void doCreateKeyPairECGenParamImportCalculatePublic(String generateProviderName, + String importProviderName) throws Exception { + + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC", generateProviderName); + + keyPairGen.initialize(256); + KeyPair keyPair = keyPairGen.generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + PublicKey publicKey = keyPair.getPublic(); + + // Recreate private key from encoding. + byte[] privKeyBytes = privateKey.getEncoded(); + KeyFactory keyFactory = KeyFactory.getInstance("EC", importProviderName); + EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privKeyBytes); + privateKey = keyFactory.generatePrivate(privateKeySpec); + + // Get public key bytes from private. + byte[] calculatedPublicKey = ((InternalPrivateKey) privateKey).calculatePublicKey().getEncoded(); + + // Get public key bytes from original public key. + byte[] publicKeyBytes = publicKey.getEncoded(); + + System.out.println("---- Comparing EC public key from KeyPair vs calculated from private key ----"); + System.out.println("EC public key from Keypair from " + generateProviderName + ": " + + BaseUtils.bytesToHex(publicKeyBytes)); + System.out.println("EC public key from calculatePublicKey() from " + importProviderName + ": " + + BaseUtils.bytesToHex(calculatedPublicKey)); + + // The original and calculated public keys should be the same + assertArrayEquals(calculatedPublicKey, publicKeyBytes); + } + @Test public void testCreateKeyPairECGenParamImport() throws Exception { doCreateKeyPairECGenParamImport(getProviderName(), getInteropProviderName()); diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyPairGenerator.java b/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyPairGenerator.java index 2e89e9476..fd7e0b7d5 100644 --- a/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyPairGenerator.java +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestECKeyPairGenerator.java @@ -11,7 +11,9 @@ import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PrivateKey; import java.security.ProviderException; +import java.security.PublicKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; @@ -20,7 +22,9 @@ import java.security.spec.EllipticCurve; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import sun.security.util.InternalPrivateKey; import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; public class BaseTestECKeyPairGenerator extends BaseTestJunit5 { @@ -101,6 +105,23 @@ public void compareEcParameterSpec(ECParameterSpec ecParameterSpecPub, } + @Test + public void testECPrivateKey_calculatePublicKey() throws Exception { + kpg.initialize(256); + KeyPair kp = kpg.generateKeyPair(); + + PublicKey ecpu = kp.getPublic(); + PrivateKey ecpr = kp.getPrivate(); + + byte[] originalEncoded = ecpu.getEncoded(); + byte[] calculatedEncoded = ((InternalPrivateKey) ecpr).calculatePublicKey().getEncoded(); + + System.out.println("---- Comparing EC public key from KeyPair vs calculated from private key ----"); + System.out.println("EC public key from Keypair: " + BaseUtils.bytesToHex(originalEncoded)); + System.out.println("EC public key from calculatePublicKey(): " + BaseUtils.bytesToHex(calculatedEncoded)); + assertArrayEquals(originalEncoded, calculatedEncoded); + } + @Test public void testECKeyGenCurves_secp192k1() throws Exception { generictestECKeyGenCurve("secp192k1"); diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestXDHInterop.java b/src/test/java/ibm/jceplus/junit/base/BaseTestXDHInterop.java index 90229ac46..946f77468 100644 --- a/src/test/java/ibm/jceplus/junit/base/BaseTestXDHInterop.java +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestXDHInterop.java @@ -11,6 +11,8 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.EncodedKeySpec; import java.security.spec.NamedParameterSpec; @@ -18,6 +20,8 @@ import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import org.junit.jupiter.api.Test; +import sun.security.util.InternalPrivateKey; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; public class BaseTestXDHInterop extends BaseTestJunit5Interop { @@ -29,6 +33,47 @@ public class BaseTestXDHInterop extends BaseTestJunit5Interop { String openJDK_private_X448 = "MEYCAQAwBwYDK2VvBQAEOOJFsgLYxgAIEWuN1FLAGWDzGQRSataAbPLDc1wv5aky4T8hevyWbYdhggc1OCcqQ93gY8rqVTDb"; // OpenJDK does not currently support FFDHE hence interop testing for FFDHE is not possible + @Test + public void testCreateKeyPairXDHGenParamImportCalculatePublic() throws Exception { + if (!"BC".equals(getInteropProviderName())) { + doCreateKeyPairXDHGenParamImportCalculatePublic(getProviderName(), getInteropProviderName()); + doCreateKeyPairXDHGenParamImportCalculatePublic(getInteropProviderName(), getProviderName()); + } + + } + + private void doCreateKeyPairXDHGenParamImportCalculatePublic(String generateProviderName, + String importProviderName) throws Exception { + + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("XDH", generateProviderName); + + keyPairGen.initialize(255); + KeyPair keyPair = keyPairGen.generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + PublicKey publicKey = keyPair.getPublic(); + + // Recreate private key from encoding. + byte[] privKeyBytes = privateKey.getEncoded(); + KeyFactory keyFactory = KeyFactory.getInstance("XDH", importProviderName); + EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privKeyBytes); + privateKey = keyFactory.generatePrivate(privateKeySpec); + + // Get public key bytes from private. + byte[] calculatedPublicKey = ((InternalPrivateKey) privateKey).calculatePublicKey().getEncoded(); + + // Get public key bytes from original public key. + byte[] publicKeyBytes = publicKey.getEncoded(); + + System.out.println("---- Comparing XDH public key from KeyPair vs calculated from private key ----"); + System.out.println("XDH public key from Keypair from " + generateProviderName + ": " + + BaseUtils.bytesToHex(publicKeyBytes)); + System.out.println("XDH public key from calculatePublicKey() from " + importProviderName + ": " + + BaseUtils.bytesToHex(calculatedPublicKey)); + + // The original and calculated public keys should be the same + assertArrayEquals(calculatedPublicKey, publicKeyBytes); + } + @Test public void testXDHInterop_X25519_OpenJDK() throws Exception { byte[] openJDK_public_bytes = Base64.getDecoder().decode(openJDK_public_X25519); diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestXDHKeyPairGenerator.java b/src/test/java/ibm/jceplus/junit/base/BaseTestXDHKeyPairGenerator.java index fa31d50ab..24f3b3e0e 100644 --- a/src/test/java/ibm/jceplus/junit/base/BaseTestXDHKeyPairGenerator.java +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestXDHKeyPairGenerator.java @@ -10,11 +10,15 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; import java.security.interfaces.XECPrivateKey; import java.security.interfaces.XECPublicKey; import java.security.spec.NamedParameterSpec; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import sun.security.util.InternalPrivateKey; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; public class BaseTestXDHKeyPairGenerator extends BaseTestJunit5 { @@ -82,6 +86,23 @@ public void doXECKeyGen(int keypairSize) throws Exception { //System.out.println("ECPrivate: " + ecpr.getS()); } + @Test + public void testXDHPrivateKey_calculatePublicKey() throws Exception { + kpg.initialize(255); + KeyPair kp = kpg.generateKeyPair(); + + PublicKey ecpu = kp.getPublic(); + PrivateKey ecpr = kp.getPrivate(); + + byte[] originalEncoded = ecpu.getEncoded(); + byte[] calculatedEncoded = ((InternalPrivateKey) ecpr).calculatePublicKey().getEncoded(); + + System.out.println("---- Comparing XDH public key from KeyPair vs calculated from private key ----"); + System.out.println("XDH public key from Keypair: " + BaseUtils.bytesToHex(originalEncoded)); + System.out.println("XDH public key from calculatePublicKey(): " + BaseUtils.bytesToHex(calculatedEncoded)); + assertArrayEquals(originalEncoded, calculatedEncoded); + } + @Test public void testXECKeyGenCurves() throws Exception { generictestXECKeyGenCurve("X25519");