Ricevute delle transazioni di scrittura del libro mastro riservato di Azure

Per applicare le garanzie di integrità delle transazioni, un libro mastro riservato di Azure usa una struttura di dati dell'albero Merkle per registrare l'hash di tutti i blocchi di transazioni aggiunti al libro mastro non modificabile. Dopo il commit di una transazione di scrittura, gli utenti del libro mastro riservato di Azure possono ottenere una prova di merkle crittografica o ricevuta, sulla voce prodotta in un libro mastro riservato per verificare che l'operazione di scrittura sia stata salvata correttamente. Una ricevuta di transazione di scrittura è la prova che il sistema ha eseguito il commit della transazione corrispondente e può essere usato per verificare che la voce sia stata effettivamente aggiunta al libro mastro.

Altre informazioni sull'uso di un albero Merkle in un libro mastro riservato sono disponibili nella documentazione CCF.

Ottenere le ricevute delle transazioni di scrittura

Configurazione e prerequisiti

Gli utenti del libro mastro riservato di Azure possono ottenere una ricevuta per una transazione specifica usando la libreria client del libro mastro riservato di Azure. L'esempio seguente illustra come ottenere una ricevuta di scrittura usando la libreria client per Python, ma i passaggi sono gli stessi con qualsiasi altro SDK supportato per il libro mastro riservato di Azure.

Si presuppone che sia già stata creata una risorsa Libro mastro riservato usando la libreria di gestione del libro mastro riservato di Azure. Se non si dispone ancora di una risorsa libro mastro esistente, crearne una usando le istruzioni seguenti.

Procedura dettagliata per il codice

Per iniziare, configurare le importazioni per il programma Python.

import json 

# Import the Azure authentication library 
from azure.identity import DefaultAzureCredential 

# Import the Confidential Ledger Data Plane SDK 
from azure.confidentialledger import ConfidentialLedgerClient 
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient 

Di seguito sono riportati i valori costanti usati per configurare il client del libro mastro riservato di Azure. Assicurarsi di aggiornare la ledger_name costante con il nome univoco della risorsa Libro mastro riservato.

# Constants for our program 
ledger_name = "<your-unique-ledger-name>" 
identity_url = "https://identity.confidential-ledger.core.azure.com" 
ledger_url = "https://" + ledger_name + ".confidential-ledger.azure.com" 

Viene eseguita l'autenticazione usando la classe DefaultAzureCredential.

# Setup authentication 
credential = DefaultAzureCredential() 

Viene quindi ottenuto e salvato il certificato del servizio Libro mastro riservato usando il client certificato dall'URL dell'identità del libro mastro riservato. Il certificato del servizio è un certificato di chiave pubblica dell'identità di rete usato come radice di attendibilità per l'autenticazione del server TLS . In altre parole, viene usato come autorità di certificazione (CA) per stabilire una connessione TLS con uno qualsiasi dei nodi nella rete CCF.

# Create a Certificate client and use it to 
# get the service identity for our ledger 
identity_client = ConfidentialLedgerCertificateClient(identity_url) 
network_identity = identity_client.get_ledger_identity( 
     ledger_id=ledger_name 
)

# Save network certificate into a file for later use 
ledger_tls_cert_file_name = "network_certificate.pem" 

with open(ledger_tls_cert_file_name, "w") as cert_file: 
    cert_file.write(network_identity["ledgerTlsCertificate"]) 

Successivamente, è possibile usare le credenziali, il certificato di rete recuperato e l'URL del libro mastro univoco per creare un client libro mastro riservato.

# Create Confidential Ledger client 
ledger_client = ConfidentialLedgerClient( 
     endpoint=ledger_url,  
     credential=credential, 
     ledger_certificate_path=ledger_tls_cert_file_name 
) 

Usando il client Confidential Ledger, è possibile eseguire qualsiasi operazione supportata in un'istanza di Libro mastro riservato di Azure. Ad esempio, è possibile aggiungere una nuova voce al libro mastro e attendere il commit della transazione di scrittura corrispondente.

