You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不使用BouncyCastle的原生Java >=11中推导ECDSA公钥

纯Java 11+ 从ECDSA私钥推导公钥的解决方案

很多现有方案依赖BouncyCastle库实现ECDSA私钥到公钥的推导,但如果需要纯Java 11+的原生实现,ChatGPT给出的代码会遇到问题——java.security.spec.ECPoint类并没有提供multiply()方法,无法直接计算生成点与私钥值的点乘。

以下是基于Java标准API的可靠实现,完全不依赖第三方库:

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.interfaces.ECPrivateKey;
import java.math.BigInteger;

public class ECDSAPublicKeyDerivation {
    public static PublicKey derivePublicKey(PrivateKey privateKey) throws Exception {
        // 将私钥强制转换为ECDSA私钥类型
        ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey;
        BigInteger privateS = ecPrivateKey.getS();
        ECParameterSpec ecSpec = ecPrivateKey.getParams();
        ECPoint generator = ecSpec.getGenerator();

        // 计算公钥点:Q = G * s(生成点乘以私钥值)
        ECPoint publicPoint = multiplyPoint(ecSpec, generator, privateS);

        // 构建公钥规格并生成公钥实例
        ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(publicPoint, ecSpec);
        KeyFactory keyFactory = KeyFactory.getInstance("EC");
        return keyFactory.generatePublic(pubKeySpec);
    }

    // 用快速幂算法实现椭圆曲线点乘
    private static ECPoint multiplyPoint(ECParameterSpec ecSpec, ECPoint point, BigInteger scalar) {
        // 初始化结果为无穷远点(用(0,0)表示)
        ECPoint result = new ECPoint(BigInteger.ZERO, BigInteger.ZERO);
        ECPoint current = point;
        BigInteger remaining = scalar;

        while (remaining.compareTo(BigInteger.ZERO) > 0) {
            if (remaining.testBit(0)) {
                result = addPoints(ecSpec, result, current);
            }
            current = addPoints(ecSpec, current, current); // 点加倍操作
            remaining = remaining.shiftRight(1);
        }
        return result;
    }

    // 实现椭圆曲线点加法逻辑(含点加倍特殊情况)
    private static ECPoint addPoints(ECParameterSpec ecSpec, ECPoint p1, ECPoint p2) {
        // 处理无穷远点的特殊情况
        if (p1.getAffineX().equals(BigInteger.ZERO) && p1.getAffineY().equals(BigInteger.ZERO)) {
            return p2;
        }
        if (p2.getAffineX().equals(BigInteger.ZERO) && p2.getAffineY().equals(BigInteger.ZERO)) {
            return p1;
        }

        EllipticCurve curve = ecSpec.getCurve();
        BigInteger p = ((ECFieldFp) curve.getField()).getP();
        BigInteger a = curve.getA();

        BigInteger x1 = p1.getAffineX();
        BigInteger y1 = p1.getAffineY();
        BigInteger x2 = p2.getAffineX();
        BigInteger y2 = p2.getAffineY();

        BigInteger lambda;
        // 点加倍(两点坐标相同)
        if (x1.equals(x2) && y1.equals(y2)) {
            lambda = x1.pow(2).multiply(BigInteger.valueOf(3)).add(a)
                    .multiply(y1.multiply(BigInteger.valueOf(2)).modInverse(p))
                    .mod(p);
        }
        // 普通点加法
        else {
            lambda = y2.subtract(y1)
                    .multiply(x2.subtract(x1).modInverse(p))
                    .mod(p);
        }

        BigInteger x3 = lambda.pow(2).subtract(x1).subtract(x2).mod(p);
        BigInteger y3 = lambda.multiply(x1.subtract(x3)).subtract(y1).mod(p);
        return new ECPoint(x3, y3);
    }
}

代码说明

  1. 核心流程:从ECDSA私钥中提取私钥值s和椭圆曲线参数,通过点乘计算公钥点Q,最后用KeyFactory生成公钥实例。
  2. 点乘实现:采用快速幂算法优化点乘效率,避免直接循环累加带来的性能损耗。
  3. 点加法逻辑:严格遵循椭圆曲线有限域运算规则,处理了无穷远点、点加倍等特殊场景。

注意事项

  • 该实现仅支持**素数域(ECFieldFp)**的椭圆曲线,这是ECDSA常用的曲线类型(如secp256r1、secp384r1等)。
  • 所有运算均基于模p执行,符合椭圆曲线的数学规则。

内容的提问来源于stack exchange,提问作者Hank

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 07:04:53