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

Java客户端ECDSA签名后,Node.js服务端远程提取(r,s)并验证签名

Java ECDSA签名与Node.js服务端验证实现方案

我来帮你搞定Java客户端签名、Node.js服务端验证的全流程,咱们一步步拆解:

一、Java端签名代码补全

你给出的签名逻辑已经是正确的,我帮你补全完整可运行的代码,避免编码和参数遗漏:

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;

public class ECDSASigner {
    // 明确指定UTF-8编码,避免环境默认编码差异
    private static final String UTF_8 = StandardCharsets.UTF_8.name();

    public static byte[] sign(String plainText, PrivateKey privateKey) throws Exception {
        // 实例化SHA1withECDSA签名对象
        Signature ecdsaSigner = Signature.getInstance("SHA1withECDSA");
        // 用私钥初始化签名器
        ecdsaSigner.initSign(privateKey);
        // 更新要签名的明文(统一UTF-8编码)
        ecdsaSigner.update(plainText.getBytes(UTF_8));
        // 生成ASN.1 DER格式的签名字节数组
        return ecdsaSigner.sign();
    }

    // 可选:把签名转成Base64方便网络传输
    public static String signToBase64(String plainText, PrivateKey privateKey) throws Exception {
        byte[] signatureBytes = sign(plainText, privateKey);
        return Base64.getEncoder().encodeToString(signatureBytes);
    }
}

二、Node.js端签名验证实现

服务端拿到公钥的x、y坐标后,需要先构造合法的EC公钥,再验证Java生成的签名。这里最关键的是曲线一致性(Java私钥的曲线必须和Node.js用的曲线匹配),具体代码如下:

const crypto = require('crypto');

/**
 * 验证ECDSA签名
 * @param {string} plainText - 签名对应的明文
 * @param {Buffer} signatureBuffer - Java生成的签名字节数组(或Base64解码后的Buffer)
 * @param {string} xHex - 公钥x坐标的十六进制字符串(无0x前缀)
 * @param {string} yHex - 公钥y坐标的十六进制字符串(无0x前缀)
 * @param {string} curveName - EC曲线名称,默认secp256r1(NIST P-256)
 * @returns {boolean} 验证结果:true=有效,false=无效
 */
function verifyECDSASignature(plainText, signatureBuffer, xHex, yHex, curveName = 'secp256r1') {
    try {
        // 1. 用x、y坐标构造ECDH对象,生成标准SPKI格式的公钥
        const ecDH = crypto.createECDH(curveName);
        ecDH.setPublicKey(Buffer.from(xHex, 'hex'), Buffer.from(yHex, 'hex'));
        const publicKey = ecDH.getPublicKey('spki');

        // 2. 创建验证器,算法要和Java端对应:Java的SHA1withECDSA对应Node.js的'ecdsa-with-SHA1'
        const verifier = crypto.createVerify('ecdsa-with-SHA1');
        // 3. 更新明文(和Java端一致用UTF-8编码)
        verifier.update(plainText, 'utf8');
        // 4. 验证签名:Java的签名是ASN.1 DER格式,Node.js的verify可以直接识别
        return verifier.verify(publicKey, signatureBuffer);
    } catch (error) {
        console.error('签名验证出错:', error);
        return false;
    }
}

// 示例用法
const plainText = "需要签名的明文内容";
// 假设从Java端拿到的Base64签名
const signatureBase64 = "Java生成的签名Base64字符串";
const signatureBuffer = Buffer.from(signatureBase64, 'base64');
// 公钥的x、y十六进制坐标
const publicKeyX = "公钥x的十六进制字符串";
const publicKeyY = "公钥y的十六进制字符串";

// 执行验证
const isValid = verifyECDSASignature(plainText, signatureBuffer, publicKeyX, publicKeyY);
console.log('签名是否有效:', isValid);

三、踩坑必看的关键注意事项

  • 曲线必须严格匹配:Java端私钥使用的EC曲线要和Node.js的curveName参数完全一致。你可以在Java端打印曲线名称确认:
    import java.security.interfaces.ECPrivateKey;
    import java.security.spec.ECParameterSpec;
    
    ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey;
    ECParameterSpec params = ecPrivateKey.getParams();
    System.out.println("当前EC曲线名称:" + params.getCurve().getName());
    
    常见曲线名称:secp256r1、secp384r1、secp256k1等。
  • 编码绝对统一:明文必须全程用UTF-8编码,Java端的getBytes(UTF_8)对应Node.js的'utf8'参数。
  • 公钥坐标格式正确:x、y坐标必须是无0x前缀的十六进制字符串,长度要符合曲线要求(比如secp256r1的x、y都是32字节,对应64位十六进制字符)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:01:53