web-dev-qa-db-ja.com

python)で署名されたファイルを確認する方法

バックグラウンド

次のように、opensslSHA256と秘密鍵を使用してファイルに署名しました。

_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.sha256_と_public_key.crt_を使用して、_sign_me.Zip_が変更されていないことを確認する必要があります。
  2. Python 3.2-3.4と互換性があります
  3. WindowsとRedhatの両方で動作する必要があり、OpenSSLがパス上または既知の場所にあるという保証はありません。理想的にはコアPythonモジュールを使用したいのですが、複雑さが軽減される場合はサードパーティのモジュールを検討します。

私が試したこと

私はこれを行う方法を見つけようとして多くの検索を行いましたが、満足のいく答えを見つけることができませんでした。これが私が試したり研究したりしたことのリストです:

  • 次のシェルコマンドを使用して、署名を手動で確認できます。要件3のため、これは永続的なソリューションとしては機能しません。

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

  • 私は この質問 を見つけました。これはほぼ正確に私がやりたいことです。 2年近くも回答もコメントもされていません。 ssl python library )について言及しています。これは、主にクライアント/サーバー証明書とソケットを扱います。

  • この質問 は、暗号ライブラリを使用して「SHA256withRSAおよびPKCS1パディング」署名を検証しているようです。残念ながら、それはPython 2.7を対象とし、さらにPython 2.7 暗号でverify()メソッドのドキュメントを見つけることができませんでした 質問が参照するモジュール。
  • また、 暗号化 と呼ばれるサードパーティのモジュールを発見しました。 Stack Overflowに関するコンセンサスは、これが暗号化などの最新/最高のモジュールであるようですが、要件に一致するドキュメントを見つけることができませんでした。

おそらく私は明らかな何かを見逃していますか?私はセキュリティ/暗号化/ハッシュに関してあまり仕事をしていないので、フィードバックを歓迎します。

7
ErikusMaximus

私を正しい方向に向けてくれたPatrickMevzekに感謝します。 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!')
8
ErikusMaximus