Udostępnij za pośrednictwem


Weryfikowanie potwierdzeń transakcji zapisu poufnych rejestru platformy Azure

Potwierdzenie zapisu poufnego rejestru platformy Azure reprezentuje kryptograficzny dowód Merkle, że odpowiednia transakcja zapisu została globalnie zatwierdzona przez sieć CCF. Użytkownicy rejestru poufnego platformy Azure mogą uzyskać potwierdzenie transakcji zapisu zatwierdzonego w dowolnym momencie, aby sprawdzić, czy odpowiednia operacja zapisu została pomyślnie zarejestrowana w niezmienialnym rejestrze.

Aby uzyskać więcej informacji na temat potwierdzeń dotyczących zapisu transakcji poufnych rejestru platformy Azure, zobacz dedykowany artykuł.

Kroki weryfikacji potwierdzenia

Potwierdzenie transakcji zapisu można zweryfikować zgodnie z określonym zestawem kroków opisanych w poniższych podsekcjach. Te same kroki opisano w dokumentacji programu CCF.

Obliczenia węzła liścia

Pierwszym krokiem jest obliczenie skrótu SHA-256 węzła liścia w drzewie Merkle odpowiadającym zatwierdzonej transakcji. Węzeł liścia składa się z uporządkowanego łączenia następujących pól, które można znaleźć w paragonie poufnej księgi platformy Azure w obszarze leafComponents:

  1. writeSetDigest
  2. Skrót SHA-256 commitEvidence
  3. claimsDigest Pola

Te wartości muszą być łączone jako tablice bajtów: oba writeSetDigest claimsDigest te wartości muszą zostać przekonwertowane z ciągów cyfr szesnastkowych na tablice bajtów. Z drugiej strony commitEvidence skrót (jako tablica bajtów) można uzyskać, stosując funkcję skrótu SHA-256 w ciągu zakodowanym za pomocą zakodowanego commitEvidence ciągu UTF-8.

Podobnie skrót węzła liścia można obliczyć, stosując funkcję skrótu SHA-256 w wyniku łączenia wynikowych bajtów.

Obliczanie węzła głównego

Drugim krokiem jest obliczenie skrótu SHA-256 katalogu głównego drzewa Merkle w momencie zatwierdzenia transakcji. Obliczenia są wykonywane przez iteracyjne łączenie i wyznaczanie wartości skrótu wyniku poprzedniej iteracji (począwszy od skrótu węzła liścia obliczonego w poprzednim kroku) przy użyciu skrótów uporządkowanych węzłów podanych w proof polu potwierdzenia. Lista proof jest dostarczana jako uporządkowana lista, a jej elementy muszą być iterated w podanej kolejności.

Łączenie należy wykonać na reprezentacji bajtów w odniesieniu do względnej kolejności wskazanej w obiektach podanych w proof polu ( left lub right).

  • Jeśli klucz bieżącego elementu w proof pliku to left, wynik poprzedniej iteracji powinien zostać dołączony do bieżącej wartości elementu.
  • Jeśli klucz bieżącego elementu w proof elemencie to right, wynik poprzedniej iteracji powinien być poprzedzany wartością bieżącego elementu.

Po każdym skontenacji należy zastosować funkcję SHA-256, aby uzyskać dane wejściowe dla następnej iteracji. Ten proces jest zgodny ze standardowymi krokami, aby obliczyć węzeł główny struktury danych Merkle Tree , biorąc pod uwagę wymagane węzły do obliczeń.

Weryfikowanie podpisu za pośrednictwem węzła głównego

Trzecim krokiem jest sprawdzenie, czy sygnatura kryptograficzna utworzona za pośrednictwem skrótu węzła głównego jest prawidłowa przy użyciu certyfikatu węzła podpisywania w potwierdzeniu. Proces weryfikacji jest zgodny ze standardowymi krokami weryfikacji podpisu cyfrowego dla komunikatów podpisanych przy użyciu algorytmu podpisu cyfrowego Elliptic Curve (ECDSA). W szczególności kroki są następujące:

  1. Zdekoduj ciąg signature base64 do tablicy bajtów.
  2. Wyodrębnij klucz publiczny ECDSA z certyfikatu certwęzła podpisywania .
  3. Sprawdź, czy podpis w katalogu głównym drzewa Merkle (obliczony przy użyciu instrukcji w poprzedniej podsekcji) jest uwierzytelniony przy użyciu wyodrębnionego klucza publicznego z poprzedniego kroku. Ten krok skutecznie odpowiada standardowemu procesowi weryfikacji podpisu cyfrowego przy użyciu ecDSA. Istnieje wiele bibliotek w najpopularniejszych językach programowania, które umożliwiają weryfikowanie podpisu ECDSA przy użyciu certyfikatu klucza publicznego za pośrednictwem niektórych danych (na przykład biblioteki kryptografii języka Python).

