Azure 機密總帳寫入交易收據
為了強制執行交易完整性保證,Azure 機密總帳會使用 Merkle 樹狀資料結構來記錄附加至不可變總帳的所有交易區塊的雜湊。 寫入交易經認可後,Azure 機密總帳使用者即可透過機密總帳所產生的項目取得密碼編譯 Merkle 證明 (或收據),以確認寫入作業已正確儲存。 寫入交易收據是系統已認可對應交易的證明,可用來驗證項目是否已有效地附加至總帳。
如需關於在機密總帳中使用 Merkle 樹狀結構的詳細資訊,請參閱 CCF 文件。
取得寫入交易收據
設定和必要條件
Azure 機密總帳使用者可以使用 Azure 機密總帳用戶端程式庫取得特定交易的收據。 下列範例說明如何使用適用於 Python 的用戶端程式庫取得寫入收據,但其步驟與任何其他支援的 Azure 機密總帳 SDK 相同。
我們假設已使用 Azure 機密總帳管理程式庫建立機密總帳資源。 如果您還沒有現有的總帳資源,請使用下列指示建立一個。
程式碼逐步解說
首先,我們設定 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
以下是用來設定 Azure 機密總帳用戶端的常數值。 請務必使用您機密總帳資源的唯一名稱來更新 ledger_name
常數。
# 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"
我們使用 DefaultAzureCredential 類別進行驗證。
# Setup authentication
credential = DefaultAzureCredential()
然後,我們使用憑證用戶端,從機密總帳身分識別 URL 取得機密總帳服務憑證,並加以儲存。 服務憑證是供 TLS 伺服器驗證作為信任根的網路身分識別公開金鑰憑證。 換句話說,此憑證可作為憑證授權單位 (CA),用以建立與 CCF 網路中的任何節點的 TLS 連線。
# 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"])
接著,我們可以使用認證、擷取的網路憑證和唯一總帳 URL 來建立機密總帳用戶端。
# Create Confidential Ledger client
ledger_client = ConfidentialLedgerClient(
endpoint=ledger_url,
credential=credential,
ledger_certificate_path=ledger_tls_cert_file_name
)
我們可以使用機密總帳用戶端,在 Azure 機密總帳執行個體上執行任何支援的作業。 例如,我們可以將新項目附加至總帳,並等候對應的寫入交易獲得認可。
# 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()
範例指令碼
以下提供程式碼逐步解說中使用的完整範例程式碼。
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))
寫入交易收據內容
以下範例說明在呼叫 GET_RECEIPT
端點時,Azure 機密總帳執行個體所傳回的 JSON 回應承載。
{
"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"
}
JSON 回應包含下列根層級的欄位。
receipt:其中包含可用來為對應寫入交易的收據驗證有效性的值。
state:傳回的 JSON 回應的狀態。 以下是允許的可能值:
Ready
:回應中傳回的收據可供使用Loading
:收據尚無法擷取,而必須重試要求
transactionId:與寫入交易收據相關聯的交易識別碼。
receipt
欄位包含下列欄位。
cert:一個字串,含有簽署寫入交易之 CCF 節點的 PEM 公開金鑰憑證。 服務識別憑證一律應背書簽署節點的憑證。 另請參閱下列連結,深入了解交易如何定期簽署,以及簽章交易如何附加至 CCF 中的總帳。
nodeId:一個十六進位字串,代表簽署 CCF 節點之公開金鑰的 SHA-256 雜湊摘要。
leafComponents:Merkle 樹狀結構中與指定交易相關聯的分葉節點雜湊元件。 Merkle 樹狀結構是一個樹狀資料結構,可記錄每個交易的雜湊,並保證總帳的完整性。 如需如何在 CCF 中使用 Merkle 樹狀結構的詳細資訊,請參閱相關的 CCF 文件。
proof:一個索引鍵/值組清單,代表與對應至指定交易的分葉節點雜湊結合時,允許重新計算樹狀結構根雜湊的 Merkle 樹狀節點雜湊。 基於 Merkle 樹狀結構的屬性,只有一部分的節點可重新計算樹狀結構的根雜湊。 此清單中的元素採用索引鍵/值組的格式:索引鍵表示在特定層級與樹狀結構中的父節點對應的相對位置;值是指定節點的 SHA-256 雜湊摘要,採十六進位字串格式。
serviceEndorsements:PEM 編碼的憑證字串清單,代表先前服務識別憑證。 背書簽署節點的服務身分識別,有可能與發出收據的身分識別不同。 例如,服務憑證會在機密總帳的災害復原之後更新。 過去的服務憑證清單可讓稽核者建置從 CCF 簽署節點到目前服務憑證的信任鏈結。
signature:一個 Base64 字串,代表簽署 CCF 節點對指定交易簽署的 Merkle 樹狀結構根。
leafComponents
欄位包含下列欄位。
claimsDigest:一個十六進位字串,代表在交易執行時,由機密總帳應用程式附加的應用程式宣告的 SHA-256 雜湊摘要。 應用程式宣告目前不受支援,因為機密總帳應用程式在執行寫入交易時不會附加任何宣告。
commitEvidence:每個交易產生的唯一字串,衍生自交易識別碼和總帳秘密。 如需認可辨識項的詳細資訊,請參閱相關的 CCF 文件。
writeSetDigest:一個十六進位字串,代表索引鍵-值存放區的 SHA-256 雜湊摘要,其中包含交易完成時寫入的所有索引鍵和值。 如需寫入集合的詳細資訊,請參閱相關的 CCF 文件。
應用程式宣告
Azure 機密總帳應用程式可將任意資料 (名為應用程式宣告) 附加至寫入交易。 這些宣告代表寫入作業期間執行的動作。 附加至交易時,宣告物件的 SHA-256 摘要會包含在總帳中,並認可為寫入交易的一部分。 在寫入交易中包含宣告,可保證宣告摘要已適當簽署,且無法竄改。
隨後,應用程式宣告將可用純文字格式顯示在與其新增所在的相同交易相對應的收據承載中。 公開的宣告可讓使用者重新計算在交易期間由總帳適當附加並簽署的相同宣告摘要。 宣告摘要可作為寫入交易收據驗證程序的一部分,讓使用者能夠以離線方式完整驗證記錄的宣告是否真確。
預覽 API 版本 2023-01-18-preview
目前支援應用程式宣告。
含有應用程式宣告的寫入交易收據內容
以下範例說明在呼叫 GET_RECEIPT
端點時,Azure 機密總帳執行個體所傳回的 JSON 回應承載 (其中記錄了應用程式宣告)。
{
"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"
}
相較於上一節所述的收據範例,JSON 回應包含另一個 applicationClaims
欄位,代表總帳在寫入交易期間記錄的應用程式宣告清單。 applicationClaims
清單中的每個物件都包含下列欄位。
kind:代表應用程式宣告的種類。 其值表示如何針對提供的類型剖析應用程式宣告物件。
ledgerEntry:代表衍生自總帳項目資料的應用程式宣告。 此宣告會包含應用程式在寫入交易期間記錄的資料 (例如,集合識別碼和使用者提供的內容),以及計算對應至單一宣告物件的摘要所需的資訊。
digest:代表採用摘要格式的應用程式宣告。 此宣告物件會包含應用程式預先計算的摘要,以及用於計算的通訊協定。
ledgerEntry
欄位包含下列欄位。
protocol:代表用來從指定的宣告資料計算宣告摘要的通訊協定。
collectionId:在對應的寫入交易期間寫入之集合的識別碼。
contents:在對應的寫入交易期間寫入之總帳的內容。
secretKey:base64 編碼的祕密金鑰。 此索引鍵將在 HMAC 演算法中與應用程式宣告中提供的值搭配用來取得宣告摘要。
digest
欄位包含下列欄位。
protocol:代表用來計算指定宣告之摘要的通訊協定。
value:應用程式宣告的摘要,採十六進位格式。 此值必須使用
protocol
值進行雜湊處理,才能計算應用程式宣告的完整摘要。
更多資源
若要深入了解寫入交易收據以及 CCF 如何確保每個交易的完整性,請參閱下列連結: