Belege für Azure Confidential Ledger-Schreibtransaktionen
Um Transaktionsintegritätsgarantien zu erzwingen, verwendet ein Azure Confidential Ledger eine Merkle-Baumdatenstruktur, um den Hash aller Transaktionsblöcke aufzuzeichnen, die an den unveränderlichen Ledger angefügt werden. Nachdem ein Commit für eine Schreibtransaktion erfolgt ist, können Azure Confidential Ledger-Benutzer einen kryptografischen Merkle-Nachweis oder Beleg über den in einem Confidential Ledger erstellten Eintrag erhalten, um zu überprüfen, ob der Schreibvorgang ordnungsgemäß gespeichert wurde. Ein Beleg für eine Schreibtransaktion ist der Nachweis, dass das System ein Commit für die entsprechende Transaktion ausgeführt hat, und kann verwendet werden, um zu überprüfen, ob der Eintrag tatsächlich an den Ledger angefügt wurde.
Weitere Informationen zur Verwendung eines Merkle-Baums in einem Confidential Ledger finden Sie in der CCF-Dokumentation.
Abrufen von Belegen für Schreibtransaktionen
Einrichtung und Voraussetzungen
Azure Confidential Ledger-Benutzer können mithilfe der Azure Confidential Ledger-Clientbibliothek einen Beleg für eine bestimmte Transaktion abrufen. Das folgende Beispiel zeigt, wie Sie mithilfe der Clientbibliothek für Python einen Schreibbeleg abrufen, doch sind die Schritte bei jedem unterstützten SDK für Azure Confidential Ledger identisch.
Es wird vorausgesetzt, dass bereits eine Confidential Ledger-Ressource mithilfe der Azure Confidential Ledger-Verwaltungsbibliothek erstellt wurde. Wenn Sie noch nicht über eine Ledger-Ressource verfügen, erstellen Sie eine anhand der folgenden Anweisungen.
Exemplarische Vorgehensweise mit Code
Zunächst werden die Importe für das Python-Programm eingerichtet.
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
Nachfolgend sind die konstanten Werte aufgeführt, die zum Einrichten des Azure Confidential Ledger-Clients verwendet werden. Denken Sie daran, die Konstante ledger_name
mit dem eindeutigen Namen Ihrer Confidential Ledger-Ressource zu aktualisieren.
# 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"
Authentifizieren Sie sich mit der DefaultAzureCredential-Klasse.
# Setup authentication
credential = DefaultAzureCredential()
Anschließend rufen Sie das Confidential Ledger-Dienstzertifikat mithilfe des Zertifikatclients aus der Confidential Ledger-Identitäts-URL ab und speichern es. Das Dienstzertifikat ist ein öffentliches Schlüsselzertifikat der Netzwerkidentität, das als Vertrauensanker für die TLS-Serverauthentifizierung verwendet wird. Anders ausgedrückt: Es wird als Zertifizierungsstelle zum Herstellen einer TLS-Verbindung mit einem der Knoten im CCF-Netzwerk verwendet.
# 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"])
Als Nächstes können Sie Ihre Anmeldeinformationen, das abgerufene Netzwerkzertifikat und die eindeutige Ledger-URL verwenden, um einen Confidential Ledger-Client zu erstellen.
# Create Confidential Ledger client
ledger_client = ConfidentialLedgerClient(
endpoint=ledger_url,
credential=credential,
ledger_certificate_path=ledger_tls_cert_file_name
)
Mit dem Confidential Ledger-Client können alle unterstützten Vorgänge auf einer Azure Confidential Ledger-Instanz ausgeführt werden. Beispielsweise können Sie einen neuen Eintrag an den Ledger anfügen und warten, bis eine Commit für die entsprechende Schreibtransaktion ausgeführt wurde.
# 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()
Nachdem ein Commit für die Transaktion durchgeführt wurde, können Sie mithilfe des Clients einen Beleg über den Eintrag abrufen, der im vorherigen Schritt an den Ledger angefügt wurde. Dazu verwenden Sie die entsprechende Transaktions-ID.
# 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()
Beispielcode
Der vollständige Beispielcode, der im Codedurchlauf verwendet wird, wird bereitgestellt.
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))
Inhalt des Belegs für eine Schreibtransaktion
Nachfolgend sehen Sie ein Beispiel für JSON-Antwortnutzdaten, die von einer Azure Confidential Ledger-Instanz beim Aufrufen des GET_RECEIPT
-Endpunkts zurückgegeben werden.
{
"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"
}
Die JSON-Antwort enthält die folgenden Felder auf der Stammebene.
receipt: Enthält die Werte, mit denen die Gültigkeit des Belegs für die entsprechende Schreibtransaktion überprüft werden kann.
state: Der Status der zurückgegebenen JSON-Antwort. Folgende Werte sind zulässig:
Ready
: Der in der Antwort zurückgegebene Beleg ist verfügbar.Loading
: Der Beleg ist noch nicht zum Abrufen verfügbar, und die Anforderung muss erneut ausgeführt werden
transactionId: Die Transaktions-ID, die dem Beleg für die Schreibtransaktion zugeordnet ist.
Das Feld receipt
enthält die folgenden Felder.
cert: Zeichenfolge mit dem öffentlichen PEM-Schlüsselzertifikat des CCF-Knotens, der die Schreibtransaktion signiert hat. Das Dienstidentitätszertifikat sollte immer das Zertifikat des Signaturknotens unterstützen. Weitere Informationen dazu, wie Transaktionen regelmäßig signiert werden und wie die Signaturtransaktionen an den Ledger in CCF angefügt werden, finden Sie unter dem folgenden Link.
nodeId: Hexadezimale Zeichenfolge, die den SHA-256-Hash-Digest für den öffentlichen Schlüssel des CCF-Signaturknotens darstellt.
leafComponents: Die Komponenten des Blattknotenhash im Merkle-Baum, die der angegebenen Transaktion zugeordnet sind. Ein Merkle-Baum ist eine Baumdatenstruktur, die den Hash jeder Transaktion aufzeichnet und die Integrität des Ledgers gewährleistet. Weitere Informationen zur Verwendung eines Merkle-Baums in CCF finden Sie in der zugehörigen CCF-Dokumentation.
proof: Liste der Schlüssel-Wert-Paare, die die Knotenhashes des Merkle-Baums darstellen und in Kombination mit dem Blattknotenhash, der der angegebenen Transaktion entspricht, die Neuberechnung des Stammhash der Struktur ermöglichen. Dank der Eigenschaften eines Merkle-Baums ist es möglich, den Stammhash der Struktur nur für eine Teilmenge von Knoten neu zu berechnen. Die Elemente in dieser Liste weisen die Form von Schlüssel-Wert-Paaren auf: Schlüssel geben die relative Position in Bezug auf den übergeordneten Knoten in der Struktur auf einer bestimmten Ebene an; Werte sind die SHA-256-Hash-Digests des angegebenen Knotens als hexadezimale Zeichenfolgen.
serviceEndorsements: Liste der PEM-codierten Zertifikatzeichenfolgen, die frühere Dienstidentitätszertifikate darstellen. Es ist möglich, dass die Dienstidentität, die den Signaturknoten unterstützt hat, nicht mit der Identität übereinstimmt, die den Beleg ausgestellt hat. Beispielsweise wird das Dienstzertifikat nach einer Notfallwiederherstellung eines Confidential Ledger erneuert. Mit der Liste der früheren Dienstzertifikate können Prüfer die Vertrauenskette vom CCF-Signaturknoten zum aktuellen Dienstzertifikat erstellen.
signature: Base64-Zeichenfolge, die die Signatur des Stamms des Merkle-Baums bei der angegebenen Transaktion durch den CCF-Signaturknoten darstellt.
Das Feld leafComponents
enthält die folgenden Felder.
claimsDigest: Hexadezimale Zeichenfolge, die den SHA-256-Hash-Digest des Anwendungsanspruchs darstellt, der von der Confidential Ledger-Anwendung zum Zeitpunkt der Transaktionsausführung angefügt wurde. Anwendungsansprüche werden derzeit nicht unterstützt, da die Confidential Ledger-Anwendung beim Ausführen einer Schreibtransaktion keinen Anspruch anfügt.
commitEvidence: Eine eindeutige Zeichenfolge, die pro Transaktion erstellt wird und von der Transaktions-ID und den Ledgergeheimnissen abgeleitet wird. Weitere Informationen zum Commitnachweis finden Sie in der zugehörigen CCF-Dokumentation.
writeSetDigest: Hexadezimale Zeichenfolge, die den SHA-256-Hash-Digest des Schlüssel-Wert-Speichers darstellt, der alle zum Zeitpunkt des Transaktionsabschlusses geschriebenen Schlüssel und Werte enthält. Weitere Informationen zum Schreibsatz finden Sie in der zugehörigen CCF-Dokumentation.
Anwendungsansprüche
Azure Confidential Ledger-Anwendungen können beliebige, als Anwendungsansprüche bezeichnete Daten anfügen, um Transaktionen zu schreiben. Diese Ansprüche stellen die Aktionen dar, die während eines Schreibvorgangs ausgeführt werden. Wenn er an eine Transaktion angefügt ist, wird der SHA-256-Digest des Anspruchsobjekts im Ledger aufgenommen und als Teil der Schreibtransaktion committet. Die Aufnahme des Anspruchs in die Schreibtransaktion garantiert, dass der Anspruchsdigest an Ort und Stelle signiert wird und nicht manipuliert werden kann.
Später können Anwendungsansprüche in ihrem einfachen Format in den Empfangsnutzdaten angezeigt werden, die der gleichen Transaktion entsprechen, in der sie hinzugefügt wurden. Mit den verfügbar gemachten Ansprüchen können Benutzer den gleichen Anspruchsdigest neu berechnen, der während der Transaktion vom Ledger angefügt und an Ort und Stelle signiert wurde. Der Anspruchsdigest kann als Teil des Überprüfungsprozesses für den Schreibtransaktionsbeleg verwendet werden und bietet Benutzern eine Offline-Möglichkeit, die Echtheit der aufgezeichneten Ansprüche vollständig zu überprüfen.
Anwendungsansprüche werden derzeit in der Vorschau-API-Version 2023-01-18-preview
unterstützt.
Schreiben von Transaktionsbeleginhalten mit Anwendungsansprüchen
Nachfolgend sehen Sie ein Beispiel für JSON-Antwortnutzdaten, die von einer Azure Confidential Ledger-Instanz, die Anwendungsansprüche aufgezeichnet hat, beim Aufrufen des GET_RECEIPT
-Endpunkts zurückgegeben werden.
{
"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"
}
Im Vergleich zum im vorherigen Abschnitt gezeigten Belegbeispiel enthält die JSON-Antwort ein weiteres applicationClaims
-Feld, das die Liste der Anwendungsansprüche darstellt, die vom Ledger während der Schreibtransaktion aufgezeichnet wurden. Jedes Objekt in der applicationClaims
-Liste enthält die folgenden Felder.
kind: Er stellt die Art des Anwendungsanspruchs dar. Der Wert gibt an, wie das Anwendungsanspruchsobjekt für den angegebenen Typ geparst wird.
ledgerEntry: Es stellt einen Anwendungsanspruch dar, der aus Ledgereintragsdaten abgeleitet wird. Der Anspruch würde die Daten enthalten, die von der Anwendung während einer Schreibtransaktion aufgezeichnet wurden (z. B. die Sammlungs-ID und die vom Benutzer bereitgestellten Inhalte) und die erforderlichen Informationen, um den Digest zu berechnen, der dem einzelnen Anspruchsobjekt entspricht.
digest: Es stellt einen Anwendungsanspruch in verdichteter Form dar. Dieses Anspruchsobjekt würde den vorberechneten Digest der Anwendung und das Protokoll enthalten, das für die Berechnung verwendet wird.
Das Feld ledgerEntry
enthält die folgenden Felder.
protocol: Es stellt das Protokoll dar, das verwendet werden soll, um den Digest eines Anspruchs aus den gegebenen Anspruchsdaten zu berechnen.
collectionId: Der Bezeichner der Sammlung, die während der entsprechenden Schreibtransaktion geschrieben wurde.
contents: Die Inhalte des Ledger, die während der entsprechenden Schreibtransaktion geschrieben wurden.
secretKey: Ein base64-codierter geheimer Schlüssel. Dieser Schlüssel soll im HMAC-Algorithmus mit den im Anwendungsanspruch angegebenen Werten verwendet werden, um den Anspruchsdigest abzurufen.
Das Feld digest
enthält die folgenden Felder.
protocol: Es stellt das Protokoll dar, das zum Berechnen des Digest des gegebenen Anspruchs verwendet wird.
value: Der Digest des Anwendungsanspruchs in hexadezimaler Form. Dieser Wert müsste mit dem
protocol
-Wert gehasht werden, um den vollständigen Digest des Anwendungsanspruchs zu berechnen.
Weitere Ressourcen
Weitere Informationen zu Belegen für Schreibtransaktionen und zur Sicherstellung der Integrität jeder Transaktion durch CCF finden Sie unter den folgenden Links:
- Schreibbelege
- Receipts
- CCF-Glossar
- Merkle-Baum
- Kryptografie
- Zertifikate
- Anwendungsansprüche
- Benutzerdefinierte Ansprüche in Belegen