Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit editor mode

Azure confidential ledger client library for .NET - version 1.3.0

Feedback

In this article

Azure confidential ledger provides a service for logging to an immutable, tamper-proof ledger. As part of theAzure Confidential Computingportfolio, Azure confidential ledger runs in SGX enclaves. It is built on Microsoft Research'sConfidential Consortium Framework.

Source code |Package (NuGet)

Getting started

This section should include everything a developer needs to do to install and create their first client connectionvery quickly.

Install the package

Install the Azure confidential ledger client library for .NET withNuGet:

dotnet add package Azure.Security.ConfidentialLedger

Prerequisites

  • AnAzure subscription.
  • A running instance of Azure confidential ledger.
  • A registered user in the Azure confidential ledger withAdministrator privileges.

Authenticate the client

Using Azure Active Directory

This document demonstrates usingDefaultAzureCredential to authenticate to the confidential ledger via Azure Active Directory. However, any of the credentials offered by theAzure.Identity will be accepted. See theAzure.Identity documentation for more information about other credentials.

Using a client certificate

As an alternative to Azure Active Directory, clients may choose to use a client certificate to authenticate via mutual TLS.

Create a client

DefaultAzureCredential will automatically handle most Azure SDK client scenarios. To get started, set environment variables for the AAD identity registered with your confidential ledger.

export AZURE_CLIENT_ID="generated app id"export AZURE_CLIENT_SECRET="random password"export AZURE_TENANT_ID="tenant id"

Then,DefaultAzureCredential will be able to authenticate theConfidentialLedgerClient.

Constructing the client also requires your confidential ledger's URI, which you can obtain from the Azure Portal page for your confidential ledger in theLedger URI field under theProperties section. When you have retrieved theLedger URI, please use it to replace"https://my-ledger-url.confidential-ledger.azure.com" in the example below.

var ledgerClient = new ConfidentialLedgerClient(new Uri("https://my-ledger-url.confidential-ledger.azure.com"), new DefaultAzureCredential());

Security Note: By default when a confidential ledger Client is created it will connect to Azure's confidential ledger Identity Service to obtain the latest TLS service certificate for your Ledger in order to secure connections to Ledger Nodes. The details of this process are available inthis sample. This behavior can be overridden by setting theoptions argument when creating the Ledger Client.

Key concepts

Ledger entries

Every write to Azure confidential ledger generates an immutable ledger entry in the service. Writes are uniquely identified by transaction ids that increment with each write.

Operation postOperation = ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(        new { contents = "Hello world!" }));string transactionId = postOperation.Id;Console.WriteLine($"Appended transaction with Id: {transactionId}");

Since Azure confidential ledger is a distributed system, rare transient failures may cause writes to be lost. For entries that must be preserved, it is advisable to verify that the write became durable. Note: It may be necessary to callGetTransactionStatus multiple times until it returns a "Committed" status. However, when callingPostLedgerEntry, a successful result indicates that the status is "Committed".

Response statusResponse = ledgerClient.GetTransactionStatus(transactionId);string status = JsonDocument.Parse(statusResponse.Content)    .RootElement    .GetProperty("state")    .GetString();Console.WriteLine($"Transaction status: {status}");// Wait for the entry to be committedwhile (status == "Pending"){    statusResponse = ledgerClient.GetTransactionStatus(transactionId);    status = JsonDocument.Parse(statusResponse.Content)        .RootElement        .GetProperty("state")        .GetString();}Console.WriteLine($"Transaction status: {status}");

Receipts

State changes to the a confidential ledger are saved in a data structure called a Merkle tree. To cryptographically verify that writes were correctly saved, a Merkle proof, or receipt, can be retrieved for any transaction id.

Response receiptResponse = ledgerClient.GetReceipt(transactionId);string receiptJson = new StreamReader(receiptResponse.ContentStream).ReadToEnd();Console.WriteLine(receiptJson);

Collections