# The method begin_create_ledger_entry returns a poller that  
# we can use to wait for the transaction to be committed 
create_entry_poller = ledger_client.begin_create_ledger_entry( 
    {"contents": "Hello World!"} 
)

create_entry_result = create_entry_poller.result() 

Dopo il commit della transazione, è possibile usare il client per ottenere una ricevuta sulla voce aggiunta al libro mastro nel passaggio precedente usando il rispettivo ID transazione.

# The method begin_get_receipt returns a poller that  
# we can use to wait for the receipt to be available by the system 
get_receipt_poller = ledger_client.begin_get_receipt( 
    create_entry_result["transactionId"] 
)

get_receipt_result = get_receipt_poller.result() 

Codice di esempio

Viene fornito il codice di esempio completo usato nella procedura dettagliata del codice.

import json 

# Import the Azure authentication library 
from azure.identity import DefaultAzureCredential 

# Import the Confidential Ledger Data Plane SDK 
from azure.confidentialledger import ConfidentialLedgerClient 
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient 

from receipt_verification import verify_receipt 

# Constants 
ledger_name = "<your-unique-ledger-name>" 
identity_url = "https://identity.confidential-ledger.core.azure.com" 
ledger_url = "https://" + ledger_name + ".confidential-ledger.azure.com" 

# Setup authentication 
credential = DefaultAzureCredential() 

# Create Ledger Certificate client and use it to 
# retrieve the service identity for our ledger 
identity_client = ConfidentialLedgerCertificateClient(identity_url) 
network_identity = identity_client.get_ledger_identity(ledger_id=ledger_name) 

# Save network certificate into a file for later use 
ledger_tls_cert_file_name = "network_certificate.pem" 

with open(ledger_tls_cert_file_name, "w") as cert_file: 
    cert_file.write(network_identity["ledgerTlsCertificate"]) 

# Create Confidential Ledger client 
ledger_client = ConfidentialLedgerClient( 
    endpoint=ledger_url, 
    credential=credential, 
    ledger_certificate_path=ledger_tls_cert_file_name, 
) 

# The method begin_create_ledger_entry returns a poller that 
# we can use to wait for the transaction to be committed 
create_entry_poller = ledger_client.begin_create_ledger_entry( 
    {"contents": "Hello World!"} 
) 
create_entry_result = create_entry_poller.result() 

# The method begin_get_receipt returns a poller that 
# we can use to wait for the receipt to be available by the system 
get_receipt_poller = ledger_client.begin_get_receipt( 
    create_entry_result["transactionId"] 
) 
get_receipt_result = get_receipt_poller.result() 

# Save fetched receipt into a file
with open("receipt.json", "w") as receipt_file: 
    receipt_file.write(json.dumps(get_receipt_result, sort_keys=True, indent=2)) 

Scrivere il contenuto della ricevuta delle transazioni

Di seguito è riportato un esempio di payload di risposta JSON restituito da un'istanza del libro mastro riservato di Azure quando si chiama l'endpoint GET_RECEIPT .

