rom*_*lot 3 openssl cryptography ecdsa
我正在尝试验证外部方提供给我们的 SHA256 ECDSA 数字签名。他们已经在内部验证了他们的签名过程,但我们的尝试没有成功。我们asn1 encoding routines
在 openssl 验证期间反复收到错误,但我无法看到签名或我们的流程有什么问题。
这是测试设置......公钥(pubkey.pem):
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOorVp0M8xien/r1/1Ln7TkSpzzcX
BL/MGRz66J1HSlEgBD5FwwpO1vo6jf/9azcrrrDdCi2NH9/cSDfv5D8gTA==
-----END PUBLIC KEY-----
Run Code Online (Sandbox Code Playgroud)
被签名的消息是明文字符串:
HELLO
Run Code Online (Sandbox Code Playgroud)
数字签名(signature.sig):
JJhwReHev8cxOsNKCR5t/Ee3WU9c7tkf9RuGNamXdpXQu9OL8ZKnsrblCO7vEmOXGKGrk6NsgA5JZpQhXO3A1Q==
Run Code Online (Sandbox Code Playgroud)
我们采取的一般方法是:
# create message file
echo "HELLO" > hello.txt
#VERIFY
openssl dgst -sha256 -verify pubkey.pem -signature signature.sig hello.txt
Run Code Online (Sandbox Code Playgroud)
响应是
Error Verifying Data
4655195756:error:0DFFF09B:asn1 encoding routines:CRYPTO_internal:too long:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/crypto/asn1/asn1_lib.c:143:
4655195756:error:0DFFF066:asn1 encoding routines:CRYPTO_internal:bad object header:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/crypto/asn1/tasn_dec.c:1113:
4655195756:error:0DFFF03A:asn1 encoding routines:CRYPTO_internal:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/crypto/asn1/tasn_dec.c:306:Type=ECDSA_SIG
Run Code Online (Sandbox Code Playgroud)
或者,我们对签名进行 base64 编码,base64 -D signature.sig > signature.bin
但得到相同的错误响应。我也尝试使用openssl pkeyutl
,但这也会导致asn1 encoding routines
错误。使用 ans1parse 解析签名产生:
openssl asn1parse -in signature.bin
Error: offset too large
Run Code Online (Sandbox Code Playgroud)
很明显,数字签名的格式是我没有处理的,但我看不到问题所在。
您的 signature.sig 文件似乎是 base64 编码的。像这样解码:
$ base64 -d signature.sig >signature.bin
Run Code Online (Sandbox Code Playgroud)
让我们看看我们有什么:
$ hexdump -C signature.bin
00000000 24 98 70 45 e1 de bf c7 31 3a c3 4a 09 1e 6d fc |$.pE....1:.J..m.|
00000010 47 b7 59 4f 5c ee d9 1f f5 1b 86 35 a9 97 76 95 |G.YO\......5..v.|
00000020 d0 bb d3 8b f1 92 a7 b2 b6 e5 08 ee ef 12 63 97 |..............c.|
00000030 18 a1 ab 93 a3 6c 80 0e 49 66 94 21 5c ed c0 d5 |.....l..If.!\...|
00000040
Run Code Online (Sandbox Code Playgroud)
出于比较目的,我根据您的公钥使用的相同曲线创建了一个新的 ECDSA 私钥(P-256):
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
Run Code Online (Sandbox Code Playgroud)
然后使用它对一些数据进行签名:
$ echo "HELLO" > hello.txt
$ openssl dgst -sha256 -sign key.pem -out hello.sig hello.txt
$ openssl asn1parse -in hello.sig -inform DER
0:d=0 hl=2 l= 68 cons: SEQUENCE
2:d=1 hl=2 l= 32 prim: INTEGER :2C1599C7765B047A2E98E2265CF6DB91232200559909D7F97CA3E859A39AC02C
36:d=1 hl=2 l= 32 prim: INTEGER :14E748DF692A8A7A2E41F984497782FF03F970DDB6591CCC68C71704B959A480
Run Code Online (Sandbox Code Playgroud)
所以你会注意到我们这里有两个整数,每个整数正好是 32 字节长。这对应于 ECDSA_SIG ASN.1 定义:
ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
Run Code Online (Sandbox Code Playgroud)
原始 ECDSA 签名由两个整数“r”和“s”组成。OpenSSL 期望它们被包裹在 DER 编码的表示中。但是,正如您已经发现签名所拥有的内容是无效的 DER。它是然而正好64个字节长-这表明它是由级联在一起2个32字节的整数。
出于本练习的目的,我们可以使用十六进制编辑器将原始 r 和 s 值转换为 DER 格式。让我们看一下我之前创建的 hello.sig 文件的十六进制转储:
$ hexdump -C hello.sig
00000000 30 44 02 20 2c 15 99 c7 76 5b 04 7a 2e 98 e2 26 |0D. ,...v[.z...&|
00000010 5c f6 db 91 23 22 00 55 99 09 d7 f9 7c a3 e8 59 |\...#".U....|..Y|
00000020 a3 9a c0 2c 02 20 14 e7 48 df 69 2a 8a 7a 2e 41 |...,. ..H.i*.z.A|
00000030 f9 84 49 77 82 ff 03 f9 70 dd b6 59 1c cc 68 c7 |..Iw....p..Y..h.|
00000040 17 04 b9 59 a4 80 |...Y..|
00000046
Run Code Online (Sandbox Code Playgroud)
我们从30
which 告诉我们我们有一个序列开始。下一个字节是44
剩余数据的长度。接下来是02
哪个是整数的标签,后跟20
(等于十进制32),这是整数的长度。接下来的 32 个字节是整数(r
值)。然后我们有另一个02
字节(整数)和20
(长度为 32),后跟s
值的 32 个字节。
因此,如果我们将字节添加30 44 02 20
到二进制签名数据的前面,然后是前 32 个字节的数据,然后是02 20
接下来的32 个字节,我们应该得到我们想要的......
...除了不幸的是它并不那么简单。你的s
价值存在复杂性。您会注意到它以 byte 开头d0
。该字节设置了其最重要的位 - 在整数的 DER 编码中表示整数值为负。那不是我们想要的。为了解决这个问题,我们必须00
在s
值的前面添加一个额外的字节。
这样做会改变总长度,因此我们现在必须将这些字节添加到30 45 02 20
开头,然后是签名数据的前 32 个字节,然后是签名数据02 21 00
的下一个 32 个字节。我在十六进制编辑器中执行此操作并提出以下内容:
$ hexdump -C signature2.bin
00000000 30 45 02 20 24 98 70 45 e1 de bf c7 31 3a c3 4a |0E. $.pE....1:.J|
00000010 09 1e 6d fc 47 b7 59 4f 5c ee d9 1f f5 1b 86 35 |..m.G.YO\......5|
00000020 a9 97 76 95 02 21 00 d0 bb d3 8b f1 92 a7 b2 b6 |..v..!..........|
00000030 e5 08 ee ef 12 63 97 18 a1 ab 93 a3 6c 80 0e 49 |.....c......l..I|
00000040 66 94 21 5c ed c0 d5 |f.!\...|
00000047
Run Code Online (Sandbox Code Playgroud)
让我们检查一下这看起来很正常:
$ openssl asn1parse -in signature2.bin -inform DER
0:d=0 hl=2 l= 69 cons: SEQUENCE
2:d=1 hl=2 l= 32 prim: INTEGER :24987045E1DEBFC7313AC34A091E6DFC47B7594F5CEED91FF51B8635A9977695
36:d=1 hl=2 l= 33 prim: INTEGER :D0BBD38BF192A7B2B6E508EEEF12639718A1AB93A36C800E496694215CEDC0D5
Run Code Online (Sandbox Code Playgroud)
现在让我们尝试验证签名:
$ openssl dgst -sha256 -verify pubkey.pem -signature signature2.bin hello.txt
Verification Failure
Run Code Online (Sandbox Code Playgroud)
该死的。那么近,又那么远。但至少我们摆脱了 ASN.1 错误。那么为什么它不起作用呢?凭直觉,我这样做了:
echo -n "HELLO" > hello2.txt
Run Code Online (Sandbox Code Playgroud)
echo 的“-n”参数会抑制输出中的换行符。也许换行符不应该包含在要为签名消化的数据中。所以,试试看:
$ openssl dgst -sha256 -verify pubkey.pem -signature signature2.bin hello2.txt
Verified OK
Run Code Online (Sandbox Code Playgroud)
成功!