While most use cases will involve one ledger, we provide the collections feature in case different logical groups of data need to be stored in the same confidential ledger.

ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(        new { contents = "Hello from Chris!", collectionId = "Chris' messages" }));ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(        new { contents = "Hello from Allison!", collectionId = "Allison's messages" }));

When no collection id is specified on method calls, the Azure confidential ledger service will assume a constant, service-determined collection id.

postOperation = ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(        new { contents = "Hello world!" }));string content = postOperation.GetRawResponse().Content.ToString();transactionId = postOperation.Id;string collectionId = "subledger:0";// Try fetching the ledger entry until it is "loaded".Response getByCollectionResponse = default;JsonElement rootElement = default;bool loaded = false;while (!loaded){    // Provide both the transactionId and collectionId.    getByCollectionResponse = ledgerClient.GetLedgerEntry(transactionId, collectionId);    rootElement = JsonDocument.Parse(getByCollectionResponse.Content).RootElement;    loaded = rootElement.GetProperty("state").GetString() != "Loading";}string contents = rootElement    .GetProperty("entry")    .GetProperty("contents")    .GetString();Console.WriteLine(contents); // "Hello world!"// Now just provide the transactionId.getByCollectionResponse = ledgerClient.GetLedgerEntry(transactionId);string collectionId2 = JsonDocument.Parse(getByCollectionResponse.Content)    .RootElement    .GetProperty("entry")    .GetProperty("collectionId")    .GetString();Console.WriteLine($"{collectionId} == {collectionId2}");

Ledger entries are retrieved from collections. When a transaction id is specified, the returned value is the value contained in the specified collection at the point in time identified by the transaction id. If no transaction id is specified, the latest available value is returned.

Operation firstPostOperation = ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(new { contents = "Hello world 0" }));ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(new { contents = "Hello world 1" }));Operation collectionPostOperation = ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(new { contents = "Hello world collection 0" }),    "my collection");ledgerClient.PostLedgerEntry(    waitUntil: WaitUntil.Completed,    RequestContent.Create(new { contents = "Hello world collection 1" }),    "my collection");transactionId = firstPostOperation.Id;// Wait for the entry to be committedstatus = "Pending";while (status == "Pending"){    statusResponse = ledgerClient.GetTransactionStatus(transactionId);    status = JsonDocument.Parse(statusResponse.Content)        .RootElement        .GetProperty("state")        .GetString();}// The ledger entry written at the transactionId in firstResponse is retrieved from the default collection.Response getResponse = ledgerClient.GetLedgerEntry(transactionId);// Try until the entry is available.loaded = false;JsonElement element = default;contents = null;while (!loaded){    loaded = JsonDocument.Parse(getResponse.Content)        .RootElement        .TryGetProperty("entry", out element);    if (loaded)    {        contents = element.GetProperty("contents").GetString();    }    else    {        getResponse = ledgerClient.GetLedgerEntry(transactionId, collectionId);    }}string firstEntryContents = JsonDocument.Parse(getResponse.Content)    .RootElement    .GetProperty("entry")    .GetProperty("contents")    .GetString();Console.WriteLine(firstEntryContents); // "Hello world 0"// This will return the latest entry available in the default collection.getResponse = ledgerClient.GetCurrentLedgerEntry();// Try until the entry is available.loaded = false;element = default;string latestDefaultCollection = null;while (!loaded){    loaded = JsonDocument.Parse(getResponse.Content)        .RootElement        .TryGetProperty("contents", out element);    if (loaded)    {        latestDefaultCollection = element.GetString();    }    else    {        getResponse = ledgerClient.GetCurrentLedgerEntry();    }}Console.WriteLine($"The latest ledger entry from the default collection is {latestDefaultCollection}"); //"Hello world 1"// The ledger entry written at collectionTransactionId is retrieved from the collection 'collection'.string collectionTransactionId = collectionPostOperation.Id;getResponse = ledgerClient.GetLedgerEntry(collectionTransactionId, "my collection");// Try until the entry is available.loaded = false;element = default;string collectionEntry = null;while (!loaded){    loaded = JsonDocument.Parse(getResponse.Content)        .RootElement        .TryGetProperty("entry", out element);    if (loaded)    {        collectionEntry = element.GetProperty("contents").GetString();    }    else    {        getResponse = ledgerClient.GetLedgerEntry(collectionTransactionId, "my collection");    }}Console.WriteLine(collectionEntry); // "Hello world collection 0"// This will return the latest entry available in the collection.getResponse = ledgerClient.GetCurrentLedgerEntry("my collection");string latestCollection = JsonDocument.Parse(getResponse.Content)    .RootElement    .GetProperty("contents")    .GetString();Console.WriteLine($"The latest ledger entry from the collection is {latestCollection}"); // "Hello world collection 1"
Ranged queries

