当前位置: 首页 > 面试题库 >

使用BouncyCastle验证Java中的ECDSA签名时出错

朱硕
2023-03-14
问题内容

我已经测试了一种解决方案,可以验证ECDSA签名如何从EC公共密钥字节中获取PublicKey对象?,该签名可以完美地处理给定的数据。

这是数据:

byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e3789c36a7a");
byte[] message = DatatypeConverter.parseHexBinary("54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29");
byte[] signature = DatatypeConverter.parseHexBinary("304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654");

这是代码(显示 true ):

private static boolean isValidSignature(byte[] pubKey, byte[] message,byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException {
    Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
    ecdsaVerify.initVerify(getPublicKeyFromBytes(pubKey));
    ecdsaVerify.update(message);
    return ecdsaVerify.verify(signature);
}

private static PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1");
    KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
    ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN());
    ECPoint point =  ECPointUtil.decodePoint(params.getCurve(), pubKey);
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
    ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
    return pk;
}

public static void main (String[] args) {
    System.out.println(isValidSignature(pubKey, message, signature));
}

当我将签名和数据更改为来自已实施系统的示例输入时,就会出现我的问题:

final static byte[] pubKey = DatatypeConverter.parseHexBinary("0447303876C6FED5550DF3EE1136989FCD87293D54A5D8E2F2F6D7FBE9A81089B889A5917443AF33E696178CEF4C9D6A4288B2745B29AF6C8BCAD1348F78EB9F9B");
final static byte[] message = DatatypeConverter.parseHexBinary("02158001f53611a06e2d1a270000013ed9305dc2780524015110500000002d0100140092569202017aa00c5dd30000000000000000000000000000000007d1000001020001b20788b80059f48d95cdefc8c6000200200030d41e0000012016840310a50733a9870fffd0430100");
final static byte[] signature = DatatypeConverter.parseHexBinary("531F8918FF250132959B01F7F56FDFD9E6CA3EC2144E12A6DA37C281489A3D96");

新数据输出此错误:

java.security.SignatureException: error decoding signature bytes.
    at org.bouncycastle.jcajce.provider.asymmetric.util.DSABase.engineVerify(Unknown Source)
    at java.security.Signature$Delegate.engineVerify(Signature.java:1178)
    at java.security.Signature.verify(Signature.java:612)
    at its.sec.exec.TestProgram.isValidSignature(TestProgram.java:168)
    at its.sec.exec.TestProgram.execution(TestProgram.java:101)
    at its.sec.exec.TestProgram.main(TestProgram.java:55)

我认为问题出在 安全消息 附带的签名上,因为:

  • 密钥对的长度和格式与示例相同。并且是正确的,因为它来自对消息进行签名的证书。
  • 消息本身(有效负载)不应影响安全过程。

最后值得一提的是,我的文档说签名必须前面有一个称为“ R”的字段,该字段 “包含通过将生成器元素乘以临时私钥而得到的椭圆曲线点的x坐标”,
并且其长度必须为与签名相同(32字节)。

有人可以指出我在这里缺少什么吗?

编辑:解决方案

正如彼得·迪特曼(Peter
Dettman)在回答中指出的那样,该signature格式的格式不正确(内容也不正确),因此无法通过该verify()方法进行计算。这是一个很好的解释,主要说:

当以DER编码时,此(签名)变为以下字节序列:

0x30 b1 0x02 b2 (vr) 0x02 b3 (vs)

哪里:

  • b1是一个单字节值,等于剩余字节列表的长度(以字节为单位)(从第一个0x02到编码结束);
  • b2是一个单字节值,等于(vr)的长度(以字节为单位);
  • b3是一个单字节值,等于(vs)的长度(以字节为单位);
  • (vr)是最小长度的“ r”值的有符号big-endian编码;
  • (vs)是值“ s”的有符号big-endian编码,长度最小。

应用该更改,signature将增加到70个字节,并且执行不会输出任何错误。


问题答案:

BC(和其他提供者)实现使用的预期ECDSA签名格式是DER编码的ASN.1序列,其中包含两个整数值rs。此签名格式已在ANSI
X9.62中指定。这是您提供的第一组数据的格式(请注意,signature总共70个字节)。

在第二组数据中,signature只有32个字节,并且根本不是ASN.1序列。我猜想这个值只是s值,并且缺少它们的r值和ASN.1
INTEGER编码,而是将值编码为与键大小相同的无符号大整数值。



 类似资料:
  • 我应该如何更改A方法?任何想法都是好的。提前谢谢。

  • 下面是Java代码: 然后使用C代码来验证签名 我在想我需要将我的签名编码为hexa,但它并没有解决我的问题。我已经使用crypto编写了一个c版本的符号方法,并且已经验证过了。所以为什么当我使用java代码时,签名没有得到验证。谢谢

  • 现有系统使用Bouncy Castle(.NET)生成签名,我需要使用Microsoft ECDsaCng类验证这些现有签名。 在我尝试用Microsoft类验证签名之前,一切都正常工作,此时它会生成一个异常,说明参数不正确。 有什么想法吗?

  • 我使用OpenSSL(在C中)对文本进行签名,但是我的Java程序并不总是验证签名的消息(只有大约1/5得到验证)。有趣的是,https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html没有证实其中任何一个: 曲线名称:secp256k1签名算法:SHA256with ECDSA 私钥 密钥 失败: 成功: 爪哇 C.

  • 下面是我的场景--我从条形码中读取数据,并将其转换为纯文本。本文是条码数据+数字签名的结合。数字签名附加在末尾,使我能够分离出实际数据和数字签名数据。数字签名数据使用sha256哈希-用户将公钥作为windows证书文件()发送给我。 所需实现:-需要从证书中提取公钥,并根据提供的条形码数据和数字签名验证公钥。 问题:

  • 我想创建一个签名并使用openssl验证它。我想有我的签名的十六进制输出。 这是我的密码 我得到这个错误: 如果我在创建签名的过程中删除了-hex,它就可以工作了。