【发布时间】:2021-12-01 08:26:58
【问题描述】:
我在 Java 中生成了一个 ECDSA 签名,我想从中获取 R 和 S 值,以便获得 COSE 编码的签名。据我了解,我生成的签名是 DER 编码的(默认使用 bouncyCastle)。当我使用 P-256 曲线 (SHA256withECDSA) 时,我正确检索了 R 和 S 值。
当我使用其他曲线(P-521、P-384)时,我在将签名从一种编码解析到另一种编码时遇到了一些问题。
下面是我的解析方式:
public static byte[] extractRandSToCose(byte[] signature) {
if (signature[0] == 0x30) {
//parse R
int rSize = signature[3];
BigInteger rBigInt = new BigInteger(Arrays.copyOfRange(signature, 4, rSize+4));
//strip out sign byte 0x00
byte[] r = toByteArrayUnsigned(rBigInt);
//parse S
int sSize = signature[5 + rSize];
int index = rSize + 6;
BigInteger sBigInt = new BigInteger(Arrays.copyOfRange(signature, index, index+sSize));
//strip out sign byte 0x00
byte[] s = toByteArrayUnsigned(sBigInt);
return Bytes.concat(r,s);
}
return null;
}
public static byte[] toByteArrayUnsigned(BigInteger bigInteger) {
byte[] extractedBytes = bigInteger.toByteArray();
int skipped = 0;
boolean skip = true;
for (byte b : extractedBytes) {
boolean signByte = b == 0x00;
if (skip && signByte) {
skipped++;
continue;
} else if (skip){
skip = false;
}
}
extractedBytes = Arrays.copyOfRange(extractedBytes, skipped, extractedBytes.length);
return extractedBytes;
}
我不明白我做错了什么。
PS:这些方法有时会起作用。它每次都适用于 P-256(DER 签名是 70 71 还是 72 字节长,并且正确地给了我 64 字节长的 Cose 签名),P-384(对于 102、103 104 字节长并且正确地给了我 96 字节)长 cose 签名),但只在 P-521 的一半时间内工作(138、139、140 字节长,即使它这样做,cose 签名也只有 31 字节长......)
【问题讨论】:
-
将 ASN.1/DER 签名转换为 IEEE P1363 (r|s) 签名的替代方法是直接以 IEEE P1363 (r|s) 格式生成签名。较新的 BC 版本使用算法
SHA256withPlain-ECDSA(或SHA384withPlain-ECDSA或SHA512withPlain-ECDSA)支持此格式。 -
关于转换,使用 ASN.1 解析器(例如来自 BouncyCastle)可能更有效,参见例如DER Decode ECDSA Signature in Java.
-
@Topaco+ Bouncy 自 2014 年 1.51 起 - 或 SunEC 自 2017 年 9 起使用不同(且更笨拙)的命名
{hash}withECDSAinP1363Format。 OP:您的代码虽然很笨拙,但应该大部分适用于高达 488 位的任何曲线,包括 P-384; P-521/secp521r1 和 brainpool512 是上面唯一常见的,但是一些恰好具有算术上小的 r 和/或 s 的签名是错误的。 -
@dave_thompson_085 - 谢谢。我依稀记得后来添加了对 r|s 的支持,但不完全是什么时候。然后我想 BC 版本的 OP 应该可以工作 ;-)
-
谢谢两位的回答,我去看看。为什么它不能在 488 位之后使用任何曲线?
标签: java bouncycastle signature ecdsa