簡體   English   中英

Python pkcs#7 x509 加密信任鏈

[英]Python pkcs#7 x509 chain of trust with cryptography

我正在開發一個 python 庫,以將 Apple Pay 有效負載處理為可用的卡詳細信息。 為此,我遵循此處的官方文檔。

除了 2 個驗證步驟外,一切都運行良好:

步驟 1.c。

確保存在從簽名到根 CA 的有效 X.509 信任鏈。 具體來說,確保簽名是使用葉證書對應的私鑰創建的,葉證書由中間 CA 簽名,中間 CA 由 Apple Root CA - G3 簽名。

在這里,我最終使用密碼庫通過以下代碼進行了最后一次檢查:

from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA

def verify_root_ca_chain_of_trust(
    trusted_root_ca: x509.Certificate,
    intermediate_cert: x509.Certificate,
    leaf_cert: x509.Certificate
) -> None:
    try:
        # verify that the intermediate CA is signed by the Apple Root CA - G3
        trusted_pub = trusted_root_ca.public_key()
        trusted_pub.verify(
            intermediate_cert.signature,
            intermediate_cert.tbs_certificate_bytes,
            ECDSA(hashes.SHA256())
        )
        # verify that the leaf certificate is signed by the intermediate CA
        trusted_intermediate_pub = intermediate_cert.public_key()
        trusted_intermediate_pub.verify(
            leaf_cert.signature,
            leaf_cert.tbs_certificate_bytes,
            ECDSA(hashes.SHA256())
        )
    except TypeError as err:
        raise CustomError('error') from err

我的問題是我只實施了所需檢查的 2/3。 我不知道該怎么做是:

具體來說,確保簽名是使用葉證書對應的私鑰創建的

步驟 1.d。 和 1.e.

d。 對於 ECC (EC_v1),確保簽名是 ephemeralPublicKey、data、transactionId 和 applicationData 鍵的串聯值的有效 ECDSA 簽名 (ecdsa-with-SHA256 1.2.840.10045.4.3.2)。

e. 檢查簽名的 CMS 簽名時間,如 RFC 5652 的第 11.3 節所定義。如果時間簽名和事務時間相差超過幾分鍾,則令牌可能是重放攻擊。

到目前為止我嘗試了什么:

def validate_token_signature(
    trusted_cert: x509.Certificate,
    signature: str,
    payment_data: str,
    ephemeral_pub: str,
    transaction_id: str,
    application_data: str = None,
) -> None:
    data_byte: bytes = base64.b64decode(ephemeral_pub)
    payment_data_byte: bytes = base64.b64decode(payment_data)
    transaction_id_byte: bytes = bytes.fromhex(transaction_id)
    data: bytes = data_byte + payment_data_byte + transaction_id_byte
    if application_data is not None:
        application_data_byte: bytes = base64.b64decode(application_data)
        data = data + application_data_byte
    try:
        trusted_leaf_pub = trusted_cert.public_key()
        trusted_leaf_pub.verify(base64.b64decode(signature), data, ECDSA(hashes.SHA256()))
    except InvalidSignature as err:
        print(err)
        # raise SignatureError('error') from err```

在這里,他們沒有指定要驗證的簽名,我認為它是 PKCS#7,但密碼學僅將 pkcs#7 處理為 x509 證書列表,如此所述:

將 PEM 編碼的 PKCS7 blob 反序列化為證書列表。 PKCS7 可以包含許多其他類型的數據,包括 CRL,但是這個 function 將忽略除證書之外的所有內容。

有沒有辦法使用 python 加密進行這些檢查,還是我必須使用另一個庫,如 pyopenssl 或其他什么?

要讀取 cms 簽名時間並驗證 pkcs7 簽名,我需要訪問 cms 內容和簽名者信息。 使用 m2crypto 或密碼學中的 pkcs#7 模塊無法實現這一點。 我最終使用的是 asn1crypto。

步驟 1.c 和 1.d 是相同檢查的一部分,所以我確實保留了信任鏈的上述檢查:

def verify_root_ca_chain_of_trust(
    trusted_root_ca: x509.Certificate,
    intermediate_cert: x509.Certificate,
    leaf_cert: x509.Certificate
) -> None:
    try:
        # verify that the intermediate CA is signed by the Apple Root CA - G3
        trusted_pub = trusted_root_ca.public_key()
        trusted_pub.verify(
            intermediate_cert.signature,
            intermediate_cert.tbs_certificate_bytes,
            ECDSA(hashes.SHA256())
        )
        # verify that the leaf certificate is signed by the intermediate CA
        trusted_intermediate_pub = intermediate_cert.public_key()
        trusted_intermediate_pub.verify(
            leaf_cert.signature,
            leaf_cert.tbs_certificate_bytes,
            ECDSA(hashes.SHA256())
        )
    except TypeError as err:
        raise CustomError('error') from err

要執行最后一部分檢查'特別是,確保使用與葉證書對應的私鑰創建簽名',我確實使用了 asn1crypto:

    from asn1crypto import cms
    from asn1crypto import core

    def validate_token_signature(
        trusted_cert: x509.Certificate,
        signature: bytes,
        payment_data: bytes,
        ephemeral_pub: bytes,
        transaction_id: bytes,
        application_data: bytes,
    ) -> None:
        
        signed_data = cms.ContentInfo.load(signature)['content']
        algo = signed_data['digest_algorithms'][0]['algorithm'].native
        signers_info = signed_data['signer_infos']
        attr_signature = signers_info[0].native['signature']
        attrs = signers_info[0]['signed_attrs']

        # Insure data signer and cert signer match
        cert_issuer: str = trusted_cert.issuer.rdns[0].rfc4514_string().split("=")[1]
        signed_data_issuer: str = dict(dict(dict(signers_info.native[0])['sid'])['issuer'])['common_name']
        if not cert_issuer == signed_data_issuer:
            raise CustomError('error')

        # Verify that cert is still validate now
        if not trusted_cert.not_valid_before < datetime.now() < trusted_cert.not_valid_after:
            raise CustomError('error')

        # user data
        udata: bytes = ephemeral_pub + payment_data + transaction_id + application_data

        mdData = getattr(hashlib, algo)(udata).digest()

        if attrs is not None and not isinstance(attrs, core.Void):
            # if attrs, mdSigned == message_digest attribute
            mdSigned = None
            for attr in attrs:
                if attr['type'].native == 'message_digest':
                    mdSigned = attr['values'].native[0]
            signedData = attrs.dump()
            signedData = b'\x31' + signedData[1:]
        else:
            # if no attrs, mdSigned == hash of userdata
            mdSigned = mdData
            signedData = udata

        # 2- verify() must succeed succeeded
        try:
            trusted_cert.public_key().verify(attr_signature, signedData, ECDSA(hashes.SHA256()))
        except InvalidSignature:
            raise CustomError('error')

        # 3- hashok must be True
        if not mdData == mdSigned:
            raise CustomError('error')

對於 cms 簽名時間驗證(1.e),我確實也使用了通過 asn1 object 獲得的數據:

    from asn1crypto import cms

    def cms_compare(p7: bytes) -> None:
        ci = cms.ContentInfo.load(p7)
        try:
            content = dict(ci.native['content'])
            signed_time: str = dict(dict(content['signer_infos'][0])['signed_attrs'][1])['values'][0]
            timed: datetime = datetime.strptime(str(signed_time), "%Y-%m-%d %H:%M:%S%z")

            if int(time.time()) - int(timed.strftime('%s')) > 30:
                raise CustomError('error')
        except TypeError:
            raise CustomError('error')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM