Az Azure Confidential Ledger írási tranzakcióinak nyugtáinak ellenőrzése

Az Azure Confidential Ledger írási tranzakciójának nyugtája egy kriptográfiai Merkle-bizonyíték arra vonatkozóan, hogy a megfelelő írási tranzakciót globálisan lekötötte a CCF-hálózat. Az Azure Confidential Ledger felhasználói bármikor megkaphatják a nyugtát egy lekötött írási tranzakcióról annak ellenőrzéséhez, hogy a megfelelő írási művelet sikeresen fel lett-e jegyezve a nem módosítható főkönyvbe.

Az Azure Confidential Ledger írási tranzakcióinak nyugtáival kapcsolatos további információkért tekintse meg a dedikált cikket.

Visszaigazolás-ellenőrzési lépések

Az írási tranzakció nyugtái az alábbi alszakaszokban ismertetett lépések meghatározott halmazát követve ellenőrizhetők. Ugyanezeket a lépéseket a CCF dokumentációja ismerteti.

Levélcsomópont számítása

Az első lépés a lekötött tranzakciónak megfelelő levélcsomópont SHA-256 kivonatának kiszámítása a Merkle-fában. A levélcsomópont a következő mezők rendezett összefűzéséből áll, amelyek egy Azure Confidential Ledger-visszaigazolásban találhatók, a következő alatt leafComponents:

  1. writeSetDigest
  2. SHA-256 kivonat commitEvidence
  3. claimsDigest Mezők

Ezeket az értékeket bájtok tömbjeként kell összefűzni: mindkettőt writeSetDigestclaimsDigest hexadecimális számjegyek sztringjeiből bájttömbökké kell konvertálni; másrészt commitEvidence az SHA-256 kivonatfüggvény UTF-8 kódolt commitEvidence sztringre való alkalmazásával is lekérhető.

Hasonlóképpen a levélcsomópont kivonatolási kivonata is kiszámítható az SHA-256 kivonatfüggvény alkalmazásával az eredményül kapott bájtok összefűzésével.

Gyökércsomópont számítása

A második lépés a Merkle-fa gyökerének SHA-256 kivonatának kiszámítása a tranzakció véglegesítésekor. A számítás az előző iteráció eredményének iteratív összefűzésével és kivonatolásával történik (az előző lépésben kiszámított levélcsomópont kivonatától kezdve) a megrendelt csomópontok nyugtamezőjében proof megadott kivonataival. A proof lista rendezett listaként van megadva, és elemeit a megadott sorrendben kell megszűrni.

Az összefűzést a bájtok ábrázolására kell elvégezni a mezőben megadott objektumokban megadott proof relatív sorrend alapján (vagy right).left

  • Ha az aktuális elem proof kulcsa az left, akkor az előző iteráció eredményét hozzá kell fűzni az aktuális elem értékéhez.
  • Ha az aktuális elem proof kulcsa az right, akkor az előző iteráció eredményét az aktuális elem értékére kell elővenni.

Minden összefűzés után az SHA-256 függvényt kell alkalmazni a következő iteráció bemenetének lekéréséhez. Ez a folyamat a Merkle Tree-adatstruktúra gyökércsomópontjának kiszámításához szükséges szokásos lépéseket követi, figyelembe véve a számításhoz szükséges csomópontokat.

Aláírás ellenőrzése a gyökércsomóponton

A harmadik lépés annak ellenőrzése, hogy a gyökércsomópont kivonatán létrehozott titkosítási aláírás érvényes-e az aláíró csomópont tanúsítványával a visszaigazolásban. Az ellenőrzési folyamat a digitális aláírás ellenőrzésének szabványos lépéseit követi az elliptikus görbe digitális aláírási algoritmusával (ECDSA) aláírt üzenetek esetében. Pontosabban a lépések a következők:

  1. A base64 sztringet signature bájtok tömbjeként dekódolja.
  2. Bontsa ki az ECDSA nyilvános kulcsát az aláíró csomópont tanúsítványából cert.
  3. Ellenőrizze, hogy a Merkle-fa gyökerén (az előző alszakasz utasításai alapján kiszámított) aláírás hiteles-e az előző lépésből kinyert nyilvános kulcs használatával. Ez a lépés hatékonyan megfelel az ECDSA-t használó szabványos digitális aláírás-ellenőrzési folyamatnak. Számos kódtár található a legnépszerűbb programozási nyelvekben, amelyek lehetővé teszik az ECDSA-aláírás ellenőrzését nyilvános kulcsú tanúsítvány használatával bizonyos adatokon keresztül (például a Python titkosítási könyvtárában ).

Az aláíró csomópont tanúsítványának ellenőrzése