{
    "receipt": {
        "cert": "-----BEGIN CERTIFICATE-----\nMIIB0jCCAXmgAwIBAgIQPxdrEtGY+SggPHETin1XNzAKBggqhkjOPQQDAjAWMRQw\nEgYDVQQDDAtDQ0YgTmV0d29yazAeFw0yMjA3MjAxMzUzMDFaFw0yMjEwMTgxMzUz\nMDBaMBMxETAPBgNVBAMMCENDRiBOb2RlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\nQgAEWy81dFeEZ79gVJnfHiPKjZ54fZvDcFlntFwJN8Wf6RZa3PaV5EzwAKHNfojj\noXT4xNkJjURBN7q+1iE/vvc+rqOBqzCBqDAJBgNVHRMEAjAAMB0GA1UdDgQWBBQS\nwl7Hx2VkkznJNkVZUbZy+TOR/jAfBgNVHSMEGDAWgBTrz538MGI/SdV8k8EiJl5z\nfl3mBTBbBgNVHREEVDBShwQK8EBegjNhcGljY2lvbmUtdGVzdC1sZWRnZXIuY29u\nZmlkZW50aWFsLWxlZGdlci5henVyZS5jb22CFWFwaWNjaW9uZS10ZXN0LWxlZGdl\ncjAKBggqhkjOPQQDAgNHADBEAiAsGawDcYcH/KzF2iK9Ldx/yABUoYSNti2Cyxum\n9RRNKAIgPB/XGh/FQS3nmZLExgBVXkDYdghQu/NCY/hHjQ9AvWg=\n-----END CERTIFICATE-----\n",
        "leafComponents": {
            "claimsDigest": "0000000000000000000000000000000000000000000000000000000000000000",
            "commitEvidence": "ce:2.40:f36ffe2930ec95d50ebaaec26e2bec56835abd051019eb270f538ab0744712a4",
            "writeSetDigest": "8452624d10bdd79c408c0f062a1917aa96711ea062c508c745469636ae1460be"
        },
        "nodeId": "70e995887e3e6b73c80bc44f9fbb6e66b9f644acaddbc9c0483cfc17d77af24f",
        "proof": [
            {
                "left": "b78230f9abb27b9b803a9cae4e4cec647a3be1000fc2241038867792d59d4bc1"
            },
            {
                "left": "a2835d4505b8b6b25a0c06a9c8e96a5204533ceac1edf2b3e0e4dece78fbaf35"
            }
        ],
        "signature": "MEUCIQCjtMqk7wOtUTgqlHlCfWRqAco+38roVdUcRv7a1G6pBwIgWKpCSdBmhzgEdwguUW/Cj/Z5bAOA8YHSoLe8KzrlqK8="
    },
    "state": "Ready",
    "transactionId": "2.40"
}

La risposta JSON contiene i campi seguenti a livello radice.

  • ricevuta: contiene i valori che possono essere utilizzati per verificare la validità della ricevuta per la transazione di scrittura corrispondente.

  • state: stato della risposta JSON restituita. Di seguito sono riportati i valori possibili consentiti:

    • Ready: la ricevuta restituita nella risposta è disponibile
    • Loading: la ricevuta non è ancora disponibile per essere recuperata e la richiesta deve essere ritentata
  • transactionId: ID transazione associato alla ricevuta della transazione di scrittura.

Il receipt campo contiene i campi seguenti.

  • cert: stringa con il certificato di chiave pubblica PEM del nodo CCF che ha firmato la transazione di scrittura. Il certificato di identità del servizio deve sempre approvare il certificato del nodo di firma. Vedere anche altri dettagli sul modo in cui le transazioni vengono firmate regolarmente e sul modo in cui le transazioni di firma vengono aggiunte al libro mastro in CCF al collegamento seguente.

  • nodeId: stringa esadecimale che rappresenta il digest hash SHA-256 della chiave pubblica del nodo CCF di firma.

  • leafComponents: componenti dell'hash del nodo foglia nell'albero Merkle associato alla transazione specificata. Un albero Merkle è una struttura di dati ad albero che registra l'hash di ogni transazione e garantisce l'integrità del libro mastro. Per altre informazioni sull'uso di un albero Merkle in CCF, vedere la documentazione CCF correlata.

  • proof: elenco di coppie chiave-valore che rappresentano gli hash dei nodi Merkle Tree che, se combinati con l'hash del nodo foglia corrispondente alla transazione specificata, consentono la ricalcolazione dell'hash radice dell'albero. Grazie alle proprietà di un albero Merkle, è possibile ricompilare l'hash radice dell'albero solo un subset di nodi. Gli elementi di questo elenco sono sotto forma di coppie chiave-valore: le chiavi indicano la posizione relativa rispetto al nodo padre nell'albero a un determinato livello; i valori sono i digest hash SHA-256 del nodo specificato, come stringhe esadecimali.

  • serviceEndorsements: elenco di stringhe di certificati con codifica PEM che rappresentano i certificati delle identità del servizio precedenti. È possibile che l'identità del servizio che ha approvato il nodo di firma non corrisponda a quella che ha emesso la ricevuta. Ad esempio, il certificato del servizio viene rinnovato dopo un ripristino di emergenza di un libro mastro riservato. L'elenco dei certificati di servizio precedenti consente ai revisori di compilare la catena di attendibilità dal nodo di firma CCF al certificato del servizio corrente.

  • signature: stringa Base64 che rappresenta la firma della radice dell'albero Merkle nella transazione specificata, tramite il nodo CCF di firma.

