[英]Java's BouncyCastle doesn't always verify OpenSSL ECDSA signature
[英]Error when verifying ECDSA signature in Java with BouncyCastle
我已經測試了一個解決方案,以驗證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”的帶符號的大端編碼;
- (vs)是值“s”的簽名大端編碼,具有最小長度。
應用該更改, signature
增長到70個字節,執行輸出沒有錯誤。
BC(和其他提供者)實現所使用的預期ECDSA簽名格式是DER編碼的ASN.1序列,其包含兩個整數值r
和s
。 此簽名格式已在ANSI X9.62中指定。 這是您提供的第一組數據中的格式(請注意, signature
總共為70個字節)。
在第二組數據中, signature
只有32個字節,根本不是ASN.1序列。 我猜這個值只是s
值,它缺少r
值和它們的ASN.1 INTEGER編碼,而是將值編碼為與鍵大小相同的無符號大整數值。
這是以ASN1 DER編碼格式編寫r和s的示例代碼
// construct the ASN1Sequence with r and s
ByteArrayOutputStream outs = new ByteArrayOutputStream();
byte radd = (byte)(((signed[0] & 0x80) > 0) ? 1 : 0);
byte sadd = (byte)(((signed[32] & 0x80) > 0) ? 1 : 0);
byte length = (byte)(0x44 + radd + sadd);
outs.write(0x30);
outs.write(length); // length 68 bytes +
outs.write(0x02); // ASN1Integer
outs.write(0x20 + radd); // length 32 bytes
if(radd > 0)
outs.write(0x00); // positive val
outs.write(signed, 0, 32);
outs.write(0x02); // ASN1Integer
outs.write(0x20 + sadd); // length 32 bytes
if(sadd > 0)
outs.write(0x00); // positive val
outs.write(signed, 32, 32);
signed = outs.toByteArray();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.