Az előző lépésen kívül azt is ellenőriznie kell, hogy az aláíró csomópont tanúsítványát az aktuális főkönyvtanúsítvány támogatja-e (azaz aláírta). Ez a lépés nem függ az előző három lépéstől, és a többitől függetlenül végrehajtható.

Lehetséges, hogy a nyugtát kiállító aktuális szolgáltatásidentitás eltér az aláíró csomópontot jóváhagyótól (például tanúsítványmegújítás miatt). Ebben az esetben a tanúsítványláncot az aláíró csomópont tanúsítványától (azaz cert a nyugta mezőjétől) a megbízható főtanúsítványig (azaz az aktuális szolgáltatásidentitás-tanúsítványig) kell ellenőrizni más korábbi szolgáltatásidentitásokon keresztül (azaz a serviceEndorsements nyugta listamezőjétől). A serviceEndorsements lista rendezett listaként van megadva a legrégebbitől a legújabb szolgáltatásazonosítóig.

A tanúsítványhitelesítést a teljes láncra vonatkozóan ellenőrizni kell, és pontosan ugyanazt a digitális aláírás-ellenőrzési folyamatot követi, amelyet az előző alszakaszban vázoltak fel. Vannak népszerű nyílt forráskódú titkosítási kódtárak (például OpenSSL), amelyek általában a tanúsítvány-jóváhagyási lépés végrehajtására használhatók.

Alkalmazásjogcímek kivonatának ellenőrzése

Opcionális lépésként abban az esetben, ha az alkalmazásjogcímek egy nyugtához vannak csatolva, a jogcímek kivonatát kiszámíthatja a közzétett jogcímekből (egy adott algoritmust követve), és ellenőrizheti, hogy az kivonat megegyezik-e a claimsDigest nyugta hasznos adataiban található adatokkal. A közzétett jogcímobjektumokból származó kivonat kiszámításához iterálnia kell a listában szereplő összes alkalmazásjogcím-objektumot, és ellenőriznie kell a mezőjét kind .

Ha a jogcím objektuma fajta LedgerEntry, akkor a jogcím főkönyvgyűjtemény-azonosítóját (collectionId) és tartalmát (contents) ki kell nyerni, és a jogcímobjektumban megadott titkos kulcs (secretKey) használatával kell kiszámítani a HMAC-kivonatokat. Ezt a két kivonatot a rendszer összefűzi, és kiszámítja az összefűzés SHA-256 kivonatát. A protokoll (protocol) és az eredményül kapott jogcímadatok kivonata összefűzve lesz, és az összefűzés egy másik SHA-256 kivonata lesz kiszámítva a végső kivonat lekéréséhez.

Ha a jogcím objektuma fajta ClaimDigest, a jogcím-kivonatot (value) ki kell nyerni, összefűzve a protokolllal (protocol), és az összefűzés SHA-256 kivonatát ki kell számítani a végső kivonat lekéréséhez.

Az egyes jogcím-kivonatok számítása után össze kell fűzni az összes számítási kivonatot az egyes alkalmazáskérelem-objektumokból (ugyanabban a sorrendben, mint a visszaigazolásban). Az összefűzést ezután a feldolgozott jogcímek számával kell előre felerősíteni. Az előző összefűzés SHA-256 kivonata létrehozza a végleges jogcím-kivonatot, amelynek meg kell egyeznie a claimsDigest nyugtaobjektumban lévő jelenel.

More resources

Az Azure Confidential Ledger írási tranzakció visszaigazolásának tartalmával és az egyes mezők magyarázatával kapcsolatos további információkért tekintse meg a dedikált cikket. A CCF dokumentációja további információt is tartalmaz a nyugta-ellenőrzésről és más kapcsolódó erőforrásokról az alábbi hivatkozásokon:

Tranzakciós visszaigazolások írásának ellenőrzése

Nyugta-ellenőrző segédprogramok

A Pythonhoz készült Azure Confidential Ledger ügyfélkódtár segédprogramfüggvényeket biztosít az írási tranzakciók nyugtáinak ellenőrzéséhez és a jogcímek kivonatának kiszámításához az alkalmazásjogcímek listájából. Az adatsík SDK és a nyugtaspecifikus segédprogramok használatáról ebben a szakaszban és ebben a mintakódban talál további információt.

Telepítés és előfeltételek

Referenciaként a Pythonban mintakódot biztosítunk az Azure Confidential Ledger írási tranzakcióinak nyugtáinak teljes ellenőrzéséhez az előző szakaszban ismertetett lépések követésével.

