在给定x、y坐标和曲线名称的椭圆曲线对象上,验证ECPoint是否有效。

3
给定一个公钥的x和y坐标以及曲线名称,我需要确定这些坐标是否表示曲线上的有效点。如果是,则测试通过。如果不是,则测试失败。
到目前为止,我的代码是:
                    String curve = (String) testGroupHeaders.get("curve");
                    String curve_num = curve.split("-")[1];
                    String specName = "secp" + curve_num + "r1";
                        String qx = (String) testsData.get("qx");
                        String qy = (String) testsData.get("qy");
                        BigInteger x = new BigInteger(qx, 16);
                        BigInteger y = new BigInteger(qy, 16);
                        ECPoint ecPoint = new ECPoint(x, y);
                        if (ecPoint.equals(ECPoint.POINT_INFINITY)) {
                            testResultsObject.put("testPassed", false);
                        } else {
                            try {
                                AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
                                parameters.init(new ECGenParameterSpec(specName));
                                ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
                                EllipticCurve ellCurve = ecParameters.getCurve();
                                ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, ecParameters);
                                KeyFactory keyFactory = KeyFactory.getInstance("EC");
                                ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(keySpec);
                                testResultsObject.put("testPassed", true);
                            } catch (Exception e) {
                                System.out.println(e);
                                testResultsObject.put("testPassed", false);
                            }

我的希望是如果点无效,则公钥的构建将失败,但是当输入已知的无效点时,此代码仍会导致测试通过。

如有任何帮助,将不胜感激。

我发现了这个问题如何确定EC点是否在曲线上?,其中有一个模糊的答案:

“可能还有更直接的方法,但如果您拥有EllipticCurve对象,您总是可以将点的坐标代入曲线方程中。”

这可能是一个潜在的解决方案,但我不确定如何实现它。我尝试过:

EllipticCurve ellC = publicKey.getParams().getCurve();


希望它无法构建曲线,但遗憾的是它仍然成功了。
请注意,BouncyCastle 对我来说不是一个选项,我必须使用 java.security 提供程序。
我还发现了这个答案 椭圆曲线点,但我也没有从中得到任何信息。
编辑:好的,我在适应上面最后一个链接答案中的 Bouncy Castle 代码后,非常接近解决问题了。

                                EllipticCurve ellCurve = ecParameters.getCurve();
                                BigInteger a = ellCurve.getA();
                                BigInteger b = ellCurve.getB();
                                ECField ecField = ellCurve.getField();
                                ECFieldFp fp = (ECFieldFp) ecField;
                                BigInteger p = ((ECFieldFp) ecField).getP();
                                BigInteger rhs = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
                                BigInteger lhs = y.multiply(y).mod(p);
                                System.out.println(rhs);
                                System.out.println(lhs);

这在大多数情况下都有效,但对于使用secp521r1的特定情况除外:

          {
            "tcId": 8,
            "qx": "02ED3613D77D9096DC0545F3EEC44270762BF52E63044D29FC880556114F4FC176DBE20A9BC717C58FA63BB3308D3136355A072704C0B8BE9A6B3CFFFE36467722C0",
            "qy": "00E857AE0D11FB1B79AC9531980A3E3BD1AC9E457E39A107D0AF5C9E09F2D6243F31C697C74CFD6A80F1CA5FCDA5950754DCC8724B9B21ED3A6705EBA1B9D2926B8F"
          },

当我应用上述代码并将lhs与rhs进行比较时,我得到了相同的输出,这表明这是曲线上的一个有效点。
rhs: 1634177850809752323211745106139613474061093186782816308703279366414782386677365518237753355552098931968864191666289807321598625802278923850729659158219971712
lhs: 1634177850809752323211745106139613474061093186782816308703279366414782386677365518237753355552098931968864191666289807321598625802278923850729659158219971712

那么我肯定错过了某个检查步骤,这说明了它是失败的。

最后发布的链接中被接受的答案实际上提供了答案。基本上,你所要做的就是使用上面的代码片段,并将曲线、x和y坐标的值分配给前三个变量。那有什么问题吗?最好发表你的尝试。因为链接已经提供了答案,所以你的问题实际上是一个重复的问题。 - Topaco
那段代码片段正在使用bouncycastle api,我认为我无法通过java.security访问ECFieldElement。 - factor2
啊,好的,我没注意到你不想应用BC。 - Topaco
参考我的上面的编辑,已经接近上一个答案了。 - factor2
你编辑的测试用例确实在曲线secp521r1,也称为P-521/nistp521上,因此你的代码现在对于Weierstrass(或X9)素域曲线是正确的,但不适用于其他曲线。(虽然让计算机执行比较lhs.equals(rhs)会更容易,而不是手动执行。) - dave_thompson_085
显示剩余2条评论
1个回答

2

找到了答案,这里发布出来给自己以后或者其他遇到同样问题的人。除了主贴中提到的修改外,我还缺少了一个检查,即仿射 x 和 y 是否在 [0,p-1] 范围内。参考:https://neilmadden.blog/2017/05/17/so-how-do-you-validate-nist-ecdh-public-keys/

以下是没有使用 bouncy castle 的完整工作代码:

                    String curve = (String) testGroupHeaders.get("curve");
                    String curve_num = curve.split("-")[1];
                    String specName = "secp" + curve_num + "r1";
                    JSONArray tests = (JSONArray) testGroupHeaders.get("tests");
                    JSONArray testResultsArray = new JSONArray();
                    for (int k = 0; k < tests.size(); k++){
                        JSONObject testResultsObject = new JSONObject();
                        JSONObject testsData = (JSONObject) tests.get(k);
                        long tcId = (long) testsData.get("tcId");
                        testResultsObject.put("tcId", tcId);
                        String qx = (String) testsData.get("qx");
                        String qy = (String) testsData.get("qy");
                        BigInteger x = new BigInteger(qx, 16);
                        BigInteger y = new BigInteger(qy, 16);
                        ECPoint ecPoint = new ECPoint(x, y);
                        if (ecPoint.equals(ECPoint.POINT_INFINITY)) {
                            testResultsObject.put("testPassed", false);
                        } else {
                            try {
                                AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
                                parameters.init(new ECGenParameterSpec(specName));
                                ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
                                EllipticCurve ellCurve = ecParameters.getCurve();
                                BigInteger a = ellCurve.getA();
                                BigInteger b = ellCurve.getB();
                                ECField ecField = ellCurve.getField();
                                BigInteger p = ((ECFieldFp) ecField).getP();
                                BigInteger rhs = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
                                BigInteger lhs = y.multiply(y).mod(p);

                                // Do this part to try and generate an exception
                                ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, ecParameters);
                                KeyFactory keyFactory = KeyFactory.getInstance("EC");
                                ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(keySpec);

                                BigInteger affineX = publicKey.getW().getAffineX();
                                BigInteger affineY = publicKey.getW().getAffineY();
                                if (affineX.compareTo(BigInteger.ZERO) < 0 || affineX.compareTo(p) >= 0
                                        || affineY.compareTo(BigInteger.ZERO) < 0 || affineY.compareTo(p) >= 0) {
                                    testResultsObject.put("testPassed", false);
                                } else {
                                    if (rhs.equals(lhs)) {
                                        testResultsObject.put("testPassed", true);
                                    } else {
                                        testResultsObject.put("testPassed", false);
                                    }
                                }
                            } catch (Exception e) {
                                testResultsObject.put("testPassed", false);
                            }

请注意,这只是POC代码,可以进行大量的清理/优化。 所有的.get调用都来自我解析的JSON请求,因此获取字符串值/输入的方式可能会有所不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接