-
Notifications
You must be signed in to change notification settings - Fork 20
Open
Description
Description
There is a discrepancy in the byte array returned by getEncoded() when an Elliptic Curve (EC) private key is reconstructed using KeyFactory. While the mathematical private scalar () remains identical, the DER-encoded PKCS#8 structure changes in length and content.
That is causing the JTREG java/security/KeyAgreement/KeySpecTest.java failure.
Steps to Reproduce
- Generate an EC Private Key using
KeyPairGenerator. - Extract the
ECPrivateKeySpecfrom the generated key. - Reconstruct a new
PrivateKeyobject usingKeyFactory.generatePrivate(spec). - Compare
original.getEncoded()andreconstructed.getEncoded().
Observed Behavior
Arrays.equals() returns false.
- Original Key: Often a "minimal" PKCS#8 structure (approx. 67 bytes).
- Reconstructed Key: Often a "full" PKCS#8 structure (approx. 150 bytes) containing optional attributes like the Public Key bits.
Expected Behavior
While the binary encodings differ due to ASN.1 optional fields, the keys should be recognized as logically/cryptographically equivalent.
Technical Analysis
The OpenJCEPlus (or SunEC) provider expands the encoded format during reconstruction.
- Key 1 (Bare):
VERSION + ALGO_ID + OCTET_STRING(S) - Key 2 (Full):
VERSION + ALGO_ID + OCTET_STRING(S) + [0]PARAMS + [1]PUBLIC_KEY
The test to reproduce:
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.KeySpec;
import java.util.HexFormat;
public class ECDiffTest {
public static void main(String[] args) throws Exception {
// Use your specific provider
String provider = "OpenJCEPlus";
// 1. Generate an initial EC Private Key (e.g., NIST P-256)
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);
kpg.initialize(256);
PrivateKey originalKey = kpg.generateKeyPair().getPrivate();
byte[] originalEncoded = originalKey.getEncoded();
// 2. Build a new key using the KeySpec of the first one
// This process often triggers the Provider to add optional metadata (like the Public Key)
KeyFactory kf = KeyFactory.getInstance("EC", provider);
KeySpec priSpec = kf.getKeySpec(originalKey, ECPrivateKeySpec.class);
PrivateKey reconstructedKey = kf.generatePrivate(priSpec);
byte[] reconstructedEncoded = reconstructedKey.getEncoded();
// 3. Comparison Output
System.out.println("=== Comparison Results ===");
System.out.println("Original Length: " + originalEncoded.length + " bytes");
System.out.println("Reconstructed Length: " + reconstructedEncoded.length + " bytes");
// Binary check
boolean isBinaryEqual = java.util.Arrays.equals(originalEncoded, reconstructedEncoded);
System.out.println("Are encoded bytes identical? " + (isBinaryEqual ? "YES" : "NO"));
// Mathematical check
boolean isMathematicallyEqual = areKeysEqual(originalKey, reconstructedKey);
System.out.println("Are the keys mathematically identical? " + (isMathematicallyEqual ? "YES" : "NO"));
if (!isBinaryEqual) {
System.out.println("\n--- Hex Comparison ---");
System.out.println("Original Hex:\n" + HexFormat.of().formatHex(originalEncoded));
System.out.println("\nReconstructed Hex:\n" + HexFormat.of().formatHex(reconstructedEncoded));
System.out.println("\nNote: The second key is longer because it likely includes the Public Key bits and Curve OID metadata.");
}
}
/**
* Compares two PrivateKeys by looking at the Private Scalar (S)
* and the Curve Parameters rather than the encoded bytes.
*/
public static boolean areKeysEqual(PrivateKey k1, PrivateKey k2) {
if (k1 == k2) return true;
if (!(k1 instanceof ECPrivateKey) || !(k2 instanceof ECPrivateKey)) return false;
ECPrivateKey ec1 = (ECPrivateKey) k1;
ECPrivateKey ec2 = (ECPrivateKey) k2;
// 1. Compare the secret scalar (S)
if (!ec1.getS().equals(ec2.getS())) {
return false;
}
// 2. Compare the Curve Parameters
ECParameterSpec s1 = ec1.getParams();
ECParameterSpec s2 = ec2.getParams();
return s1.getCurve().equals(s2.getCurve()) &&
s1.getGenerator().equals(s2.getGenerator()) &&
s1.getOrder().equals(s2.getOrder()) &&
s1.getCofactor() == s2.getCofactor();
}
}The failing JTREG
java/security/KeyAgreement/KeySpecTest.java
The root cause PR
Suggested fix
Revert the EC private key equals method to check getS() and params (the same as the above test).
Metadata
Metadata
Assignees
Labels
No labels