Ledger entries in a collection may be retrieved over a range of transaction ids.Note: Both ranges are optional; they can be provided individually or not at all.

ledgerClient.GetLedgerEntries(fromTransactionId: "2.1", toTransactionId: collectionTransactionId);

User management

Users are managed directly with the confidential ledger instead of through Azure. New users may be AAD-based or certificate-based.

string newUserAadObjectId = "<some AAD user or service princpal object Id>";ledgerClient.CreateOrUpdateUser(    newUserAadObjectId,    RequestContent.Create(new { assignedRole = "Reader" }));

Confidential consortium and enclave verifications

One may want to validate details about the confidential ledger for a variety of reasons. For example, you may want to view details about how Microsoft may manage your confidential ledger as part ofConfidential Consortium Framework governance, or verify that your confidential ledger is indeed running in SGX enclaves. A number of client methods are provided for these use cases.

Pageable<BinaryData> consortiumResponse = ledgerClient.GetConsortiumMembers();foreach (var page in consortiumResponse){    string membersJson = page.ToString();    // Consortium members can manage and alter the confidential ledger, such as by replacing unhealthy nodes.    Console.WriteLine(membersJson);}// The constitution is a collection of JavaScript code that defines actions available to members,// and vets proposals by members to execute those actions.Response constitutionResponse = ledgerClient.GetConstitution();string constitutionJson = new StreamReader(constitutionResponse.ContentStream).ReadToEnd();Console.WriteLine(constitutionJson);// Enclave quotes contain material that can be used to cryptographically verify the validity and contents of an enclave.Response enclavesResponse = ledgerClient.GetEnclaveQuotes();string enclavesJson = new StreamReader(enclavesResponse.ContentStream).ReadToEnd();Console.WriteLine(enclavesJson);

Microsoft Azure Attestation Service is one provider of SGX enclave quotes.

Thread safety

We guarantee that all client instance methods are thread-safe and independent of each other (guideline). This ensures that the recommendation of reusing client instances is always safe, even across threads.

Additional concepts

Client options |Accessing the response |Long-running operations |Handling failures |Diagnostics |Mocking |Client lifetime

Examples

Coming Soon...

Troubleshooting

Response values returned from Azure confidential ledger client methods areResponse objects, which contain information about the http response such as the httpStatus property and aHeaders object containing more information about the failure.

Setting up console logging

The simplest way to see the logs is to enable the console logging.To create an Azure SDK log listener that outputs messages to console use AzureEventSourceListener.CreateConsoleLogger method.

// Setup a listener to monitor logged events.using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();

To learn more about other logging mechanisms seehere.

Next steps

For more extensive documentation on Azure confidential ledger, see the APIreference documentation.You may also read more about Microsoft Research's open-source ConfidentialConsortium Framework.

Contributing

This project welcomes contributions and suggestions. Most contributions requireyou to agree to a Contributor License Agreement (CLA) declaring that you havethe right to, and actually do, grant us the rights to use your contribution. Fordetails, visitcla.microsoft.com.

This project has adopted theMicrosoft Open Source Code of Conduct.For more information see theCode of Conduct FAQ or contactopencode@microsoft.com with any additional questions or comments.

Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, seeour contributor guide.

Feedback

Was this page helpful?

YesNoNo

Need help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?

  • Last updated on

In this article

Was this page helpful?

YesNo
NoNeed help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?