使用BouncyCastlevalidationJava中的ECDSA签名时出错

我已经测试了一个解决方案,以validationECDSA签名( 如何从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”的带符号的大端编码;
  • (vs)是值为“s”的签名大端编码,具有最小长度。

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

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

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