java - 使用 BouncyCaSTLe 在 Java 中验证 ECDSA 签名时出错

标签 java bouncycastle signature ecdsa

我已经测试了一个解决方案来验证 ECDSA 签名 ( How can I get a PublicKey object from EC public key bytes? ) 与给定数据完美配合。

这是数据:

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)

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

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

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

有人可以指出我在这里遗漏了什么吗?

编辑:解决方案

正如 Peter Dettman 在他的回答中指出的那样,signature 的格式不正确(内容也不正确),无法通过 verify() 方法进行计算。 Here是一个很好的解释,主要是说:

When encoded in DER, this (signature) becomes the following sequence of bytes:

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

where:

  • b1 is a single byte value, equal to the length, in bytes, of the remaining list of bytes (from the first 0x02 to the end of the encoding);
  • b2 is a single byte value, equal to the length, in bytes, of (vr);
  • b3 is a single byte value, equal to the length, in bytes, of (vs);
  • (vr) is the signed big-endian encoding of the value "r", of minimal length;
  • (vs) is the signed big-endian encoding of the value "s", of minimal length.

应用该更改后,signature 增长到 70 字节并且执行输出没有错误。

最佳答案

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

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

关于java - 使用 BouncyCaSTLe 在 Java 中验证 ECDSA 签名时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30175149/

相关文章:

java - 让 GPG 解密在 Java 中工作(Bouncy CaSTLe)

c# - 如何在 C# 中创建与 BouncyCaSTLe 的 TLS 连接?

java - 如何从服务器验证客户端代码未被修改

ios - 转换 ASN。 1 个签名到 IEEE P1363 签名

java - OWL API,从 URI 中提取字符串

java - 在NIFI中创建自定义 Controller 服务时无法生成扩展的文档

javascript - javascript中的RSA加密和Java中的解密

android - OAuth 生成签名

java - 在 JasperException 上重定向页面

java - java中Enum的执行顺序