Reçus de transaction d’écriture du registre confidentiel Azure
Pour appliquer des garanties d’intégrité des transactions, un registre confidentiel Azure utilise une structure de données en arborescence Merkle pour enregistrer le code de hachage de tous les blocs de transactions ajoutés au registre immuable. Une fois qu’une transaction d’écriture est validée, les utilisateurs du registre confidentiel Azure peuvent obtenir une preuve Merkle de chiffrement, ou reçu, sur l’entrée produite dans un registre confidentiel pour vérifier que l’opération d’écriture a été correctement enregistrée. Un reçu de transaction d’écriture est la preuve que le système a validé la transaction correspondante. Il peut être utilisé pour vérifier que l’entrée a bien été ajoutée au registre.
Pour plus d’informations sur l’utilisation d’une arborescence Merkle dans un registre confidentiel, consultez la documentation du CCF.
Obtenir des reçus de transaction écrite
Configuration et prérequis
Les utilisateurs du registre confidentiel Azure peuvent obtenir un reçu pour une transaction spécifique en utilisant la bibliothèque de client du registre confidentiel Azure. L’exemple suivant montre comment obtenir un reçu d’écriture à l’aide de la bibliothèque de client pour Python, mais les étapes sont les mêmes avec tout autre SDK pris en charge pour le registre confidentiel Azure.
Nous partons du principe qu’une ressource de registre confidentiel a déjà été créée à l’aide de la bibliothèque de gestion du registre confidentiel Azure. Si vous n’avez pas encore de ressource de registre existante, créez-en une en suivant les instructions ci-après.
Vérification du code
Nous commençons par configurer les importations pour notre programme 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
Voici les valeurs constantes utilisées pour configurer le client du registre confidentiel Azure. Veillez à mettre à jour la constante ledger_name
avec le nom unique de votre ressource de registre confidentiel.
# 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"
Nous nous authentifions avec la classe DefaultAzureCredential.
# Setup authentication
credential = DefaultAzureCredential()
Ensuite, nous obtenons et enregistrons le certificat de service du registre confidentiel à l’aide du client de certificat à partir de l’URL d’identité du registre confidentiel. Le certificat de service est un certificat de clé publique d’identité réseau utilisé comme racine d’approbation pour l’authentification du serveur TLS. En d’autres termes, il est utilisé comme autorité de certification pour établir une connexion TLS avec l’un des nœuds du réseau 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"])
Ensuite, nous pouvons utiliser nos informations d’identification, le certificat réseau extrait et notre URL de registre unique pour créer un client de registre confidentiel.
# Create Confidential Ledger client
ledger_client = ConfidentialLedgerClient(
endpoint=ledger_url,
credential=credential,
ledger_certificate_path=ledger_tls_cert_file_name
)
À l’aide du client de registre confidentiel, nous pouvons exécuter toutes les opérations prises en charge sur une instance du registre confidentiel Azure. Par exemple, nous pouvons ajouter une nouvelle entrée au registre et attendre que la transaction d’écriture correspondante soit validée.
# 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()
Une fois la transaction validée, nous pouvons utiliser le client pour obtenir un reçu sur l’entrée ajoutée au registre à l’étape précédente à l’aide de l’ID de transaction correspondant.
# 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()
Exemple de code
L’exemple de code complet utilisé dans la procédure pas à pas est fourni.
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))
Contenu du reçu de transaction d’écriture
Voici un exemple de charge utile de réponse JSON retournée par une instance du registre confidentiel Azure lors de l’appel du point de terminaison 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 réponse JSON contient les champs suivants au niveau de la racine.
receipt : Ce champ contient les valeurs qui peuvent être utilisées pour vérifier la validité du reçu pour la transaction d’écriture correspondante.
state : État de la réponse JSON retournée. Les valeurs possibles autorisées sont les suivantes :
Ready
: Le reçu retourné dans la réponse est disponible.Loading
: le reçu n’est pas encore disponible pour être extrait et une nouvelle tentative de la requête doit être effectuée.
transactionId : ID de transaction associé au reçu de transaction d’écriture.
Le champ receipt
contient les champs suivants.
cert : Chaîne avec le certificat de clé publique PEM du nœud CCF qui a signé la transaction d’écriture. Le certificat d’identité du service doit toujours approuvé le certificat du nœud de signature. Pour plus d’informations sur la façon dont les transactions sont régulièrement signées et la façon dont les transactions de signature sont ajoutées au registre dans CCF, consultez ce lien.
nodeId : Chaîne hexadécimale représentant le code de hachage SHA-256 de la clé publique du nœud CCF de signature.
leafComponents : Composants du code de hachage du nœud terminal dans l’arborescence Merkle associés à la transaction spécifiée. Une arborescence Merkle est une structure de données en arborescence qui enregistre le code de hachage de chaque transaction et garantit l’intégrité du registre. Pour plus d’informations sur l’utilisation d’une arborescence Merkle dans CCF, consultez la documentation du CCF associée.
proof : Liste des paires clé-valeur représentant les codes de hachage des nœuds de l’arborescence Merkle qui, quand ils sont combinés avec le code de hachage du nœud terminal correspondant à la transaction donnée, autorisent le recalcul du code de hachage racine de l’arborescence. Grâce aux propriétés d’une arborescence Merkle, il est possible de recalculer le code de hachage racine de l’arborescence uniquement pour un sous-ensemble de nœuds. Les éléments de cette liste se présentent sous la forme de paires clé-valeur : les clés indiquent la position relative par rapport au nœud parent dans l’arborescence à un certain niveau tandis que les valeurs sont les codes de hachage SHA-256 du nœud donné, sous forme de chaînes hexadécimales.
serviceEndorsements : Liste des chaînes de certificats codées en PEM représentant les certificats précédents des identités du service. Il est possible que l’identité du service qui a approuvé le nœud de signature ne soit pas la même que celle qui a émis le reçu. Par exemple, le certificat de service est renouvelé après la reprise d’activité d’un registre confidentiel. La liste des certificats de service antérieurs permet aux auditeurs de créer la chaîne d’approbation à partir du nœud de signature CCF jusqu’au certificat de service actuel.
signature : Chaîne en base64 représentant la signature de la racine de l’arborescence Merkle au niveau de la transaction donnée, par le nœud CCF de signature.
Le champ leafComponents
contient les champs suivants.
claimsDigest : Chaîne hexadécimale représentant le code de hachage SHA-256 de la revendication d’application attachée par l’application du registre confidentiel au moment de l’exécution de la transaction. Les revendications d’application ne sont actuellement pas prises en charge, car l’application du registre confidentiel n’attache aucune revendication lors de l’exécution d’une transaction d’écriture.
commitEvidence : Chaîne unique produite par transaction, dérivée de l’ID de transaction et des secrets du registre. Pour plus d’informations sur la preuve de validation, consultez la documentation du CCF connexe.
writeSetDigest : Chaîne hexadécimale représentant le code de hachage SHA-256 du magasin de paires clé-valeur, qui contient toutes les clés et valeurs écrites au moment de la transaction. Pour plus d’informations sur l’ensemble d’écritures, consultez la documentation du CCF connexe.
Revendications d’application
Les applications Azure Confidential Ledger peuvent joindre des données arbitraires, appelées revendications d’application, pour les transactions en écriture. Ces revendications représentent les actions exécutées pendant une opération d’écriture. Lorsqu’elle est jointe à une transaction, la synthèse SHA-256 de l’objet de revendications est incluse dans le registre et validée dans le cadre de la transaction d’écriture. Inclure la revendication dans la transaction en écriture garantit que la synthèse de revendications est signée en place et ne peut pas être falsifiée.
Plus tard, les revendications d’application peuvent être révélées dans leur format simple dans la charge utile de réception correspondant à la même transaction à laquelle elles ont été ajoutées. Les revendications exposées permettent aux utilisateurs de recalculer la même synthèse de revendications que celle qui a été jointe et signée en place par le registre lors de la transaction. La synthèse de revendications peut être utilisée dans le cadre du processus de vérification du reçu de transaction en écriture. Cela permet aux utilisateurs de vérifier entièrement l’authenticité des revendications enregistrées.
Les revendications d’application sont actuellement prises en charge dans la préversion 2023-01-18-preview
de l’API.
Contenu du reçu de transaction en écriture avec des revendications d’application
Voici un exemple de charge utile de réponse JSON retournée par une instance Azure Confidential Ledger ayant enregistré les revendications d’application lors de l’appel du point de terminaison 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"
}
Par rapport à l’exemple de reçu présenté dans la section précédente, la réponse JSON contient un autre champ applicationClaims
qui représente la liste des revendications d’application enregistrées par le registre pendant la transaction en écriture. Chaque objet à l’intérieur de la liste applicationClaims
contient les champs suivants.
kind : représente le type de revendication d’application. La valeur indique la façon d’analyser l’objet de revendications d’application en fonction du type fourni.
ledgerEntry : représente une revendication d’application dérivée des données d’entrée de registre. La revendication contient les données enregistrées par l’application lors d’une transaction en écriture, comme l’ID de collection et le contenu fourni par l’utilisateur, et les informations requises pour calculer la synthèse correspondant à l’objet de revendication unique.
digest : représente une revendication d’application sous forme condensée. Cet objet de revendication contient la synthèse précalculée par l’application et le protocole utilisé pour le calcul.
Le champ ledgerEntry
contient les champs suivants.
protocol : représente le protocole à utiliser pour calculer la synthèse d’une revendication à partir des données de revendication données.
collectionId : identificateur de la collection écrite lors de la transaction en écriture correspondante.
contents : contenu du registre écrit lors de la transaction en écriture correspondante.
secretKey : clé secrète encodée au format base64. Cette clé doit être utilisée dans l’algorithme HMAC avec les valeurs fournies dans la revendication d’application pour obtenir la synthèse de revendication.
Le champ digest
contient les champs suivants.
protocol : représente le protocole utilisé pour calculer la synthèse de la revendication donnée.
value : synthèse de la revendication d’application, sous forme hexadécimale. Cette valeur doit être hachée avec la valeur
protocol
pour calculer la synthèse complète de revendications d’application.
Plus de ressources
Pour plus d’informations sur les reçus de transaction d’écriture et sur la façon dont le CCF garantit l’intégrité de chaque transaction, consultez les liens suivants :
- Reçus d’écriture
- Reçus
- Glossaire du CCF
- Arborescence Merkle
- Cryptographie
- Certificates
- Revendications d’application
- Revendications définies par l’utilisateur dans les reçus