Weryfikowanie poręczenia certyfikatu węzła podpisywania

Oprócz poprzedniego kroku wymagane jest również sprawdzenie, czy certyfikat węzła podpisywania jest zatwierdzony (tj. podpisany) przez bieżący certyfikat rejestru. Ten krok nie zależy od pozostałych trzech poprzednich kroków i może być wykonywany niezależnie od innych.

Istnieje możliwość, że bieżąca tożsamość usługi, która wystawiła potwierdzenie, różni się od tożsamości, która zatwierdziła węzeł podpisywania (na przykład ze względu na odnowienie certyfikatu). W takim przypadku wymagane jest zweryfikowanie łańcucha zaufania certyfikatów z certyfikatu węzła podpisywania (czyli cert pola paragonu) do zaufanego głównego urzędu certyfikacji (ca) (czyli bieżącego certyfikatu tożsamości usługi) za pośrednictwem innych poprzednich tożsamości usługi (czyli serviceEndorsements pola listy w paragonie). Lista serviceEndorsements jest udostępniana jako uporządkowana lista od najstarszej do najnowszej tożsamości usługi.

Potwierdzenie certyfikatu musi zostać zweryfikowane dla całego łańcucha i jest zgodne z dokładnie tym samym procesem weryfikacji podpisu cyfrowego opisanym w poprzedniej podsekcji. Istnieją popularne biblioteki kryptograficzne typu open source (na przykład OpenSSL), które mogą być zwykle używane do przeprowadzania kroku poręczenia certyfikatu.

Weryfikowanie skrótu oświadczeń aplikacji

W ramach opcjonalnego kroku oświadczenia aplikacji są dołączane do potwierdzenia, można obliczyć skrót oświadczeń z uwidocznionych oświadczeń (zgodnie z określonym algorytmem) i sprawdzić, czy skrót pasuje claimsDigest do zawartego w ładunku paragonu. Aby obliczyć skrót z uwidocznionych obiektów oświadczeń, należy wykonać iterację po każdym obiekcie oświadczenia aplikacji na liście i sprawdzić jego kind pole.

Jeśli obiekt oświadczenia jest rodzaju LedgerEntry, identyfikator kolekcji rejestru (collectionId) i zawartość (contents) oświadczenia powinny zostać wyodrębnione i użyte do obliczenia skrótów HMAC przy użyciu klucza tajnego (secretKey) określonego w obiekcie oświadczenia. Te dwa skróty są następnie łączone, a skrót SHA-256 łączenia jest obliczany. Protokół (protocol) i wynikowy skrót danych oświadczenia są następnie łączone, a kolejny skrót SHA-256 łączenia jest obliczany w celu uzyskania końcowego skrótu.

Jeśli obiekt oświadczenia jest rodzaju ClaimDigest, skrót oświadczenia (value) powinien zostać wyodrębniony, połączony z protokołem (protocol), a skrót SHA-256 łączenia jest obliczany w celu uzyskania końcowego skrótu.

Po obliczeniu każdego pojedynczego skrótu oświadczenia należy połączyć wszystkie obliczone skróty z każdego obiektu oświadczenia aplikacji (w tej samej kolejności, w której są prezentowane w potwierdzeniu). Łączenie powinno być następnie poprzedzane liczbą przetworzonych oświadczeń. Skrót SHA-256 poprzedniej łączenia generuje ostateczny skrót oświadczeń, który powinien być zgodny z wartością obecną claimsDigest w obiekcie paragonu.

Więcej zasobów

Aby uzyskać więcej informacji na temat zawartości potwierdzenia transakcji zapisu poufnej księgi platformy Azure i wyjaśnienia każdego pola, zobacz dedykowany artykuł. Dokumentacja programu CCF zawiera również więcej informacji na temat weryfikacji potwierdzenia i innych powiązanych zasobów pod następującymi linkami:

Weryfikowanie potwierdzeń transakcji zapisu

Narzędzia weryfikacji potwierdzenia

Biblioteka klienta poufnej księgi platformy Azure dla języka Python udostępnia funkcje narzędzi umożliwiające weryfikowanie potwierdzeń transakcji zapisu i obliczanie skrótów oświadczeń z listy oświadczeń aplikacji. Aby uzyskać więcej informacji na temat korzystania z zestawu SDK płaszczyzny danych i narzędzi specyficznych dla paragonów, zobacz tę sekcję i ten przykładowy kod.

Konfigurowanie i wymagania wstępne

W celach referencyjnych udostępniamy przykładowy kod w języku Python, aby w pełni zweryfikować potwierdzenia transakcji zapisu poufnych rejestru platformy Azure, wykonując kroki opisane w poprzedniej sekcji.