Il leafComponents campo contiene i campi seguenti.

  • claimsDigest: stringa esadecimale che rappresenta il digest hash SHA-256 dell'attestazione dell'applicazione collegata dall'applicazione Confidential Ledger al momento dell'esecuzione della transazione. Le attestazioni dell'applicazione non sono attualmente supportate perché l'applicazione Confidential Ledger non associa alcuna attestazione durante l'esecuzione di una transazione di scrittura.

  • commitEvidence: stringa univoca prodotta per transazione, derivata dall'ID transazione e dai segreti del libro mastro. Per altre informazioni sull'evidenza di commit, vedere la documentazione CCF correlata.

  • writeSetDigest: stringa esadecimale che rappresenta il digest hash SHA-256 dell'archivio Key-Value, che contiene tutte le chiavi e i valori scritti al momento del completamento della transazione. Per altre informazioni sul set di scrittura, vedere la documentazione CCF correlata.

Attestazioni dell'applicazione

Le applicazioni Libro mastro riservato di Azure possono collegare dati arbitrari, denominati attestazioni dell'applicazione, per scrivere transazioni. Queste attestazioni rappresentano le azioni eseguite durante un'operazione di scrittura. Se associato a una transazione, il digest SHA-256 dell'oggetto attestazioni viene incluso nel libro mastro ed eseguito il commit come parte della transazione di scrittura. L'inclusione dell'attestazione nella transazione di scrittura garantisce che il digest dell'attestazione sia firmato e non possa essere manomesso.

Successivamente, le attestazioni dell'applicazione possono essere rivelate nel formato normale nel payload della ricevuta corrispondente alla stessa transazione in cui sono state aggiunte. Le attestazioni esposte consentono agli utenti di ricompilare lo stesso digest delle attestazioni collegato e connesso dal libro mastro durante la transazione. Il digest delle attestazioni può essere usato come parte del processo di verifica della ricezione delle transazioni di scrittura, offrendo agli utenti un modo offline per verificare completamente l'autenticità delle attestazioni registrate.

Le attestazioni dell'applicazione sono attualmente supportate nella versione 2023-01-18-previewdell'API di anteprima.

Scrivere il contenuto della ricevuta delle transazioni con attestazioni dell'applicazione

Di seguito è riportato un esempio di payload di risposta JSON restituito da un'istanza del libro mastro riservato di Azure che ha registrato le attestazioni dell'applicazione, quando si chiama l'endpoint GET_RECEIPT .

