使用 openssl 实用程序签名,使用 python 脚本验证

问题描述

我想在 python 中验证已使用命令行实用程序签名的文件的签名。

我的命令行。

openssl pkeyutl -sign -in data.sha256 -inkey device.key -out data-pss.sign -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:32 -pkeyopt rsa_mgf1_md:sha256

我的python脚本。

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization 

# client cert -> get public key
with open('device.pem','rb') as fc:
    cert = x509.load_pem_x509_certificate(fc.read(),default_backend())
    public_key = cert.public_key()

# private key
with open('device.key','rb') as fk:
    private_key = serialization.load_pem_private_key(fk.read(),password=None,backend=default_backend())

# data sha256 that has been signed
with open('data.sha256','rb') as fd:
    data_sha256 = fd.read()

# data signature done by command line
with open('data-pss.sign','rb') as fds:
    data_sig = fds.read()

public_key.verify(data_sig,data_sha256,padding.PSS(
                    mgf=padding.MGF1(hashes.SHA256()),salt_length=32
                  ),hashes.SHA256())

我得到 cryptography.exceptions.InvalidSignature

如果我通过 python 进行签名 - 没问题 - 验证成功。

data_sig_py = private_key.sign(data_sha256,padding.PSS(
                                 mgf=padding.MGF1(hashes.SHA256()),salt_length=32
                               ),hashes.SHA256()
                              )

public_key.verify(data_sig_py,hashes.SHA256())

解决方法

在 Python 代码中,已经散列的数据在 RSAPublicKey#verify() 的第二个参数中传递。在这种情况下,必须在 Prehashed 的第四个参数中传递一个 RSAPublicKey#verify() 实例,即特别是 utils.Prehashed(hashes.SHA256()),另见此 example

目前一个 HashAlgorithm 实例作为第四个参数传递,即特别是 hashes.SHA256()。这也是可能的,但前提是在第二个参数中传递原始数据(即未散列数据)而不是已经散列的数据。 RSAPublicKey#verify() 在这种情况下隐式散列。

即在当前的 Python 代码中,数据在验证时double 散列。然而,在 OpenSSL 语句中,唯一的单个散列数据是签名的。因此,使用 Python 代码的验证失败。
如果使用 Python 代码创建签名(如您的上一个示例),则数据也会两次散列,从而导致验证成功。