Azure.Security.ConfidentialLedger 1.3.0
前缀保留
dotnet add package Azure.Security.ConfidentialLedger --version 1.3.0
NuGet\Install-Package Azure.Security.ConfidentialLedger -Version 1.3.0
<PackageReference Include="Azure.Security.ConfidentialLedger" Version="1.3.0" />
paket add Azure.Security.ConfidentialLedger --version 1.3.0
#r "nuget: Azure.Security.ConfidentialLedger, 1.3.0"
// Install Azure.Security.ConfidentialLedger as a Cake Addin #addin nuget:?package=Azure.Security.ConfidentialLedger&version=1.3.0 // Install Azure.Security.ConfidentialLedger as a Cake Tool #tool nuget:?package=Azure.Security.ConfidentialLedger&version=1.3.0
Azure机密账本.NET客户端库
Azure机密账本提供了一种将日志记录到不可变、防篡改账本的服务。作为Azure机密计算系列服务的一部分,Azure机密账本在SGX区域能运行。它是基于Microsoft Research的机密联盟框架构建的。
入门指南
本节应包含开发人员需要执行的所有步骤来快速安装和创建他们的第一个客户端连接。
安装包
使用NuGet安装Azure机密账本.NET客户端库
dotnet add package Azure.Security.ConfidentialLedger
先决条件
- 一个Azure订阅。
- Azure机密账本正在运行的实例。
- 在 Azure 机密账本中拥有
管理员
权限的注册用户。
身份验证客户端
使用 Azure Active Directory
本文档演示了使用 DefaultAzureCredential 通过 Azure Active Directory 对机密账本进行身份验证。然而,Azure.Identity 提供的任何凭据都将被接受。有关其他凭据的更多信息,请参阅 Azure.Identity 文档。
使用客户端证书
作为 Azure Active Directory 的替代方案,客户端可以选择使用客户端证书通过互信 TLS 进行身份验证。
创建客户端
DefaultAzureCredential
将自动处理大多数 Azure SDK 客户端场景。要开始,请设置您与机密账本注册的 AAD 身份的环境变量。
export AZURE_CLIENT_ID="generated app id"
export AZURE_CLIENT_SECRET="random password"
export AZURE_TENANT_ID="tenant id"
然后,DefaultAzureCredential
将能够对 ConfidentialLedgerClient
进行身份验证。
构建客户端还需要您的机密账本的 URI,您可以在 Azure 门户中您的机密账本页面下的“属性”部分中的“账本 URI”字段中获取。当您检索到“账本 URI”后,请使用它替换以下示例中的“https://my-ledger-url.confidential-ledger.azure.com”。
var ledgerClient = new ConfidentialLedgerClient(new Uri("https://my-ledger-url.confidential-ledger.azure.com"), new DefaultAzureCredential());
安全提示:默认情况下,创建机密账本客户端时,它将连接到 Azure 的机密账本身份服务以获取您的账本的最新 TLS 服务证书,以确保与账本节点的连接安全。此过程的详细信息可在 此示例 中找到。此行为可通过在创建账本客户端时设置
options
参数来覆盖。
关键概念
账本条目
对 Azure 机密账本的每次写入都在服务中生成一个不可变的账本条目。写入操作由递增的交易 ID 唯一标识。
Operation postOperation = ledgerClient.PostLedgerEntry(
waitUntil: WaitUntil.Completed,
RequestContent.Create(
new { contents = "Hello world!" }));
string transactionId = postOperation.Id;
Console.WriteLine($"Appended transaction with Id: {transactionId}");
由于 Azure 机密账本是一个分布式系统,偶尔的暂时性故障可能会导致写入丢失。对于必须保留的条目,建议验证写入是否成为永久性。注意:可能需要多次调用 GetTransactionStatus
直到它返回“已提交”状态。然而,在调用 PostLedgerEntry
时,成功的结果表示状态是“已提交”。
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 committed
while (status == "Pending")
{
statusResponse = ledgerClient.GetTransactionStatus(transactionId);
status = JsonDocument.Parse(statusResponse.Content)
.RootElement
.GetProperty("state")
.GetString();
}
Console.WriteLine($"Transaction status: {status}");
收据
机密账本的状态更改保存在一个称为 Merkle 树的数据结构中。为了加密验证写入是否正确保存,可以检索任何交易 ID 的 Merkle 证明或收据。
Response receiptResponse = ledgerClient.GetReceipt(transactionId);
string receiptJson = new StreamReader(receiptResponse.ContentStream).ReadToEnd();
Console.WriteLine(receiptJson);
集合
虽然大多数用例将涉及一个账本,但我们提供集合功能,以便将不同的逻辑数据组存储在同一个机密账本中。
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" }));
在方法调用指定集合 ID 时,Azure 机密账本服务将假设一个固定的、由服务确定的集合 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}");
从集合中检索账本条目。当指定交易 ID 时,返回的值是在交易 ID 指定的时间点在指定集合中包含的值。如果没有指定交易 ID,则返回最新的可用值。
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 committed
status = "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"
范围查询
可以从交易 ID 的范围内检索集合中的账本条目。注意:两个范围都是可选的;可以单独提供或根本不提供。
ledgerClient.GetLedgerEntries(fromTransactionId: "2.1", toTransactionId: collectionTransactionId);
用户管理
用户直接通过机密账本进行管理,而不是通过 Azure。新用户可以是基于 AAD 或基于证书的。
string newUserAadObjectId = "<some AAD user or service princpal object Id>";
ledgerClient.CreateOrUpdateUser(
newUserAadObjectId,
RequestContent.Create(new { assignedRole = "Reader" }));
机密联盟和封禁区验证
出于各种原因,人们可能希望验证有关机密账簿的详细信息。例如,您可能想查看有关Microsoft如何作为机密联合体框架治理的一部分管理您的机密账簿的详细信息,或验证您的机密账簿是否确实在SGX enclave中运行。为此提供了一系列客户端方法。
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是SGX enclave报价的提供商之一。
线程安全
我们保证所有客户端实例方法都是线程安全的,彼此独立(指南)。这确保了重新使用客户端实例的建议始终是安全的,即使在多线程环境下也是。
其他概念
客户端选项 | 访问响应 | 长运行操作 | 处理失败 | 诊断 | 模拟 | 客户端生命周期
示例
即将推出...
故障排除
Azure机密账簿客户端方法返回的响应值是Response
对象,其中包含有关HTTP响应的信息,例如HTTP Status
属性和一个包含更多失败信息的Headers
对象。
设置控制台日志记录
查看日志的最简单方法是启用控制台日志记录。要创建将消息输出到控制台的自定义Azure SDK日志侦听器,请使用AzureEventSourceListener.CreateConsoleLogger方法。
// Setup a listener to monitor logged events.
using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();
有关其他日志记录机制的更多信息,请参阅此处。
下一步
有关Azure机密账簿的更详细文档,请参阅API 参考文档。您还可以阅读有关Microsoft Research开源机密联合体框架的更多信息。
贡献
本项目欢迎贡献和建议。大多数贡献需要您同意一份贡献者许可协议(CLA),声明您有权,并且实际上确实授权我们使用您的贡献。有关详细信息,请访问cla.microsoft.com。
本项目已采用Microsoft开源行为准则。有关更多信息,请参阅行为准则常见问题解答或通过[email protected]联系以提出任何其他问题或评论。
产品 | 版本 兼容和支持的附加计算目标框架版本。 |
---|---|
.NET | net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。 |
.NET Core | netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。 |
.NET Standard | netstandard2.0 兼容。 netstandard2.1 已计算。 |
.NET Framework | net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。 |
MonoAndroid | monoandroid 已计算。 |
MonoMac | monomac 已计算。 |
MonoTouch | monotouch 已计算。 |
Tizen | tizen40 已计算。 tizen60 已计算。 |
Xamarin.iOS | xamarinios 已计算。 |
Xamarin.Mac | xamarinmac 已计算。 |
Xamarin.TVOS | xamarintvos 已计算。 |
Xamarin.WatchOS | xamarinwatchos 已计算。 |
-
.NETStandard 2.0
- Azure.Core (>= 1.37.0)
- System.Text.Json (>= 4.7.2)
NuGet 包
此包没有被任何 NuGet 包使用。
GitHub 仓库
此包没有被任何流行的 GitHub 仓库使用。
版本 | 下载 | 最近更新 |
---|---|---|
1.3.0 | 15,606 | 2/5/2024 |
1.2.0 | 12,795 | 9/12/2023 |
1.1.0 | 16,957 | 11/9/2022 |
1.1.0-beta.1 | 782 | 8/10/2022 |
1.0.0 | 4,878 | 7/18/2022 |
1.0.0-beta.3 | 256 | 7/8/2022 |
1.0.0-beta.2 | 1,955 | 6/8/2021 |