你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

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

提交事务后,可以使用客户端通过相应的事务 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() 

代码示例

提供了代码演练中使用的完整示例代码。

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

写入事务收据内容

以下是 Azure 机密账本实例在调用 GET_RECEIPT 终结点时返回的 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:与写入事务收据关联的事务 ID。

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:每个事务生成的唯一字符串,派生自事务 ID 和账本机密。 有关提交证据的详细信息,请参阅相关的 CCF 文档

  • writeSetDigest:十六进制字符串,表示 键值存储的 SHA-256 哈希摘要,其中包含事务完成时写入的所有键和值。 有关写入集的详细信息,请参阅相关的 CCF 文档

应用程序声明

Azure 机密账本应用程序可以附加任意数据(称为应用程序声明)以写入事务。 这些声明表示写入操作期间执行的操作。 附加到事务时,声明对象的 SHA-256 摘要包含在账本中,并作为写入事务的一部分提交。 在写入事务中包含声明可保证声明摘要已就地签名,并且不会被篡改。

稍后,应用程序声明可以以普通格式显示在与添加它们的同一事务对应的回执有效负载中。 公开的声明允许用户重新计算相同的声明摘要(在事务期间由账本附加和登录)。 声明摘要可用作写入事务回执验证过程的一部分,为用户提供一种脱机方式来完全验证所记录声明的真实性。

预览版 API 2023-01-18-preview目前支持应用程序声明。

使用应用程序声明写入事务回执内容

以下是 Azure 机密账本实例(记录应用程序声明)在调用GET_RECEIPT终结点时返回的 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:它表示派生自账本条目数据的应用程序声明。 声明会包含应用程序在写入事务期间记录的数据(例如,集合 ID 和用户提供的内容),以及计算对应于单个声明对象的摘要所需的信息。

  • digest:它以摘要形式表示应用程序声明。 此声明对象会包含应用程序的预计算摘要和用于计算的协议。

ledgerEntry 字段包含以下字段。

  • protocol:它表示用于从给定声明数据中计算声明摘要的协议。

  • collectionId:在相应写入事务期间写入的集合的标识符。

  • contents:在相应的写入事务期间写入的账本的内容。

  • secretKey:base64 编码的密钥。 此密钥将在 HMAC 算法中与应用程序声明中提供的值结合使用,从而获取声明摘要。

digest 字段包含以下字段。

  • protocol:它表示用于计算给定声明摘要的协议。

  • value:应用程序声明的摘要,采用十六进制格式。 必须使用protocol值对此值进行哈希处理,从而计算应用程序声明的完整摘要。

更多资源

有关写入事务收据以及 CCF 如何确保每个事务的完整性的详细信息,请参阅以下链接:

后续步骤