【发布时间】:2022-01-11 14:43:19
【问题描述】:
我正在尝试使用 BearSSL 在嵌入式设备上验证通过 OpenSSL 创建的 ECDSA 签名。
首先我使用 OpenSSL 创建了一个私钥并提取了公钥:
$ openssl ecparam -name secp256r1 -genkey -noout -out private.pem
$ openssl ec -in private.pem -pubout -out public.pem
然后我提取了原始公钥:
$ openssl ec -noout -text -inform PEM -in public.pem -pubin
read EC key
Public-Key: (256 bit)
pub:
04:28:4b:54:a4:d4:92:6c:82:2d:da:8a:e1:be:4b:
49:61:5d:91:2b:2d:f5:f2:66:f8:9b:d1:be:cb:fb:
db:fc:4f:68:cf:52:68:55:36:53:0f:8e:8d:69:3f:
40:3a:06:62:ad:5b:5a:66:e6:1d:31:c6:13:08:f3:
4f:94:7b:59:7a
我有一个text.txt,它只包含hello world,并使用以下命令对其进行签名:
$ cat text.txt
hello world
$ openssl dgst -sha256 -sign private.pem text.txt > signature
现在包含 ASN1 格式的签名,并且预期的长度为 72 个字节:
$ hexdump signature
0000000 4530 2002 ac54 51af 8ac0 cee8 dc74 4120
0000010 105c b65d a085 06c5 8e9f 1527 12f5 8e50
0000020 9d19 9b30 2102 a900 d2a5 343e 3a10 0bdd
0000030 e0a8 82f8 de2a 4f2d 51bf a775 bc42 2d2e
0000040 19c0 874f d85e 004b
现在到嵌入式部分。我首先包括数据、签名和公钥:
uint8_t text[] = "hello world\n";
size_t textlen = 12;
uint8_t signature[] = {
0x45, 0x30, 0x20, 0x02, 0xac, 0x54, 0x51, 0xaf, 0x8a, 0xc0, 0xce, 0xe8, 0xdc, 0x74, 0x41, 0x20,
0x10, 0x5c, 0xb6, 0x5d, 0xa0, 0x85, 0x06, 0xc5, 0x8e, 0x9f, 0x15, 0x27, 0x12, 0xf5, 0x8e, 0x50,
0x9d, 0x19, 0x9b, 0x30, 0x21, 0x02, 0xa9, 0x00, 0xd2, 0xa5, 0x34, 0x3e, 0x3a, 0x10, 0x0b, 0xdd,
0xe0, 0xa8, 0x82, 0xf8, 0xde, 0x2a, 0x4f, 0x2d, 0x51, 0xbf, 0xa7, 0x75, 0xbc, 0x42, 0x2d, 0x2e,
0x19, 0xc0, 0x87, 0x4f, 0xd8, 0x5e, 0x00, 0x4b };
static const uint8_t public_bytes[] = {
0x04, 0x28, 0x4b, 0x54, 0xa4, 0xd4, 0x92, 0x6c, 0x82, 0x2d, 0xda, 0x8a, 0xe1, 0xbe, 0x4b,
0x49, 0x61, 0x5d, 0x91, 0x2b, 0x2d, 0xf5, 0xf2, 0x66, 0xf8, 0x9b, 0xd1, 0xbe, 0xcb, 0xfb,
0xdb, 0xfc, 0x4f, 0x68, 0xcf, 0x52, 0x68, 0x55, 0x36, 0x53, 0x0f, 0x8e, 0x8d, 0x69, 0x3f,
0x40, 0x3a, 0x06, 0x62, 0xad, 0x5b, 0x5a, 0x66, 0xe6, 0x1d, 0x31, 0xc6, 0x13, 0x08, 0xf3,
0x4f, 0x94, 0x7b, 0x59, 0x7a };
static const br_ec_public_key public_key = {
.curve = BR_EC_secp256r1,
.q = (void *)public_bytes,
.qlen = sizeof(public_bytes)
};
此外,出于偏执,我比较了 text.txt 和我的字符串的 md5 总和,然后再进一步快速检查:
$ md5sum text.txt
6f5902ac237024bdd0c176cb93063dc4 text.txt
uint8_t sum[br_md5_SIZE];
br_md5_context md5ctx;
br_md5_init(&md5ctx);
br_md5_update(&md5ctx, text, textlen);
br_md5_out(&md5ctx, sum); // sum is the same as md5sum command output
我之前告诉 OpenSSL 使用 sha256 对使用 ECDSA 的签名过程的负载进行哈希处理,所以我现在在 BearSSL 中做同样的事情并尝试使用 secp256r1 验证签名:
br_sha256_context sha256ctx;
br_sha256_init(&sha256ctx);
br_sha256_update(&sha256ctx, text, textlen);
br_sha256_out(&sha256ctx, hash);
uint32_t result = br_ecdsa_i15_vrfy_asn1(&br_ec_prime_i15, hash, sizeof(hash), &public_key, signature, sizeof(signature));
我的期望是验证有效,因为我给了它相同的散列、散列函数、曲线、相应的公钥和签名。但是,这不起作用。我可能遗漏了一些明显但无法解决的问题。
【问题讨论】:
-
“这行不通”到底是什么意思?您是否收到错误消息、错误代码、意外结果?在哪一步? BearSSL 是否提供您可以检查的错误代码?你能检查中间结果吗,例如
hash的值? hexdump 输出显示 16 位值,而您的签名是 8 位值数组。 hexdump 是小端还是大端?也许您必须交换字节对?试试od -t x1 signature。 -
您是直接在嵌入式设备上还是在 Windows/Linux/macOS 上尝试验证?关于字节序的问题 +1。
-
@Bodo Dang。是的,由于字节顺序,我在 C 中的签名都是错误的。我猜我应该使用
xxd而不是hexdump。或od如您所建议的那样。不过,我现在无法测试它。但是,如果您将其放入答案中,我可以在我开始工作后立即接受它。 -
其实那个签名是71字节;除了 CPU-endian 之外,hexdump 还可以四舍五入。对于具有 256 位顺序的曲线,例如 P-256/secp256r1/prime256v1,ECDSA-sig DER 长度几乎 25% 的时间为 72,约 50% 的时间为 71,几乎 25% 的时间为 70,以及大约 0.2% 的时间(但偶尔)少于 70。
标签: c openssl cryptography ecdsa