Delen via


Ontvangstbevestigingen voor schrijfbewerkingen van Azure Confidential Ledger controleren

Een azure Confidential Ledger-ontvangstbewijs voor schrijftransacties vertegenwoordigt een cryptografisch Merkle-bewijs dat de bijbehorende schrijftransactie wereldwijd is doorgevoerd door het CCF-netwerk. Azure Confidential Ledger-gebruikers kunnen op elk gewenst moment een ontvangstbevestiging krijgen via een vastgelegde schrijftransactie om te controleren of de bijbehorende schrijfbewerking is vastgelegd in het onveranderbare grootboek.

Zie het speciale artikel voor meer informatie over het schrijven van transactiebevestigingen van Azure Confidential Ledger.

Verificatiestappen voor ontvangst

Een ontvangstbewijs voor schrijftransacties kan worden geverifieerd volgens een specifieke set stappen die worden beschreven in de volgende subsecties. Dezelfde stappen worden beschreven in de CCF-documentatie.

Leaf-knooppuntberekening

De eerste stap is het berekenen van de SHA-256-hash van het leaf-knooppunt in de Merkle-structuur die overeenkomt met de vastgelegde transactie. Een leaf-knooppunt bestaat uit de geordende samenvoeging van de volgende velden die kunnen worden gevonden in een ontvangstbewijs van azure Confidential Ledger, onder leafComponents:

  1. writeSetDigest
  2. SHA-256 digest van commitEvidence
  3. claimsDigest Velden

Deze waarden moeten worden samengevoegd als matrices van bytes: beide writeSetDigest en claimsDigest moeten worden geconverteerd van tekenreeksen van hexadecimale cijfers naar matrices van bytes. Aan de andere kant kan de hash van commitEvidence (als matrix van bytes) worden verkregen door de SHA-256-hashfunctie toe te passen op de UTF-8 gecodeerde commitEvidence tekenreeks.

Op dezelfde manier kan de hash-digest van het leaf-knooppunt worden berekend door de SHA-256-hashfunctie toe te passen op de resultaatsamenvoeging van de resulterende bytes.

Hoofdknooppuntberekening

De tweede stap is het berekenen van de SHA-256-hash van de hoofdmap van de Merkle Tree op het moment dat de transactie is doorgevoerd. De berekening wordt uitgevoerd door iteratief het resultaat van de vorige iteratie samen te vouwen en hashen (te beginnen met de leaf-knooppunthash die in de vorige stap is berekend) met de hashes van de geordende knooppunten die zijn opgegeven in het proof veld van een ontvangstbevestiging. De proof lijst wordt geleverd als een geordende lijst en de bijbehorende elementen moeten in de opgegeven volgorde worden doorlopen.

De samenvoeging moet worden uitgevoerd op de bytesweergave met betrekking tot de relatieve volgorde die wordt aangegeven in de objecten die in het proof veld zijn opgegeven (of left right).

  • Als de sleutel van het huidige element is proof left, moet het resultaat van de vorige iteratie worden toegevoegd aan de huidige elementwaarde.
  • Als de sleutel van het huidige element proof is right, moet het resultaat van de vorige iteratie worden voorafgegaan aan de huidige elementwaarde.

Na elke samenvoeging moet de SHA-256-functie worden toegepast om de invoer voor de volgende iteratie te verkrijgen. Dit proces volgt de standaardstappen voor het berekenen van het hoofdknooppunt van een Merkle Tree-gegevensstructuur op basis van de vereiste knooppunten voor de berekening.

Handtekening controleren via hoofdknooppunt

De derde stap is om te controleren of de cryptografische handtekening die is geproduceerd via de hash van het hoofdknooppunt geldig is met behulp van het certificaat voor het ondertekeningsknooppunt in het ontvangstbewijs. Het verificatieproces volgt de standaardstappen voor verificatie van digitale handtekeningen voor berichten die zijn ondertekend met behulp van het Elliptic Curve Digital Signature Algorithm (ECDSA). Meer specifiek zijn de stappen:

  1. De base64-tekenreeks signature decoderen in een matrix van bytes.
  2. Pak de openbare ECDSA-sleutel uit het handtekeningknooppuntcertificaat cert.
  3. Controleer of de handtekening over de hoofdmap van de Merkle-boomstructuur (berekend met behulp van de instructies in de vorige subsectie) authentiek is met behulp van de uitgepakte openbare sleutel uit de vorige stap. Deze stap komt effectief overeen met een standaard verificatieproces voor digitale handtekeningen met behulp van ECDSA. Er zijn veel bibliotheken in de populairste programmeertalen waarmee u een ECDSA-handtekening kunt verifiëren met behulp van een openbaar sleutelcertificaat voor bepaalde gegevens (bijvoorbeeld de cryptografiebibliotheek voor Python).

