从 EC Point 和 ECParameterSpec 构造 EC Public Key 给出无效的 x 值

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

我有一个 prime256v1 曲线的未压缩 EC 点,我正在尝试使用以下代码(从 引用)从中构造一个 PublicKey 对象,使用 BouncyCastle 提供程序:

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);

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

但我得到这个例外:

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.ECWhosebug.main(ECWhosebug.java:70)

我做错了什么?

P.S:此 PublicKey 是在 SafeNet HSM 中生成的,EC Point 和 Params 值是从 PublicKey 对象中检索的。

您在 X 值中包含未压缩点指示器 04,因此您的 X 和 Y 值都已移动。

所以直接的答案是跳过指标。但是,可以肯定的是,我不会使用您收到的点的大小,而是使用曲线的大小将其复制出来。这样,该方法将在读取更小或更大尺寸曲线的点之前失败。

当然,在这种情况下,您还需要创建一张支票。

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);