Biblioteca de clientes do razão confidencial do Azure para .NET – versão 1.2.0

O razão confidencial do Azure fornece um serviço para fazer logon em um razão imutável e à prova de adulterações. Como parte do portfólio de Computação Confidencial do Azure , o razão confidencial do Azure é executado em enclaves SGX. Ele é baseado no Confidential Consortium Framework da Microsoft Research.

Código-fonte | Pacote (NuGet)

Introdução

Esta seção deve incluir tudo o que um desenvolvedor precisa fazer para instalar e criar sua primeira conexão de cliente muito rapidamente.

Instalar o pacote

Instale a biblioteca de clientes do razão confidencial do Azure para .NET com o NuGet:

dotnet add package Azure.Security.ConfidentialLedger

Pré-requisitos

  • Uma assinatura do Azure.
  • Uma instância em execução do razão confidencial do Azure.
  • Um usuário registrado no razão confidencial do Azure com Administrator privilégios.

Autenticar o cliente

Como usar o Azure Active Directory

Este documento demonstra como usar DefaultAzureCredential para autenticar no razão confidencial por meio do Azure Active Directory. No entanto, qualquer uma das credenciais oferecidas pelo Azure.Identity será aceita. Consulte a documentação do Azure.Identity para obter mais informações sobre outras credenciais.

Usando um certificado de cliente

Como alternativa ao Azure Active Directory, os clientes podem optar por usar um certificado de cliente para autenticar por meio de TLS mútuo.

Criar um cliente

DefaultAzureCredential lidará automaticamente com a maioria dos cenários de cliente do SDK do Azure. Para começar, defina variáveis de ambiente para a identidade do AAD registrada com seu razão confidencial.

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

Em seguida, DefaultAzureCredential poderá autenticar o ConfidentialLedgerClient.

A construção do cliente também requer o URI do razão confidencial, que você pode obter na página portal do Azure para seu razão confidencial no Ledger URI campo na Properties seção . Quando você tiver recuperado o , use-o Ledger URIpara substituí-lo "https://my-ledger-url.confidential-ledger.azure.com" no exemplo abaixo.

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

Observação de segurança: por padrão, quando um cliente do razão confidencial é criado, ele se conecta ao Serviço de Identidade do razão confidencial do Azure para obter o certificado de serviço TLS mais recente para seu Razão, a fim de proteger conexões com nós do Razão. Os detalhes desse processo estão disponíveis neste exemplo. Esse comportamento pode ser substituído definindo o options argumento ao criar o Cliente do Razão.

Principais conceitos

Entradas do razão

Cada gravação no razão confidencial do Azure gera uma entrada do razão imutável no serviço. As gravações são identificadas exclusivamente por IDs de transação que incrementam com cada gravação.

Operation postOperation = ledgerClient.PostLedgerEntry(
    waitUntil: WaitUntil.Completed,
    RequestContent.Create(
        new { contents = "Hello world!" }));

string transactionId = postOperation.Id;
Console.WriteLine($"Appended transaction with Id: {transactionId}");

Como o razão confidencial do Azure é um sistema distribuído, falhas transitórias raras podem fazer com que as gravações sejam perdidas. Para entradas que devem ser preservadas, é aconselhável verificar se a gravação se tornou durável. Observação: pode ser necessário chamar GetTransactionStatus várias vezes até que retorne um status "Confirmado". No entanto, ao chamar PostLedgerEntry, um resultado bem-sucedido indica que o status é "Confirmado".

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}");

Recebimentos

As alterações de estado em um razão confidencial são salvas em uma estrutura de dados chamada árvore Merkle. Para verificar criptograficamente se as gravações foram salvas corretamente, uma prova merkle ou recibo pode ser recuperada para qualquer ID de transação.

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

Console.WriteLine(receiptJson);

Coleções

Embora a maioria dos casos de uso envolva um razão, fornecemos o recurso de coleções caso diferentes grupos lógicos de dados precisem ser armazenados no mesmo razão confidencial.

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" }));

Quando nenhuma ID de coleção for especificada em chamadas de método, o serviço do razão confidencial do Azure assumirá uma ID de coleção constante determinada pelo serviço.

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}");

As entradas do razão são recuperadas de coleções. Quando uma ID de transação é especificada, o valor retornado é o valor contido na coleção especificada no ponto no tempo identificado pela ID da transação. Se nenhuma ID de transação for especificada, o valor mais recente disponível será retornado.

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"
Consultas com intervalos

As entradas do razão em uma coleção podem ser recuperadas em um intervalo de IDs de transação. Observação: ambos os intervalos são opcionais; eles podem ser fornecidos individualmente ou não.

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

Gerenciamento de Usuários

Os usuários são gerenciados diretamente com o razão confidencial em vez de por meio do Azure. Novos usuários podem ser baseados no AAD ou em certificado.

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

Verificações confidenciais de consórcio e enclave

Pode-se querer validar detalhes sobre o razão confidencial por vários motivos. Por exemplo, talvez você queira exibir detalhes sobre como a Microsoft pode gerenciar seu razão confidencial como parte da governança do Confidential Consortium Framework ou verificar se o razão confidencial está realmente em execução em enclaves SGX. Vários métodos de cliente são fornecidos para esses casos de uso.

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

O Microsoft Atestado do Azure Service é um provedor de cotações de enclave SGX.

Acesso thread-safe

Garantimos que todos os métodos de instância do cliente sejam thread-safe e independentes uns dos outros (diretriz). Isso garante que a recomendação de reutilize instâncias de cliente seja sempre segura, mesmo entre threads.

Conceitos adicionais

Opções do | clienteAcessando a resposta | Operações de execução longa | Tratamento de falhas | Diagnostics | Zombando | Tempo de vida do cliente

Exemplos

Em breve...

Solução de problemas

Os valores de resposta retornados dos métodos de cliente do razão confidencial do Azure são Response objetos, que contêm informações sobre a resposta http, como a propriedade http Status e um Headers objeto que contém mais informações sobre a falha.

Configuração do registro em log do console

A maneira mais simples de ver os logs é habilitar o log do console. Para criar um ouvinte de log do SDK do Azure que gera mensagens para o console, use o método AzureEventSourceListener.CreateConsoleLogger.

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

Para saber mais sobre outros mecanismos de registro em log, confira aqui.

Próximas etapas

Para obter uma documentação mais abrangente sobre o razão confidencial do Azure, consulte a documentação de referência da API. Você também pode ler mais sobre a Estrutura de Consórcio Confidencial de software livre da Microsoft Research.

Contribuição

Este projeto aceita contribuições e sugestões. A maioria das contribuições exige que você concorde com um CLA (Contrato de Licença do Colaborador) declarando que você tem o direito de nos conceder, e de fato concede, os direitos de usar sua contribuição. Para obter detalhes, visite cla.microsoft.com.

Este projeto adotou o Código de Conduta de Software Livre da Microsoft. Para obter mais informações, confira as Perguntas frequentes sobre o Código de Conduta ou contate opencode@microsoft.com para enviar outras perguntas ou comentários.

Impressões