【问题标题】:How to verify a signed file in python如何在python中验证签名文件
【发布时间】:2018-11-09 12:07:39
【问题描述】:

背景

我使用 openssl SHA256 和私钥签署了一个文件,如下所示:

with subprocess.Popen(
        # Pipe the signature to openssl to convert it from raw binary encoding to base64 encoding.
        # This will prevent any potential corruption due to line ending conversions, and also allows 
        # a human to read and copy the signature (e.g. for manual verification).
        'openssl dgst -sha256 -sign private.key sign_me.zip | openssl base64 > signature.sha256',
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        shell=True,
) as proc:
    out, _ = proc.communicate()

要求

  1. 我需要使用signature.sha256public_key.crt来验证sign_me.zip没有被修改。
  2. 兼容 Python 3.2 - 3.4
  3. 需要同时在 Windows 和 Redhat 上工作,并且不保证 OpenSSL 将在路径上或在已知位置。理想情况下,我想使用核心 Python 模块,但如果它可以降低复杂性,我会考虑使用 3rd 方模块。

我的尝试

我做了很多搜索,试图弄清楚如何做到这一点,但我一直未能找到令人满意的答案。以下是我尝试和/或研究过的事情的清单:

  • 我可以通过以下 shell 命令手动验证签名。由于要求 3,这不能作为永久解决方案。

    openssl dgst -sha256 -verify <(openssl x509 -in public_key.crt -pubkey -noout) -signature signature.sha256 sign_me.zip

  • 我找到了this question,这几乎正是我想要做的。近 2 年没有人回答,甚至没有评论。它提到了ssl python library,它主要处理客户端/服务器证书和套接字。

  • This question 似乎使用加密库来验证“SHA256withRSA and PKCS1 padding”签名。不幸的是,它针对的是 Python 2.7,此外,我无法在问题引用的 Python 2.7 crypto 模块中找到 verify() 方法文档。
  • 我还发现了一个名为cryptography 的第三方模块。 Stack Overflow 上的共识似乎是,这是用于加密等的最新/最好的模块,但我无法找到符合我要求的文档。

也许我遗漏了一些明显的东西?我在安全/加密/散列方面没有做太多工作,因此欢迎提供反馈。

【问题讨论】:

  • 查看cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa 的“验证”部分。或“非对称算法”下的其他情况。在能够使用verify()之前,您将需要一些管道来从字符串/文件中构造适当的对象。
  • 这里是另一个选项:gist.github.com/lkdocs/6519372
  • 如果您想减少错误和互操作性问题,您应该使用用于验证它的相同工具生成签名。
  • 核心 Python 模块依赖于 OpenSSL 库(OpenSSL 必须安装在系统上才能工作) .对于 WinOpenSSL 库静态链接在 Python 模块内。我倾向于认为其他 3rd 方模块的情况是一样的。

标签: python-3.x ssl openssl cryptography sha256


【解决方案1】:

感谢 Patrick Mevzek 为我指明了正确的方向。我最终使用Cryptography 模块找到了以下解决我的问题的方法。我最终更改了我签署文件的方式,以匹配我以后验证它的方式。

密钥生成:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa

# Generate the public/private key pair.
private_key = rsa.generate_private_key(
    public_exponent = 65537,
    key_size = 4096,
    backend = default_backend(),
)

# Save the private key to a file.
with open('private.key', 'wb') as f:
    f.write(
        private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        )
    )

# Save the public key to a file.
with open('public.pem', 'wb') as f:
    f.write(
        private_key.public_key().public_bytes(
            encoding = serialization.Encoding.PEM,
            format = serialization.PublicFormat.SubjectPublicKeyInfo,
        )
    )

签名:

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

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

# Load the contents of the file to be signed.
with open('payload.dat', 'rb') as f:
    payload = f.read()

# Sign the payload file.
signature = base64.b64encode(
    private_key.sign(
        payload,
        padding.PSS(
            mgf = padding.MGF1(hashes.SHA256()),
            salt_length = padding.PSS.MAX_LENGTH,
        ),
        hashes.SHA256(),
    )
)
with open('signature.sig', 'wb') as f:
    f.write(signature)

验证:

import base64
import cryptography.exceptions
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_public_key

# Load the public key.
with open('public.pem', 'rb') as f:
    public_key = load_pem_public_key(f.read(), default_backend())

# Load the payload contents and the signature.
with open('payload.dat', 'rb') as f:
    payload_contents = f.read()
with open('signature.sig', 'rb') as f:
    signature = base64.b64decode(f.read())

# Perform the verification.
try:
    public_key.verify(
        signature,
        payload_contents,
        padding.PSS(
            mgf = padding.MGF1(hashes.SHA256()),
            salt_length = padding.PSS.MAX_LENGTH,
        ),
        hashes.SHA256(),
    )
except cryptography.exceptions.InvalidSignature as e:
    print('ERROR: Payload and/or signature files failed verification!')

【讨论】:

  • 如果验证 X.509 证书,请改用padding.PKCS1v15()
  • 感谢@ErikusMaximus 和ErikusMaximus 的帮助!网上没人有这个!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-30
  • 2019-12-10
  • 2012-01-30
  • 2014-02-08
  • 2015-10-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多