Goedkeuring van handtekeningknooppuntcertificaat controleren

Naast de vorige stap is het ook vereist om te controleren of het certificaat van het handtekeningknooppunt is goedgekeurd (dat wil wel ondertekend) door het huidige grootboekcertificaat. Deze stap is niet afhankelijk van de andere drie vorige stappen en kan onafhankelijk van de andere stappen worden uitgevoerd.

Het is mogelijk dat de huidige service-id die het ontvangstbewijs heeft uitgegeven, verschilt van de identiteit die het ondertekeningsknooppunt heeft goedgekeurd (bijvoorbeeld vanwege een certificaatvernieuwing). In dit geval is het vereist om de vertrouwensketen van certificaten te verifiëren van het certificaat voor ondertekeningsknooppunten (dat wil gezegd, het cert veld in de ontvangstbevestiging) tot aan de vertrouwde basiscertificeringsinstantie (ca) (dat wil gezegd het huidige certificaat voor service-id's) via andere eerdere service-id's (dat wil gezegd, het serviceEndorsements lijstveld in het ontvangstbewijs). De serviceEndorsements lijst wordt geleverd als een geordende lijst van de oudste naar de meest recente service-id.

Goedkeuring van certificaten moet worden geverifieerd voor de hele keten en volgt hetzelfde verificatieproces voor digitale handtekeningen dat in de vorige subsectie is beschreven. Er zijn populaire opensource-cryptografische bibliotheken (bijvoorbeeld OpenSSL) die doorgaans kunnen worden gebruikt voor het uitvoeren van een stap voor het goedkeuren van certificaten.

Samenvatting van toepassingsclaims controleren

Als optionele stap, als toepassingsclaims zijn gekoppeld aan een ontvangstbewijs, is het mogelijk om de claimssamenvating te berekenen van de weergegeven claims (na een specifiek algoritme) en te controleren of de digest overeenkomt met de claimsDigest inhoud van de nettolading van de ontvangst. Als u de samenvatting van de weergegeven claimobjecten wilt berekenen, moet u elk claimobject van de toepassing in de lijst doorlopen en het bijbehorende veld controleren kind .

Als het claimobject van soort LedgerEntryis, moet de verzamelings-id van het grootboek (collectionId) en de inhoud (contents) van de claim worden geëxtraheerd en gebruikt om hun HMAC-samenvattingen te berekenen met behulp van de geheime sleutel (secretKey) die is opgegeven in het claimobject. Deze twee samenvattingen worden vervolgens samengevoegd en de SHA-256-hash van de samenvoeging wordt berekend. Het protocol (protocol) en de resulterende claimgegevenssamenvatting worden vervolgens samengevoegd en een andere SHA-256-hash van de samenvoeging wordt berekend om de uiteindelijke samenvatting te verkrijgen.

Als het claimobject van soort ClaimDigestis, moet de claimsamenvatting (value) worden geëxtraheerd, samengevoegd met het protocol (protocol) en wordt de SHA-256-hash van de samenvoeging berekend om de uiteindelijke samenvatting te verkrijgen.

Nadat u elke samenvatting van elke claim hebt berekend, moet u alle berekende samenvattingen van elk toepassingsclaimobject samenvoegen (in dezelfde volgorde die in het ontvangstbewijs worden weergegeven). De samenvoeging moet vervolgens worden voorafgegaan door het aantal verwerkte claims. De SHA-256-hash van de vorige samenvoeging produceert de uiteindelijke claimssamenvatting, die overeenkomt met het claimsDigest huidige in het ontvangstobject.

Meer resources

Zie het speciale artikel voor meer informatie over de inhoud van een Azure Confidential Ledger-transactiebevestiging en uitleg van elk veld. De CCF-documentatie bevat ook meer informatie over ontvangstbevestiging en andere gerelateerde bronnen via de volgende koppelingen:

Ontvangstbevestigingen voor schrijftransacties controleren

Hulpprogramma's voor ontvangstverificatie

De Azure Confidential Ledger-clientbibliotheek voor Python biedt hulpprogrammafuncties voor het verifiëren van ontvangstbewijzen voor schrijftransacties en het berekenen van de claimssamenvating uit een lijst met toepassingsclaims. Zie deze sectie en deze voorbeeldcode voor meer informatie over het gebruik van de Data Plane SDK en de hulpprogramma's die specifiek zijn voor ontvangstbewijzen.

Installatie en vereisten

Voor referentiedoeleinden bieden we voorbeeldcode in Python om azure Confidential Ledger-transactiebevestigingen volledig te verifiëren volgens de stappen die in de vorige sectie worden beschreven.

Als u het volledige verificatie-algoritme wilt uitvoeren, zijn het huidige servicenetwerkcertificaat en een ontvangstbewijs voor schrijftransacties van een actieve vertrouwelijke grootboekresource vereist. Raadpleeg dit artikel voor meer informatie over het ophalen van een ontvangstbewijs voor schrijftransacties en het servicecertificaat van een exemplaar van vertrouwelijk grootboek.

Kennismaking met code

De volgende code kan worden gebruikt om de vereiste objecten te initialiseren en het algoritme voor ontvangstverificatie uit te voeren. Een afzonderlijk hulpprogramma (verify_receipt) wordt gebruikt om het volledige verificatie-algoritme uit te voeren en accepteert de inhoud van het receipt veld in een GET_RECEIPT antwoord als een woordenlijst en het servicecertificaat als een eenvoudige tekenreeks. De functie genereert een uitzondering als het ontvangstbewijs niet geldig is of als er een fout is opgetreden tijdens de verwerking.

Er wordt van uitgegaan dat zowel het ontvangstbewijs als het servicecertificaat uit bestanden kunnen worden geladen. Zorg ervoor dat u zowel de service_certificate_file_name als receipt_file_name de constanten bijwerkt met de respectieve bestandsnamen van het servicecertificaat en de ontvangstbevestiging die u wilt verifiëren.

import json 

# Constants
service_certificate_file_name = "<your-service-certificate-file>"
receipt_file_name = "<your-receipt-file>"

# Use the receipt and the service identity to verify the receipt content 
with open(service_certificate_file_name, "r") as service_certificate_file, open( 
    receipt_file_name, "r" 
) as receipt_file: 

    # Load relevant files content 
    receipt = json.loads(receipt_file.read())["receipt"] 
    service_certificate_cert = service_certificate_file.read() 

    try: 
        verify_receipt(receipt, service_certificate_cert) 
        print("Receipt verification succeeded") 

    except Exception as e: 
        print("Receipt verification failed") 

        # Raise caught exception to look at the error stack
        raise e 

Omdat voor het verificatieproces enkele cryptografische en hash-primitieven zijn vereist, worden de volgende bibliotheken gebruikt om de berekening te vergemakkelijken.

  • De CCF Python-bibliotheek: de module biedt een set hulpprogramma's voor verificatie van ontvangstbewijzen.
  • De Cryptografiebibliotheek van Python: een veelgebruikte bibliotheek met verschillende cryptografische algoritmen en primitieven.
  • De hashlib-module, onderdeel van de standaardbibliotheek van Python: een module die een algemene interface biedt voor populaire hash-algoritmen.
from ccf.receipt import verify, check_endorsements, root 
from cryptography.x509 import load_pem_x509_certificate, Certificate 
from hashlib import sha256 
from typing import Dict, List, Any 

In de verify_receipt functie controleren we of het opgegeven ontvangstbewijs geldig is en alle vereiste velden bevat.

# Check that all the fields are present in the receipt 
assert "cert" in receipt 
assert "leafComponents" in receipt 
assert "claimsDigest" in receipt["leafComponents"] 
assert "commitEvidence" in receipt["leafComponents"] 
assert "writeSetDigest" in receipt["leafComponents"] 
assert "proof" in receipt 
assert "signature" in receipt 

We initialiseren de variabelen die in de rest van het programma worden gebruikt.

# Set the variables 
node_cert_pem = receipt["cert"] 
claims_digest_hex = receipt["leafComponents"]["claimsDigest"] 
commit_evidence_str = receipt["leafComponents"]["commitEvidence"] 
write_set_digest_hex = receipt["leafComponents"]["writeSetDigest"] 
proof_list = receipt["proof"] 
service_endorsements_certs_pem = receipt.get("serviceEndorsements", [])
root_node_signature = receipt["signature"] 

We kunnen de PEM-certificaten laden voor de service-id, het ondertekeningsknooppunt en de goedkeuringscertificaten van eerdere service-identiteiten met behulp van de cryptografiebibliotheek.

# Load service and node PEM certificates 
service_cert = load_pem_x509_certificate(service_cert_pem.encode()) 
node_cert = load_pem_x509_certificate(node_cert_pem.encode()) 

# Load service endorsements PEM certificates 
service_endorsements_certs = [ 
    load_pem_x509_certificate(pem.encode()) 
    for pem in service_endorsements_certs_pem 
] 

De eerste stap van het verificatieproces is het berekenen van de digest van het leaf-knooppunt.

# Compute leaf of the Merkle Tree corresponding to our transaction 
leaf_node_hex = compute_leaf_node( 
    claims_digest_hex, commit_evidence_str, write_set_digest_hex 
)

De compute_leaf_node functie accepteert als parameters voor de bladonderdelen van het ontvangstbewijs (de claimsDigest, de commitEvidenceen de writeSetDigest) en retourneert de leaf-knooppunt-hash in hexadecimale vorm.

Zoals eerder beschreven, berekenen we de samenvatting van commitEvidence (met behulp van de SHA-256-functie hashlib ). Vervolgens converteren we beide writeSetDigest en claimsDigest naar matrices van bytes. Ten slotte samenvoegen we de drie matrices en verwerken we het resultaat met behulp van de SHA256-functie.

def compute_leaf_node( 
    claims_digest_hex: str, commit_evidence_str: str, write_set_digest_hex: str 
) -> str: 
    """Function to compute the leaf node associated to a transaction 
    given its claims digest, commit evidence, and write set digest.""" 

    # Digest commit evidence string 
    commit_evidence_digest = sha256(commit_evidence_str.encode()).digest() 

    # Convert write set digest to bytes 
    write_set_digest = bytes.fromhex(write_set_digest_hex) 

    # Convert claims digest to bytes 
    claims_digest = bytes.fromhex(claims_digest_hex) 

    # Create leaf node by hashing the concatenation of its three components 
    # as bytes objects in the following order: 
    # 1. write_set_digest 
    # 2. commit_evidence_digest 
    # 3. claims_digest 
    leaf_node_digest = sha256( 
        write_set_digest + commit_evidence_digest + claims_digest 
    ).digest() 

    # Convert the result into a string of hexadecimal digits 
    return leaf_node_digest.hex() 

Na het berekenen van het blad kunnen we de wortel van de Merkle-boom berekenen.

# Compute root of the Merkle Tree 
root_node = root(leaf_node_hex, proof_list) 

We gebruiken de functie root die is opgegeven als onderdeel van de CCF Python-bibliotheek. De functie voegt achtereenvolgens het resultaat van de vorige iteratie samen met een nieuw element uit proof, verwerkt de samenvoeging en herhaalt vervolgens de stap voor elk element in proof de eerder berekende digest. De samenvoeging moet de volgorde van de knooppunten in de Merkle-boomstructuur respecteren om ervoor te zorgen dat de hoofdmap correct wordt gecomputeerd.

def root(leaf: str, proof: List[dict]): 
    """ 
    Recompute root of Merkle tree from a leaf and a proof of the form: 
    [{"left": digest}, {"right": digest}, ...] 
    """ 

    current = bytes.fromhex(leaf) 

    for n in proof: 
        if "left" in n: 
            current = sha256(bytes.fromhex(n["left"]) + current).digest() 
        else: 
            current = sha256(current + bytes.fromhex(n["right"])).digest() 
    return current.hex() 

Na het berekenen van de hash van het hoofdknooppunt, kunnen we de handtekening in de ontvangstbevestiging via de hoofdmap controleren om te controleren of de handtekening juist is.

# Verify signature of the signing node over the root of the tree 
verify(root_node, root_node_signature, node_cert) 

Op dezelfde manier biedt de CCF-bibliotheek een functie verify om deze verificatie uit te voeren. We gebruiken de openbare ECDSA-sleutel van het handtekeningknooppuntcertificaat om de handtekening te controleren via de hoofdmap van de structuur.

def verify(root: str, signature: str, cert: Certificate):
    """ 
    Verify signature over root of Merkle Tree 
    """ 

    sig = base64.b64decode(signature) 
    pk = cert.public_key() 
    assert isinstance(pk, ec.EllipticCurvePublicKey) 
    pk.verify( 
        sig, 
        bytes.fromhex(root), 
        ec.ECDSA(utils.Prehashed(hashes.SHA256())), 
    )

De laatste stap van ontvangstverificatie is het valideren van het certificaat dat is gebruikt voor het ondertekenen van de hoofdmap van de Merkle-boomstructuur.

# Verify node certificate is endorsed by the service certificates through endorsements 
check_endorsements(node_cert, service_cert, service_endorsements_certs) 

Op dezelfde manier kunnen we het CCF-hulpprogramma check_endorsements gebruiken om te controleren of de service-identiteit het ondertekeningsknooppunt onderschrijft. De certificaatketen kan bestaan uit eerdere servicecertificaten, dus we moeten valideren dat de goedkeuring transitief wordt toegepast als serviceEndorsements dit geen lege lijst is.

def check_endorsement(endorsee: Certificate, endorser: Certificate): 
    """ 
    Check endorser has endorsed endorsee 
    """ 

    digest_algo = endorsee.signature_hash_algorithm 
    assert digest_algo 
    digester = hashes.Hash(digest_algo) 
    digester.update(endorsee.tbs_certificate_bytes) 
    digest = digester.finalize() 
    endorser_pk = endorser.public_key() 
    assert isinstance(endorser_pk, ec.EllipticCurvePublicKey) 
    endorser_pk.verify( 
        endorsee.signature, digest, ec.ECDSA(utils.Prehashed(digest_algo)) 
    ) 

def check_endorsements( 
    node_cert: Certificate, service_cert: Certificate, endorsements: List[Certificate] 
): 
    """ 
    Check a node certificate is endorsed by a service certificate, transitively through a list of endorsements. 
    """ 

    cert_i = node_cert 
    for endorsement in endorsements: 
        check_endorsement(cert_i, endorsement) 
        cert_i = endorsement 
    check_endorsement(cert_i, service_cert) 

Als alternatief kunnen we het certificaat ook valideren met behulp van de OpenSSL-bibliotheek met behulp van een vergelijkbare methode.

from OpenSSL.crypto import ( 
    X509, 
    X509Store, 
    X509StoreContext, 
)

def verify_openssl_certificate( 
    node_cert: Certificate, 
    service_cert: Certificate, 
    service_endorsements_certs: List[Certificate], 
) -> None: 
    """Verify that the given node certificate is a valid OpenSSL certificate through 
    the service certificate and a list of endorsements certificates.""" 

    store = X509Store() 

    # pyopenssl does not support X509_V_FLAG_NO_CHECK_TIME. For recovery of expired 
    # services and historical receipts, we want to ignore the validity time. 0x200000 
    # is the bitmask for this option in more recent versions of OpenSSL. 
    X509_V_FLAG_NO_CHECK_TIME = 0x200000 
    store.set_flags(X509_V_FLAG_NO_CHECK_TIME) 

    # Add service certificate to the X.509 store 
    store.add_cert(X509.from_cryptography(service_cert)) 

    # Prepare X.509 endorsement certificates 
    certs_chain = [X509.from_cryptography(cert) for cert in service_endorsements_certs] 

    # Prepare X.509 node certificate 
    node_cert_pem = X509.from_cryptography(node_cert) 

    # Create X.509 store context and verify its certificate 
    ctx = X509StoreContext(store, node_cert_pem, certs_chain) 
    ctx.verify_certificate() 

Voorbeeldcode

De volledige voorbeeldcode die in het code-overzicht wordt gebruikt, wordt verstrekt.

Hoofdprogramma

import json 

# Use the receipt and the service identity to verify the receipt content 
with open("network_certificate.pem", "r") as service_certificate_file, open( 
    "receipt.json", "r" 
) as receipt_file: 

    # Load relevant files content 
    receipt = json.loads(receipt_file.read())["receipt"]
    service_certificate_cert = service_certificate_file.read()

    try: 
        verify_receipt(receipt, service_certificate_cert) 
        print("Receipt verification succeeded") 

    except Exception as e: 
        print("Receipt verification failed") 

        # Raise caught exception to look at the error stack 
        raise e 

Ontvangstbevestiging

from cryptography.x509 import load_pem_x509_certificate, Certificate 
from hashlib import sha256 
from typing import Dict, List, Any 

from OpenSSL.crypto import ( 
    X509, 
    X509Store, 
    X509StoreContext, 
) 

from ccf.receipt import root, verify, check_endorsements 

def verify_receipt(receipt: Dict[str, Any], service_cert_pem: str) -> None: 
    """Function to verify that a given write transaction receipt is valid based 
    on its content and the service certificate. 
    Throws an exception if the verification fails.""" 

    # Check that all the fields are present in the receipt 
    assert "cert" in receipt 
    assert "leafComponents" in receipt 
    assert "claimsDigest" in receipt["leafComponents"] 
    assert "commitEvidence" in receipt["leafComponents"] 
    assert "writeSetDigest" in receipt["leafComponents"] 
    assert "proof" in receipt 
    assert "signature" in receipt 

    # Set the variables 
    node_cert_pem = receipt["cert"] 
    claims_digest_hex = receipt["leafComponents"]["claimsDigest"] 
    commit_evidence_str = receipt["leafComponents"]["commitEvidence"] 

    write_set_digest_hex = receipt["leafComponents"]["writeSetDigest"] 
    proof_list = receipt["proof"] 
    service_endorsements_certs_pem = receipt.get("serviceEndorsements", [])
    root_node_signature = receipt["signature"] 

    # Load service and node PEM certificates
    service_cert = load_pem_x509_certificate(service_cert_pem.encode()) 
    node_cert = load_pem_x509_certificate(node_cert_pem.encode()) 

    # Load service endorsements PEM certificates
    service_endorsements_certs = [ 
        load_pem_x509_certificate(pem.encode()) 
        for pem in service_endorsements_certs_pem 
    ] 

    # Compute leaf of the Merkle Tree 
    leaf_node_hex = compute_leaf_node( 
        claims_digest_hex, commit_evidence_str, write_set_digest_hex 
    ) 

    # Compute root of the Merkle Tree
    root_node = root(leaf_node_hex, proof_list) 

    # Verify signature of the signing node over the root of the tree
    verify(root_node, root_node_signature, node_cert) 

    # Verify node certificate is endorsed by the service certificates through endorsements
    check_endorsements(node_cert, service_cert, service_endorsements_certs) 

    # Alternative: Verify node certificate is endorsed by the service certificates through endorsements 
    verify_openssl_certificate(node_cert, service_cert, service_endorsements_certs) 

def compute_leaf_node( 
    claims_digest_hex: str, commit_evidence_str: str, write_set_digest_hex: str 
) -> str: 
    """Function to compute the leaf node associated to a transaction 
    given its claims digest, commit evidence, and write set digest.""" 

    # Digest commit evidence string
    commit_evidence_digest = sha256(commit_evidence_str.encode()).digest() 

    # Convert write set digest to bytes
    write_set_digest = bytes.fromhex(write_set_digest_hex) 

    # Convert claims digest to bytes
    claims_digest = bytes.fromhex(claims_digest_hex) 

    # Create leaf node by hashing the concatenation of its three components 
    # as bytes objects in the following order: 
    # 1. write_set_digest 
    # 2. commit_evidence_digest 
    # 3. claims_digest 
    leaf_node_digest = sha256( 
        write_set_digest + commit_evidence_digest + claims_digest 
    ).digest() 

    # Convert the result into a string of hexadecimal digits 
    return leaf_node_digest.hex() 

def verify_openssl_certificate( 
    node_cert: Certificate, 
    service_cert: Certificate, 
    service_endorsements_certs: List[Certificate], 
) -> None: 
    """Verify that the given node certificate is a valid OpenSSL certificate through 
    the service certificate and a list of endorsements certificates.""" 

    store = X509Store() 

    # pyopenssl does not support X509_V_FLAG_NO_CHECK_TIME. For recovery of expired 
    # services and historical receipts, we want to ignore the validity time. 0x200000 
    # is the bitmask for this option in more recent versions of OpenSSL. 
    X509_V_FLAG_NO_CHECK_TIME = 0x200000 
    store.set_flags(X509_V_FLAG_NO_CHECK_TIME) 

    # Add service certificate to the X.509 store
    store.add_cert(X509.from_cryptography(service_cert)) 

    # Prepare X.509 endorsement certificates
    certs_chain = [X509.from_cryptography(cert) for cert in service_endorsements_certs] 

    # Prepare X.509 node certificate
    node_cert_pem = X509.from_cryptography(node_cert) 

    # Create X.509 store context and verify its certificate
    ctx = X509StoreContext(store, node_cert_pem, certs_chain) 
    ctx.verify_certificate() 

Volgende stappen