{
  "applicationClaims": [
    {
      "kind": "LedgerEntry",
      "ledgerEntry": {
        "collectionId": "subledger:0",
        "contents": "Hello world",
        "protocol": "LedgerEntryV1",
        "secretKey": "Jde/VvaIfyrjQ/B19P+UJCBwmcrgN7sERStoyHnYO0M="
      }
    }
  ],
  "receipt": {
    "cert": "-----BEGIN CERTIFICATE-----\nMIIBxTCCAUygAwIBAgIRAMR89lUNeIghDUfpyHi3QzIwCgYIKoZIzj0EAwMwFjEU\nMBIGA1UEAwwLQ0NGIE5ldHdvcmswHhcNMjMwNDI1MTgxNDE5WhcNMjMwNzI0MTgx\nNDE4WjATMREwDwYDVQQDDAhDQ0YgTm9kZTB2MBAGByqGSM49AgEGBSuBBAAiA2IA\nBB1DiBUBr9/qapmvAIPm1o3o3LRViSOkfFVI4oPrw3SodLlousHrLz+HIe+BqHoj\n4nBjt0KAS2C0Av6Q+Xg5Po6GCu99GQSoSfajGqmjy3j3bwjsGJi5wHh1pNbPmMm/\nTqNhMF8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUCPaDohOGjVgQ2Lb8Pmubg7Y5\nDJAwHwYDVR0jBBgwFoAU25KejcEmXDNnKvSLUwW/CQZIVq4wDwYDVR0RBAgwBocE\nfwAAATAKBggqhkjOPQQDAwNnADBkAjA8Ci9myzieoLoIy+7mUswVEjUG3wrEXtxA\nDRmt2PK9bTDo2m3aJ4nCQJtCWQRUlN0CMCMOsXL4NnfsSxaG5CwAVkDwLBUPv7Zy\nLfSh2oZ3Wn4FTxL0UfnJeFOz/CkDUtJI1A==\n-----END CERTIFICATE-----\n",
    "leafComponents": {
      "claimsDigest": "d08d8764437d09b2d4d07d52293cddaf40f44a3ea2176a0528819a80002df9f6",
      "commitEvidence": "ce:2.13:850a25da46643fa41392750b6ca03c7c7d117c27ae14e3322873de6322aa7cd3",
      "writeSetDigest": "6637eddb8741ab54cc8a44725be67fd9be390e605f0537e5a278703860ace035"
    },
    "nodeId": "0db9a22e9301d1167a2a81596fa234642ad24bc742451a415b8d653af056795c",
    "proof": [
      {
        "left": "bcce25aa51854bd15257cfb0c81edc568a5a5fa3b81e7106c125649db93ff599"
      },
      {
        "left": "cc82daa27e76b7525a1f37ed7379bb80f6aab99f2b36e2e06c750dd9393cd51b"
      },
      {
        "left": "c53a15cbcc97e30ce748c0f44516ac3440e3e9cc19db0852f3aa3a3d5554dfae"
      }
    ],
    "signature": "MGYCMQClZXVAFn+vflIIikwMz64YZGoH71DKnfMr3LXkQ0lhljSsvDrmtmi/oWwOsqy28PsCMQCMe4n9aXXK4R+vY0SIfRWSCCfaADD6teclFCkVNK4317ep+5ENM/5T/vDJf3V4IvI="
  },
  "state": "Ready",
  "transactionId": "2.13"
}

Rispetto all'esempio di ricevuta illustrato nella sezione precedente, la risposta JSON contiene un altro applicationClaims campo che rappresenta l'elenco delle attestazioni dell'applicazione registrate dal libro mastro durante la transazione di scrittura. Ogni oggetto all'interno dell'elenco applicationClaims contiene i campi seguenti.

  • kind: rappresenta il tipo di attestazione dell'applicazione. Il valore indica come analizzare l'oggetto attestazione dell'applicazione per il tipo specificato.

  • ledgerEntry: rappresenta un'attestazione dell'applicazione derivata dai dati di immissione del libro mastro. L'attestazione conterrà i dati registrati dall'applicazione durante una transazione di scrittura (ad esempio, l'ID raccolta e il contenuto fornito dall'utente) e le informazioni necessarie per calcolare il digest corrispondente all'oggetto attestazione singolo.

  • digest: rappresenta un'attestazione dell'applicazione in formato digerito. Questo oggetto attestazione conterrà il digest pre-calcolato dall'applicazione e il protocollo usato per il calcolo.

Il ledgerEntry campo contiene i campi seguenti.

  • protocollo: rappresenta il protocollo da usare per calcolare il digest di un'attestazione dai dati dell'attestazione specificati.

  • collectionId: identificatore della raccolta scritta durante la transazione di scrittura corrispondente.

  • contents: contenuto del libro mastro scritto durante la transazione di scrittura corrispondente.

  • secretKey: chiave privata con codifica Base64. Questa chiave deve essere usata nell'algoritmo HMAC con i valori forniti nell'attestazione dell'applicazione per ottenere il digest dell'attestazione.

Il digest campo contiene i campi seguenti.

  • protocollo: rappresenta il protocollo usato per calcolare il digest dell'attestazione specificata.

  • value: digest dell'attestazione dell'applicazione, in formato esadecimale. Questo valore deve essere sottoposto a hashing con il protocol valore per calcolare il digest completo dell'attestazione dell'applicazione.

Altre risorse

Per altre informazioni sulle ricevute delle transazioni di scrittura e su come CCF garantisce l'integrità di ogni transazione, vedere i collegamenti seguenti:

Passaggi successivi