Openssl 使用 openssl pkeyutl 签名,使用 openssl dgst 验证

Openssl 使用 openssl pkeyutl 签名,使用 openssl dgst 验证

我想用 签署哈希openssl pkeyutl并用 进行验证openssl dgst -verify

下面是我测试的私钥和公钥(EC 密钥):

私人.pem:

-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFYL7prqjwKcVpKp4VF0kSshVoNCu3QzFeJHjvq78n4FoAoGCCqGSM49
AwEHoUQDQgAEkcL/M+0hEuW/VUNCZT5Jc1pyw9gm4vphWldTdAqMJhC8eTiP/gao
rTkz6+iFfOPbJTEzRD8y36WqYAlS+65W8A==
-----END EC PRIVATE KEY-----

公共.pem:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkcL/M+0hEuW/VUNCZT5Jc1pyw9gm
4vphWldTdAqMJhC8eTiP/gaorTkz6+iFfOPbJTEzRD8y36WqYAlS+65W8A==
-----END PUBLIC KEY-----
$ echo -n "123456" | openssl dgst -sha256 
8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92

123456 中的 S̶HA̶-̶2̶4̶5̶ SHA-246 是8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92

让我们用 pkeyutl 签署 SHA256(123456)

$ echo -n "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92" | openssl pkeyutl -inkey private.pem -sign | openssl base64 -e -A
MEYCIQDIJHf2SQJliMNvPwgCqanzqWxleK/YGSCd15RK8IYPEQIhAOlcvXH9ASQRMRNgKgMr4ZZLL3nyaCsTHBeU0iReZMmp

所以我们有使用 base64 编码的签名MEYCIQDIJHf2SQJliMNvPwgCqanzqWxleK/YGSCd15RK8IYPEQIhAOlcvXH9ASQRMRNgKgMr4ZZLL3nyaCsTHBeU0iReZMmp

现在让我们验证一下openssl pkeyutl -verify

$ openssl pkeyutl -verify -pubin -inkey public.pem  -sigfile <(echo -n "MEYCIQDIJHf2SQJliMNvPwgCqanzqWxleK/YGSCd15RK8IYPEQIhAOlcvXH9ASQRMRNgKgMr4ZZLL3nyaCsTHBeU0iReZMmp" | openssl enc -A -base64 -d ) -in <(echo -n "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92")
Signature Verified Successfully

所以验证成功。

现在有问题的部分:我们想要签名的原始数据是“123456”。让我们使用openssl dgst它:

$ echo -n "123456" | openssl dgst -sha256 -verify public.pem -signature <(echo -n "MEYCIQDIJHf2SQJliMNvPwgCqanzqWxleK/YGSCd15RK8IYPEQIhAOlcvXH9ASQRMRNgKgMr4ZZLL3nyaCsTHBeU0iReZMmp" | openssl enc -A -base64 -d)
Verification Failure

返回验证失败。

从原始“123456”文本创建 sha256 摘要时是否openssl dgst执行其他操作?

据我所知,它应该有效,因为签名与通过生成的哈希pkeyutl相同。这里我们使用相同的私钥和公钥,所以我不明白为什么无法验证通过生成的签名,sha256openssl dgst -sha256openssl dgst -verifypkeyutl

答案1

TLDR:这是十六进制

123456 的 SHA-245 [您指的是 SHA-256] 是 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92

不是。我们经常说这种话作为捷径或惯例,但你似乎误解了。8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92是 SHA-256123456 以十六进制显示意思是 16 进制,我们通常将其缩写为十六进制,或者我们也将其称为“编码为”或“表示为”十六进制。实际哈希值是 32 个字节的任意位,这些位不构成字符,因此无法正确显示,或发布到堆栈上,或以其他方式供人类使用;这就是为什么我们几乎总是使用十六进制或 base64 来处理程序和文件之外的此类数据。(更确切地说,哈希是256 位只有 32 个字节在使用 8 位字节的系统或设备上——并非所有设备都如此,但普通用户遇到的所有设备都如此。特别是,OpenSSL 工作的所有设备都使用 8 位字节,因此对于 OpenSSLSHA-256 是 32 字节。)

您需要将十六进制表示形式转换回实际数据(在 Unix 上有几个选项,由这里的许多 Q 或 serverfault 或 unix.SX 涵盖,但在 Windows 上较少(WSL 之外,它实际上是 Unix)) - 或者使用dgst -binary首先不创建十六进制表示形式,因此更简单:

$ echo -n 123456 |openssl sha256 -binary |tee hash |od -tx1
0000000 8d 96 9e ef 6e ca d3 c2 9a 3a 62 92 80 e6 86 cf
0000020 0c 3f 5d 5a 86 af f3 ca 12 02 0c 92 3a dc 6c 92
0000040
$ openssl pkeyutl -sign -inkey private -in hash |openssl base64 -e -A |tee sig
MEUCIQCxK8aJ195ihqV/XJtwESXnpD7lQX47uppM8SD/JMehiAIgKDqNXvPfvC7SqbSJ3yBaqt3mJ+GIHD9sDP1YGBh9shA=
$ openssl pkeyutl -verify -pubin -inkey public -in hash -sigfile <( openssl base64 -d -A <sig )
Signature Verified Successfully
$ echo -n 123456 |openssl sha256 -verify public -signature <( openssl base64 -d -A <sig )
Verified OK
$

PS:注意并非所有版本都echo支持-n;请参阅https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo

更新 Vlado 提出的编辑建议(被社区拒绝):当使用openssl pkeyutl -sign 对于 RSASSA-PKCS1-v1_5签名(即 RSA 而非 RSA-PSS)您需要-pkeyopt digest:sha256创建并-verify检查符合标准的签名,该签名将与openssl sha256 -verify(以及-sign)互操作。对于 ECDSA 签名,不需要该选项,这是这里提出的问题,尽管意识到差异是件好事。(对于 DSA 也不需要,因为现在很少有人使用它。并且不适用于 EdDSA,因为目前都不pkeyutl -sign支持dgst -sign它。)

相关内容