Aby uruchomić pełny algorytm weryfikacji, wymagany jest bieżący certyfikat sieci usługi i potwierdzenie transakcji zapisu z uruchomionego zasobu Poufne rejestry. Zapoznaj się z tym artykułem, aby uzyskać szczegółowe informacje na temat pobierania potwierdzenia transakcji zapisu i certyfikatu usługi z wystąpienia poufnego rejestru.

Przewodnik po kodzie

Poniższy kod może służyć do inicjowania wymaganych obiektów i uruchamiania algorytmu weryfikacji potwierdzenia. Oddzielne narzędzie (verify_receipt) służy do uruchamiania pełnego algorytmu weryfikacji i akceptuje zawartość receipt pola w GET_RECEIPT odpowiedzi jako słownik i certyfikat usługi jako prosty ciąg. Funkcja zgłasza wyjątek, jeśli potwierdzenie nie jest prawidłowe lub wystąpił błąd podczas przetwarzania.

Zakłada się, że zarówno potwierdzenie, jak i certyfikat usługi można załadować z plików. Pamiętaj, aby zaktualizować zarówno stałe, jak service_certificate_file_name i receipt_file_name przy użyciu odpowiednich nazw plików certyfikatu usługi i potwierdzenia, które chcesz zweryfikować.

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 

Ponieważ proces weryfikacji wymaga niektórych typów pierwotnych kryptograficznych i skrótów, następujące biblioteki są używane do ułatwienia obliczeń.

  • Biblioteka języka Python CCF: moduł udostępnia zestaw narzędzi do weryfikacji potwierdzenia.
  • Biblioteka kryptograficzna języka Python: powszechnie używana biblioteka zawierająca różne algorytmy kryptograficzne i typy pierwotne.
  • Moduł hashlib, część standardowej biblioteki języka Python: moduł, który udostępnia wspólny interfejs dla popularnych algorytmów tworzenia skrótów.
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 

verify_receipt Wewnątrz funkcji sprawdzamy, czy podane potwierdzenie jest prawidłowe i zawiera wszystkie wymagane pola.

# 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 

Zainicjujemy zmienne, które będą używane w pozostałej części programu.

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

Certyfikaty PEM dla tożsamości usługi, węzła podpisywania i certyfikatów poręczenia można załadować z poprzednich tożsamości usługi przy użyciu biblioteki kryptograficznej.

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

Pierwszym krokiem procesu weryfikacji jest obliczenie skrótu węzła liścia.

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

Funkcja compute_leaf_node przyjmuje jako parametry składników liścia potwierdzenia ( claimsDigest, , commitEvidencei writeSetDigest) i zwraca skrót węzła liścia w postaci szesnastkowej.

Jak opisano wcześniej, obliczamy skrót commitEvidence (przy użyciu funkcji SHA-256 hashlib ). Następnie konwertujemy wartości i writeSetDigest claimsDigest na tablice bajtów. Na koniec łączymy trzy tablice i szyfrujemy wynik przy użyciu funkcji SHA256.

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

Po obliczeniu liścia możemy obliczyć pierwiastek drzewa Merkle.

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

Używamy funkcji root udostępnionej w ramach biblioteki języka Python CCF. Funkcja z kolei łączy wynik poprzedniej iteracji z nowym elementem z proof, szyfruje łączenie, a następnie powtarza krok dla każdego elementu z proof wcześniej obliczonym skrótem. Połączenie musi uwzględniać kolejność węzłów w drzewie Merkle, aby upewnić się, że katalog główny został poprawnie skompilowany.

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

Po obliczeniu skrótu węzła głównego możemy zweryfikować podpis zawarty w paragonie w katalogu głównym, aby sprawdzić, czy podpis jest poprawny.

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

Podobnie biblioteka CCF udostępnia funkcję verify do wykonania tej weryfikacji. Używamy klucza publicznego ECDSA certyfikatu węzła podpisywania, aby zweryfikować podpis w katalogu głównym drzewa.

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

Ostatni krok weryfikacji potwierdzenia polega na weryfikacji certyfikatu, który został użyty do podpisania katalogu głównego drzewa Merkle.

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

Podobnie możemy użyć narzędzia check_endorsements CCF, aby sprawdzić, czy tożsamość usługi obsługuje węzeł podpisywania. Łańcuch certyfikatów może składać się z poprzednich certyfikatów usługi, dlatego należy sprawdzić, czy poparcie jest stosowane przechodnio, jeśli serviceEndorsements nie jest pustą listą.

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) 

Alternatywnie możemy również zweryfikować certyfikat przy użyciu biblioteki OpenSSL przy użyciu podobnej metody.

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

Przykładowy kod

Podano pełny przykładowy kod używany w przewodniku po kodzie.

Główny 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 

Weryfikacja potwierdzenia

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

Następne kroki