Constructing EC Public Key from EC Point and ECParameterSpec gives invalid x value

always_a_rookie_to_learn :

I've an uncompressed EC Point for prime256v1 curve, and I'm trying to construct a PublicKey object from it using the below code (referred from here), using BouncyCastle provider:

public static void main(String[] args) throws Exception
{
    // Using BC as SunEC (java8) doesn't support prime256v1.
    Security.addProvider(new BouncyCastleProvider());

    // Input values -----------------

    // Complete EC Point (with uncompressed prefix 04 and the length [41])
    // 0441044ef454741576ed945005ea87f114bf8045bcff84155914246aaef43bc35804c9537201ea2387f7edabf76e85b9a7fc341001ddda3272a9685d9aa36ff96526d9

    // EC Point value w/o the tag and length
    final String ecPointHex = "044ef454741576ed945005ea87f114bf8045bcff84155914246aaef43bc35804c9537201ea2387f7edabf76e85b9a7fc341001ddda3272a9685d9aa36ff96526d9";
    // final String ecParamsHex = "06082a8648ce3d030107"; // prime256v1; OID: 1.2.840.10045.3.1.7

    final String curveName = "prime256v1";
    // ------------------------------

    // EC Parameter Spec ------------

    AlgorithmParameters params = AlgorithmParameters.getInstance("EC", "BC");
    params.init(new ECGenParameterSpec(curveName));

    ECParameterSpec ecParameterSpec = params.getParameterSpec(ECParameterSpec.class);

    // ------------------------------

    // EC Point ---------------------

    byte[] ecPointBinary = Hex.decode(ecPointHex);

    byte[] x = Arrays.copyOfRange(ecPointBinary, 0, ecPointBinary.length / 2);
    byte[] y = Arrays.copyOfRange(ecPointBinary, ecPointBinary.length / 2, ecPointBinary.length);

    ECPoint ecPoint = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));

    // ------------------------------

    // Construct Public Key ---------

    KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); // Tried ECDSA as well

    ECPublicKey ecPublicKey = (ECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec)); // <-- exception here

    System.out.println(ecPublicKey);

    // ------------------------------
}

But I get this exception:

Exception in thread "main" java.lang.IllegalArgumentException: x value invalid for SecP256R1FieldElement
    at org.bouncycastle.math.ec.custom.sec.SecP256R1FieldElement.<init>(Unknown Source)
    at org.bouncycastle.math.ec.custom.sec.SecP256R1Curve.fromBigInteger(Unknown Source)
    at org.bouncycastle.math.ec.ECCurve.createPoint(Unknown Source)
    at org.bouncycastle.math.ec.ECCurve.createPoint(Unknown Source)
    at org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertPoint(Unknown Source)
    at org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertPoint(Unknown Source)
    at org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey.<init>(Unknown Source)
    at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePublic(Unknown Source)
    at java.security.KeyFactory.generatePublic(KeyFactory.java:328)
    at com.test.ECStackOverflow.main(ECStackOverflow.java:70)

What am I doing wrong?

P.S: This PublicKey was generated in a SafeNet HSM and the EC Point and Params values are retrieved from the PublicKey object.

Maarten Bodewes :

You're including the uncompressed point indicator 04 in your X value and therefore both your X and Y value have been shifted.

So a direct answer is to skip the indicator. However, just to be sure, I would not use the size of the point as you receive it, but use the size of the curve to copy it out. That way the method will fail before reads points for a lower or higher sized curve.

Of course, in that case you'd also create a check.

int primeSizeBytes = (ecParameterSpec.getCurve().getField().getFieldSize() + Byte.SIZE - 1) / Byte.SIZE;

if (ecPointBinary.length != 1 + 2 * primeSizeBytes) {
    // or think of your own exception
    throw new InvalidKeyException("EC point size invalid");
}

if (ecPointBinary[0] != 04) {
    throw new InvalidKeyException("EC uncompressed point indicator with byte value 04 missing");
}

byte[] x = Arrays.copyOfRange(ecPointBinary, 1, 1 + primeSizeBytes);
byte[] y = Arrays.copyOfRange(ecPointBinary, 1 + primeSizeBytes, 1 + 2 * primeSizeBytes);

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=420956&siteId=1