OpenSSL ECDSA 签名比预期长

OpenSSL ECDSA signatures longer than expected

我正在尝试生成 "raw",用于加密芯片的未编码 ECDSA 签名。目标是在主机 pc 上签名,然后将其发送到芯片进行验证。但是,我 运行 遇到了一个小问题。我的理解是 ECDSA 签名应该是 64 字节(对于 secp256v1)。而且,我用芯片生成签名的时候,确实是64字节的长度。但是,当我使用 openssl 时,签名的长度为 71 个字节。签名的开头似乎是某种前缀,但我找不到关于那是什么的任何数据。

这是我尝试做所有事情的方式:

生成密钥:

openssl ecparam -genkey -name secp256r1 -noout -out privkeyv1.pem

生成待签名的"message":

echo -n "Hello World" > test.txt

我尝试了两种签名消息的方法。两者都会导致相同的意外输出。

第一种方法 - 生成测试文件的 sha256 哈希,然后对其进行签名:

sha256sum test.txt | cut -f 1 -d " " > hash

使用 pkutil 签名

openssl pkeyutl -sign -in hash -inkey privkeyv1.pem -out test_sig_meth1

方法二:用openssl dgst签名

openssl dgst -sha256 -binary -sign privkeyv1.pem -out test_sig_meth2 test.txt 问题:这是 xxd -p -c 256 test_sig_meth1 的输出: 3045022000a86fb146d5f8f6c15b962640bc2d1d928f5e0f96a5924e4db2853ec8b66fb002210085431613d0a235db1adabc090cc1062a246a78941972e298423f4b3d081b48c8

以及xxd -p -c 256 test_sig_meth2的输出: 30450220693732cd53d9f2ba3deae213d74cdf69a00e7325a10ddc6a4445ff2b33f95e62022100b6d2561e3afba10f95247ed05f0c59620dc0913f0d798b4148e05c4116b6384e

如您所见,这两种方法都在开头生成一些字节,看起来像 header 字节(30450220,可能更长),但我不确定它们的用途或者如何删除它们。作为参考,这里是在加密芯片上生成的相同方法的签名。如果你删除末尾的空字节填充,它就是 64 字节。 4677AD09F2AF49D7445ED5D6AC7253ADC863EC6D5DB6D3CFBF9C6D3E221D0A7BA2561942524F46B590AEE749D827FBF80A961E884E3A7D85EC75FE48ADBC0BD00000000000000000000000

问题:如何使用 openssl 生成 64 字节原始(未编码,没有 header)ECDSA 签名,我可以使用此方案?

出于效率原因,大多数芯片将只输出 rs 作为字节数组或八位字节串,其中每个 rs 是与八位字节中的字段大小(即密钥大小)相同。另一种方法是将 rs 作为数字序列输出,因为最后,这就是 rs 的内容。使用 ASN.1,这变成了 INTEGER 值的序列。

要从这样的序列进行转换,您可以先使用 BER 解析器进行 BER 解码以检索整数。然后实现一个 I2OSP 算法(整数到八位字节流原语),它需要 bytes/octets 中的值和密钥大小作为参数。该数字应该是大整数形式,但这很好,因为 ASN.1 BER 编码的整数也是大整数。基本上,如果数字太小,你必须用零字节填充。然后你连接数字。

我不会深入探讨将字节数组转换为整数的 OS2IP。请注意,如果您以 BER 形式对其进行编码,那么整数应该 而不是 被零字节填充。所以仍然需要一些技巧。

因此,虽然签名形式发生了变化,但签名仍然有效;您可以简单地在一种形式和另一种形式之间进行转换,并且签名仍然会验证 - 显然,只要您使用正确的库来完成工作。