A teljes ellenőrzési algoritmus futtatásához szükség van az aktuális szolgáltatás hálózati tanúsítványára és egy futó bizalmas könyvelési erőforrásból származó írási tranzakció nyugtára. Ebből a cikkből megtudhatja, hogyan kér le egy írási tranzakció nyugtát és a szolgáltatástanúsítványt egy bizalmas főkönyvi példányból.

Kódbemutató

Az alábbi kód a szükséges objektumok inicializálására és a visszaigazolás-ellenőrzési algoritmus futtatására használható. A teljes ellenőrzési algoritmus futtatásához külön segédprogram (verify_receipt) használható, amely a válaszban GET_RECEIPT a mező tartalmát receipt szótárként, a szolgáltatástanúsítványt pedig egyszerű sztringként fogadja el. A függvény kivételt okoz, ha a nyugta érvénytelen, vagy hiba történt a feldolgozás során.

Feltételezzük, hogy a nyugta és a szolgáltatástanúsítvány is betölthető fájlokból. Mindenképpen frissítse az állandókat és receipt_file_name az service_certificate_file_name állandókat az ellenőrizni kívánt szolgáltatástanúsítvány és nyugta megfelelő fájlneveivel.

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 

Mivel az ellenőrzési folyamat titkosítási és kivonatolási primitíveket igényel, a következő kódtárak segítik a számítást.

  • A CCF Python-kódtár: a modul számos eszközt biztosít a nyugta-ellenőrzéshez.
  • Python-titkosítási kódtár: széles körben használt kódtár, amely különböző titkosítási algoritmusokat és primitíveket tartalmaz.
  • A hashlib modul, amely a Python standard kódtár része: egy modul, amely közös felületet biztosít a népszerű kivonatoló algoritmusokhoz.
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 

A függvényen verify_receipt belül ellenőrizzük, hogy a megadott visszaigazolás érvényes-e, és tartalmazza-e az összes szükséges mezőt.

# 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 

Inicializáljuk a program többi részében használni kívánt változókat.

# 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"] 

A titkosítási kódtár használatával betölthetjük a szolgáltatásidentitás PEM-tanúsítványait, az aláíró csomópontot és a korábbi szolgáltatásidentitások jóváhagyási tanúsítványait.

# 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 
] 

Az ellenőrzési folyamat első lépése a levélcsomópont kivonatának kiszámítása.

# 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 
)

A compute_leaf_node függvény paraméterként fogadja el a nyugta levélösszetevőit (a claimsDigest, a commitEvidenceés a writeSetDigest) és hexadecimális formában adja vissza a levélcsomópont kivonatát.

A korábban részletezett módon kiszámítjuk a kivonatot commitEvidence (az SHA-256 hashlib függvény használatával). Ezt követően bájtok tömbjeivé alakítjuk át mindkettőt writeSetDigestclaimsDigest . Végül összefűzzük a három tömböt, és az eredményt az SHA256 függvénnyel emésztjük fel.

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() 

A levél számítása után kiszámíthatjuk a Merkle fa gyökerét.

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

A CCF Python-kódtár részeként megadott függvényt root használjuk. A függvény egymás után összefűzi az előző iteráció eredményét egy új elemmel proof, megemészti az összefűzést, majd megismétli a korábban kiszámított kivonat minden elemének proof lépését. Az összefűzésnek tiszteletben kell tartania a Merkle-fában lévő csomópontok sorrendjét, hogy a gyökér megfelelően legyen újrafordítva.

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() 

A gyökércsomópont kivonatának kiszámítása után ellenőrizheti a visszaigazolásban található aláírást a gyökéren keresztül, hogy ellenőrizze, hogy az aláírás helyes-e.

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

Hasonlóképpen a CCF-kódtár egy függvényt verify is biztosít az ellenőrzés elvégzéséhez. Az aláíró csomópont tanúsítványának ECDSA nyilvános kulcsával ellenőrizzük az aláírást a fa gyökerén.

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())), 
    )

A visszaigazolás ellenőrzésének utolsó lépése a Merkle-fa gyökerének aláírásához használt tanúsítvány érvényesítése.

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

Hasonlóképpen a CCF segédprogrammal check_endorsements ellenőrizheti, hogy a szolgáltatásidentitás támogatja-e az aláíró csomópontot. A tanúsítványlánc korábbi szolgáltatástanúsítványokból állhat, ezért ellenőrizni kell, hogy az ellenőrzés tranzitív módon van-e alkalmazva, ha serviceEndorsements nem üres lista.

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) 

Másik lehetőségként a tanúsítványt az OpenSSL-kódtár hasonló módszerrel történő használatával is érvényesíthetjük.

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() 

Mintakód

A kódbemutatóban használt teljes mintakód meg van adva.

Fő program

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 

Nyugta ellenőrzése

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() 

Következő lépések