Azure Confidential Ledger 쓰기 트랜잭션 영수증
트랜잭션 무결성을 보장하기 위해 Azure Confidential Ledger는 Merkle 트리 데이터 구조를 사용하여 변경할 수 없는 원장에 추가되는 모든 트랜잭션 블록의 해시를 기록합니다. 쓰기 트랜잭션이 커밋된 후 Azure Confidential Ledger 사용자는 Confidential Ledger에서 생성된 항목에 대한 암호화 Merkle 증명 또는 영수증을 가져와 쓰기 작업이 올바르게 저장되었는지 확인할 수 있습니다. 쓰기 트랜잭션 영수증은 시스템이 해당 트랜잭션을 커밋했다는 증거이며 항목이 원장에 효과적으로 추가되었는지 확인하는 데 사용할 수 있습니다.
Confidential Ledger에서 Merkle 트리가 사용되는 방법에 대한 자세한 내용은 CCF 설명서에서 확인할 수 있습니다.
쓰기 트랜잭션 영수증 가져오기
설정 및 필수 조건
Azure Confidential Ledger 사용자는 Azure Confidential Ledger 클라이언트 라이브러리를 사용하여 특정 트랜잭션에 대한 영수증을 가져올 수 있습니다. 다음 예에서는 Python용 클라이언트 라이브러리를 사용하여 수신 확인을 받는 방법을 보여 주지만 단계는 지원되는 다른 Azure Confidential Ledger용 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 Confidential Ledger 클라이언트를 설정하는 데 사용되는 상수 값입니다. Confidential Ledger 리소스의 고유한 이름으로 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()
그런 다음 Confidential Ledger Identity URL에서 인증서 클라이언트를 사용하여 Confidential Ledger 서비스 인증서를 가져와 저장합니다. 서비스 인증서는 TLS 서버 인증을 위한 신뢰할 수 있는 루트로 사용되는 네트워크 ID 공개 키 인증서입니다. 즉, CCF 네트워크의 모든 노드와 TLS 연결을 설정하기 위한 CA(인증 기관)으로 사용됩니다.
# 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을 사용하여 Confidential Ledger 클라이언트를 만들 수 있습니다.
# Create Confidential Ledger client
ledger_client = ConfidentialLedgerClient(
endpoint=ledger_url,
credential=credential,
ledger_certificate_path=ledger_tls_cert_file_name
)
Confidential Ledger 클라이언트를 사용하여 Azure Confidential Ledger 인스턴스에서 지원되는 모든 작업을 실행할 수 있습니다. 예를 들어, 원장에 새 항목을 추가하고 해당 쓰기 트랜잭션이 커밋될 때까지 기다릴 수 있습니다.
# 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))
트랜잭션 영수증 콘텐츠 작성
다음은 GET_RECEIPT
엔드포인트를 호출할 때 Azure Confidential Ledger 인스턴스에서 반환하는 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 공개 키 인증서가 있는 문자열입니다. 서비스 ID 인증서는 항상 서명 노드의 인증서를 보증해야 합니다. 다음 링크에서 트랜잭션이 정기적으로 서명되는 방법과 서명 트랜잭션이 CCF의 원장에 추가되는 방법에 대한 자세한 내용도 참조하세요.
nodeId: 서명하는 CCF 노드의 공개 키에 대한 SHA-256 해시 다이제스트를 나타내는 16진수 문자열입니다.
leafComponents: 지정된 트랜잭션과 연결된 Merkle 트리의 리프 노드 해시 구성 요소입니다. Merkle 트리는 모든 트랜잭션의 해시를 기록하고 원장의 무결성을 보장하는 트리 데이터 구조입니다. Merkle 트리가 CCF에서 사용되는 방법에 대한 자세한 내용은 관련 CCF 설명서를 참조하세요.
proof: 지정된 트랜잭션에 해당하는 리프 노드 해시와 결합될 때 트리의 루트 해시 재계산을 허용하는 Merkle 트리 노드 해시를 나타내는 키-값 쌍 목록입니다. Merkle 트리의 속성 덕분에 노드의 하위 집합에서만 트리의 루트 해시를 다시 계산할 수 있습니다. 이 목록의 요소는 키-값 쌍의 형식입니다. 키는 특정 수준에서 트리의 부모 노드에 대한 상대적 위치를 나타냅니다. 값은 16진수 문자열로 지정된 노드의 SHA-256 해시 다이제스트입니다.
serviceEndorsements: 이전 서비스 ID 인증서를 나타내는 PEM 인코딩 인증서 문자열 목록입니다. 서명 노드를 승인한 서비스 ID가 영수증을 발급한 서비스 ID와 동일하지 않을 수 있습니다. 예를 들어, 기밀 원장의 재해 복구 후 서비스 인증서가 갱신됩니다. 과거 서비스 인증서 목록을 통해 감사자는 CCF 서명 노드에서 현재 서비스 인증서까지 신뢰 체인을 빌드할 수 있습니다.
signature: 서명 CCF 노드에 의해 지정된 트랜잭션에서 Merkle 트리 루트의 서명을 나타내는 Base64 문자열입니다.
leafComponents
필드에는 다음 필드가 포함됩니다.
claimsDigest: 트랜잭션이 실행될 때 Confidential Ledger 애플리케이션이 첨부한 애플리케이션 클레임의 SHA-256 해시 다이제스트를 나타내는 16진수 문자열입니다. Confidential Ledger 애플리케이션이 쓰기 트랜잭션을 실행할 때 클레임을 첨부하지 않기 때문에 애플리케이션 클레임은 현재 지원되지 않습니다.
commitEvidence: 트랜잭션 ID 및 원장 비밀에서 파생된 트랜잭션당 생성된 고유한 문자열입니다. 커밋 증거에 대한 자세한 내용은 관련 CCF 설명서를 참조하세요.
writeSetDigest: 키-값 저장소의 SHA-256 해시 다이제스트를 나타내는 16진수 문자열로, 트랜잭션이 완료될 때 작성된 모든 키와 값을 포함합니다. 쓰기 집합에 대한 자세한 내용은 관련 CCF 설명서를 참조하세요.
애플리케이션 클레임
Azure Confidential Ledger 애플리케이션은 애플리케이션 클레임이라는 임의의 데이터를 연결하여 트랜잭션을 작성할 수 있습니다. 이러한 클레임은 쓰기 작업 중에 실행되는 작업을 나타냅니다. 트랜잭션에 연결되면 클레임 개체의 SHA-256 다이제스트가 원장에 포함되고 쓰기 트랜잭션의 일부로 커밋됩니다. 쓰기 트랜잭션에 클레임을 포함하면 클레임 다이제스트가 로그인되어 변조될 수 없습니다.
나중에 애플리케이션 클레임이 추가된 동일한 트랜잭션에 해당하는 영수증 페이로드에서 일반 형식으로 표시될 수 있습니다. 노출된 클레임을 통해 사용자는 트랜잭션 중에 원장에 의해 연결되고 서명된 것과 동일한 클레임 다이제스트를 다시 계산할 수 있습니다. 클레임 다이제스트는 쓰기 트랜잭션 영수증 확인 프로세스의 일부로 사용할 수 있으므로 사용자가 기록된 클레임의 신뢰성을 완전히 확인할 수 있는 오프라인 방법을 제공합니다.
애플리케이션 클레임은 현재 미리 보기 API 버전 2023-01-18-preview
에서 지원됩니다.
애플리케이션 클레임을 사용하여 트랜잭션 영수증 콘텐츠 작성
다음은 GET_RECEIPT
엔드포인트를 호출할 때 애플리케이션 클레임을 기록한 Azure Confidential Ledger 인스턴스에서 반환되는 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
목록 내의 각 개체에는 다음 필드가 포함됩니다.
종류: 애플리케이션 클레임의 종류를 나타냅니다. 값은 제공된 형식에 대한 애플리케이션 클레임 개체를 구문 분석하는 방법을 나타냅니다.
ledgerEntry: 원장 항목 데이터에서 파생된 애플리케이션 클레임을 나타냅니다. 클레임에는 쓰기 트랜잭션 중에 애플리케이션에서 기록한 데이터(예: 컬렉션 ID 및 사용자가 제공한 콘텐츠)와 단일 클레임 개체에 해당하는 다이제스트를 계산하는 데 필요한 정보가 포함됩니다.
다이제스트: 다이제스트된 형식의 애플리케이션 클레임을 나타냅니다. 이 클레임 개체에는 애플리케이션이 미리 계산한 다이제스트와 계산에 사용되는 프로토콜이 포함됩니다.
ledgerEntry
필드에는 다음 필드가 포함됩니다.
프로토콜: 지정된 클레임 데이터로부터 클레임의 다이제스트를 계산하는 데 사용되는 프로토콜을 나타냅니다.
collectionId: 해당 쓰기 트랜잭션 중에 작성된 컬렉션의 식별자입니다.
콘텐츠: 해당 쓰기 트랜잭션 중에 작성된 원장의 내용입니다.
secretKey: base64로 인코딩된 비밀 키입니다. 이 키는 클레임 다이제스트를 얻기 위해 애플리케이션 클레임에 제공된 값과 함께 HMAC 알고리즘에 사용됩니다.
digest
필드에는 다음 필드가 포함됩니다.
프로토콜: 지정된 클레임의 다이제스트를 계산하는 데 사용되는 프로토콜을 나타냅니다.
값: 애플리케이션 클레임의 다이제스트(16진수 형식)입니다. 이 값은 애플리케이션 클레임의 전체 다이제스트를 계산하기 위해
protocol
값으로 해시되어야 합니다.
추가 리소스
쓰기 트랜잭션 수신 및 CCF가 각 트랜잭션의 무결성을 보장하는 방법에 대한 자세한 내용은 다음 링크를 참조하세요.