【问题标题】:Extracting (r,s) and Verifying ECDSA signature remotely远程提取 (r,s) 和验证 ECDSA 签名
【发布时间】:2018-04-23 06:09:47
【问题描述】:

我正在尝试使用 java 客户端对内容进行签名,然后在服务器 (nodejs) 上进行验证。我的客户端签名函数使用 ECDSA 并返回 byte[]。我可以访问服务器上包含publicKeyxy 坐标值。

public static byte[] sign(String plainText, PrivateKey privateKey) throws Exception {
    java.security.Signature dsa = java.security.Signature.getInstance("SHA1withECDSA");
    dsa.initSign(privateKey);
    dsa.update(plainText.getBytes(UTF_8));
    return dsa.sign();
}

是否可以找到构成签名的 rs 值?如何将从上面获得的byte[] 转换为(r,s) 对或DER-encoded signature 作为十六进制?在node 服务器端,我使用elliptic 进行签名验证。

编辑

感谢 Dave 的 cmets,我正在使用 methods indicated in this SO answer

public static BigInteger extractR(byte[] signature) throws Exception {
    int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
    int lengthR = signature[startR + 1];
    return new BigInteger(Arrays.copyOfRange(signature, startR + 2, startR + 2 + lengthR));
}

public static BigInteger extractS(byte[] signature) throws Exception {
    int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
    int lengthR = signature[startR + 1];
    int startS = startR + 2 + lengthR;
    int lengthS = signature[startS + 1];
    return new BigInteger(Arrays.copyOfRange(signature, startS + 2, startS + 2 + lengthS));
}

知道xyrs 值后,我正在尝试在节点服务器上验证消息this is a test string

Message : this is a test string
Curve Parameters: secp256k1
Public Key:
   X : 52552626316292256179275635993655485173638967401615704770864787021340356427096
   Y : 115577290317206876914379725139810202736866562857077399175416156471449711434272
Signature details:
   R : [0, -63, -80, -50, -87, -56, 93, 19, 82, 46, 51, 14, -75, 103, 115, 126, 21, 94, 43, 102, -21, -86, -29, -5, 25, 14, -6, -116, 120, -54, -66, 2, -78]
   S : [0, -40, -119, 77, -14, 113, -105, -117, 93, 70, -107, -3, 63, 12, 77, -48, 59, -47, -7, -126, -60, -109, 95, -6, -66, -120, -8, -103, 122, 40, 24, -31, 89]

为了使用elliptic 模块进行验证,我有以下内容

var EC_Instance = new EC();
var signature = {
    r : new Buffer([0, -63, ..., 2, -78]),
    s : new Buffer([0, -40, ..., -31, 89])
};
var x = "52552626316292256179275635993655485173638967401615704770864787021340356427096";
var y = "115577290317206876914379725139810202736866562857077399175416156471449711434272";
EC_Instance.importPublicKey(x, y); // calls ec.keyFromPublic(pub, 'hex')
var verification_true = EC_Instance.verify("this is a test string", signature);

EC_Instance 是包含以下内容的类的对象:

constructor() {
    // Require the elliptic library for curve cryptography
    var EC = require('elliptic').ec;
    var ec = new EC('secp256k1');
    this.ec = ec;
}

importPublicKey(x, y) {
    var pub = { x: x.toString('hex'), y: y.toString('hex') };
    var key = this.ec.keyFromPublic(pub, 'hex');
    this.key = key;
    return key;
}

verify(message, signature) {
    return this.key.verify(message, signature);
}

【问题讨论】:

  • 来自 Java 加密 is (ASN.1) DER 的 ECDSA 签名,请参阅 stackoverflow.com/questions/49825455stackoverflow.com/questions/48177791 。要在 Java 中进行转换,请参阅 Stack 搜索已在此页面右侧建议的 stackoverflow.com/questions/34063694stackoverflow.com/questions/39385718
  • 非常感谢@dave_thompson_085。这很有帮助。但是,我似乎仍然坚持在节点服务器端验证签名。我已经用更多细节编辑了这个问题。感谢您的帮助。
  • 您发布的 X、Y 不是 secp256k1 上一个点(因此是公钥)坐标的十六进制表示。它们是此类坐标的十进制 表示。使用该点/键,您的 r,s do 在 Java+Bouncy(LWAPI) 中针对字符串的 SHA1(不是 SHA256)进行验证。注意 Java 将 byte 视为带符号的 -128..+127 并且 BigInteger.toByteArray 是二进制补码;我不知道 JS 和那个 JS 模块是否以相同的方式执行这些操作,并且链接中的示例表明不是。既然它说它确实将 DER 作为十六进制字符串或缓冲区,你为什么不试试呢?

标签: java node.js cryptography ecdsa


【解决方案1】:

这可能是哈希函数。 SHA-1 不应再用于签名操作。

我认为对于 node.js 代码,使用了 SHA-256 哈希方法,尽管用当前文档几乎不可能验证这一点(甚至几乎没有提到哈希)。

请注意 - 对于任何签名 - 哈希都是一个配置参数;它应该(对于 EC 而言,至少在没有实际验证的情况下不能)由签名本身来确定。

【讨论】:

  • 感谢@Maarten,我只是深入研究发现椭圆库使用hash.js,而curve chosen as secp256k1 它定义了hash.sha256。我更新了相关的 java 客户端代码,但验证似乎仍然没有按预期工作。看来我还是错过了什么
猜你喜欢
  • 1970-01-01
  • 2022-06-13
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 2014-11-12
相关资源
最近更新 更多