Bibliothèque cliente de registre confidentiel Azure pour .NET - version 1.2.0

Le registre confidentiel Azure fournit un service de journalisation dans un registre immuable et inviolable. Dans le cadre du portefeuille d’informatique confidentielle Azure , le registre confidentiel Azure s’exécute dans les enclaves SGX. Il s’appuie sur l’infrastructure de consortium confidentiel de Microsoft Research.

| Code sourcePackage (NuGet)

Prise en main

Cette section doit inclure tout ce qu’un développeur doit faire pour installer et créer sa première connexion client très rapidement.

Installer le package

Installez la bibliothèque cliente de registre confidentiel Azure pour .NET avec NuGet :

dotnet add package Azure.Security.ConfidentialLedger

Prérequis

  • Un abonnement Azure.
  • Une instance en cours d’exécution du registre confidentiel Azure.
  • Un utilisateur inscrit dans le registre confidentiel Azure avec Administrator des privilèges.

Authentifier le client

Utilisation d’Azure Active Directory

Ce document illustre l’utilisation de DefaultAzureCredential pour s’authentifier auprès du registre confidentiel via Azure Active Directory. Toutefois, toutes les informations d’identification proposées par Azure.Identity seront acceptées. Pour plus d’informations sur d’autres informations d’identification, consultez la documentation Azure.Identity .

Utilisation d’un certificat client

En guise d’alternative à Azure Active Directory, les clients peuvent choisir d’utiliser un certificat client pour s’authentifier via TLS mutuel.

Créer un client

DefaultAzureCredential gère automatiquement la plupart des scénarios de client du Kit de développement logiciel (SDK) Azure. Pour commencer, définissez des variables d’environnement pour l’identité AAD inscrite auprès de votre registre confidentiel.

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

Ensuite, DefaultAzureCredential sera en mesure d’authentifier le ConfidentialLedgerClient.

La construction du client nécessite également l’URI de votre registre confidentiel, que vous pouvez obtenir à partir de la page du portail Azure pour votre registre confidentiel dans le Ledger URI champ sous la Properties section . Une fois que vous avez récupéré le , utilisez-le Ledger URIpour le remplacer "https://my-ledger-url.confidential-ledger.azure.com" dans l’exemple ci-dessous.

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

Remarque de sécurité : par défaut, lorsqu’un client de registre confidentiel est créé, il se connecte au service d’identité du registre confidentiel d’Azure pour obtenir le dernier certificat de service TLS pour votre registre afin de sécuriser les connexions aux nœuds du registre. Les détails de ce processus sont disponibles dans cet exemple. Ce comportement peut être remplacé en définissant l’argument lors de la options création du client de registre.

Concepts clés

Entrées de registre

Chaque écriture dans le registre confidentiel Azure génère une entrée de registre immuable dans le service. Les écritures sont identifiées de manière unique par des ID de transaction qui s’incrémentent à chaque écriture.

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

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

Étant donné que le registre confidentiel Azure est un système distribué, de rares défaillances temporaires peuvent entraîner la perte d’écritures. Pour les entrées qui doivent être conservées, il est conseillé de vérifier que l’écriture est devenue durable. Remarque : Il peut être nécessaire d’appeler GetTransactionStatus plusieurs fois jusqu’à ce qu’il retourne un status « Commit ». Toutefois, lors de l’appel PostLedgerEntryde , un résultat réussi indique que le status est « Commit ».

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

Reçus

Les modifications d’état apportées au registre confidentiel sont enregistrées dans une structure de données appelée arborescence Merkle. Pour vérifier par chiffrement que les écritures ont été correctement enregistrées, une preuve Merkle, ou un reçu, peut être récupéré pour n’importe quel ID de transaction.

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

Console.WriteLine(receiptJson);

Collections

Bien que la plupart des cas d’usage impliquent un seul registre, nous fournissons la fonctionnalité collections au cas où différents groupes logiques de données doivent être stockés dans le même registre confidentiel.

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

Lorsqu’aucun ID de collection n’est spécifié lors des appels de méthode, le service de registre confidentiel Azure suppose un ID de collection constant déterminé par le service.

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

Les entrées de registre sont récupérées à partir de collections. Lorsqu’un ID de transaction est spécifié, la valeur retournée correspond à la valeur contenue dans la collection spécifiée au point dans le temps identifié par l’ID de transaction. Si aucun ID de transaction n’est spécifié, la dernière valeur disponible est retournée.

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"
Requêtes étendues

Les entrées de registre d’une collection peuvent être récupérées sur une plage d’ID de transaction. Remarque : Les deux plages sont facultatives ; ils peuvent être fournis individuellement ou pas du tout.

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

User Management

Les utilisateurs sont gérés directement avec le registre confidentiel au lieu d’Azure. Les nouveaux utilisateurs peuvent être basés sur AAD ou sur certificat.

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

Vérifications confidentielles des consortiums et des enclaves

Vous pouvez valider les détails du registre confidentiel pour diverses raisons. Par exemple, vous pouvez afficher des détails sur la façon dont Microsoft peut gérer votre registre confidentiel dans le cadre de la gouvernance de l’infrastructure du consortium confidentiel, ou vérifier que votre registre confidentiel s’exécute bien dans des enclaves SGX. Un certain nombre de méthodes clientes sont fournies pour ces cas d’usage.

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 est un fournisseur de guillemets d’enclave SGX.

Sécurité des threads

Nous garantissons que toutes les méthodes de instance client sont sécurisées pour les threads et indépendantes les unes des autres (recommandations). Cela garantit que la recommandation de réutilisation des instances clientes est toujours sécurisée, même entre les threads.

Concepts supplémentaires

Options clientes | Accès à la réponse | Opérations de longue durée | Gestion des défaillances | Diagnostics | Moqueur | Durée de vie du client

Exemples

À venir...

Dépannage

Les valeurs de réponse retournées par les méthodes clientes du registre confidentiel Azure sont Response des objets, qui contiennent des informations sur la réponse http telles que la propriété http Status et un Headers objet contenant plus d’informations sur l’échec.

Configuration de la journalisation de la console

Le moyen le plus simple de voir les journaux consiste à activer la journalisation de la console. Pour créer un écouteur de journal du Kit de développement logiciel (SDK) Azure qui génère des messages dans la console, utilisez la méthode AzureEventSourceListener.CreateConsoleLogger.

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

Pour en savoir plus sur les autres mécanismes de journalisation , consultez ici.

Étapes suivantes

Pour obtenir une documentation plus complète sur le registre confidentiel Azure, consultez la documentation de référence sur l’API. Vous pouvez également en savoir plus sur l’infrastructure de consortium confidentiel open source de Microsoft Research.

Contribution

Ce projet accepte les contributions et les suggestions. La plupart des contributions vous demandent d’accepter un contrat de licence de contribution (CLA) déclarant que vous avez le droit de nous accorder, et que vous nous accordez réellement, les droits d’utilisation de votre contribution. Pour plus d’informations, consultez cla.microsoft.com.

Ce projet a adopté le Code de conduite Open Source de Microsoft. Pour plus d’informations, consultez les Questions fréquentes (FAQ) sur le code de conduite ou envoyez vos questions ou vos commentaires à opencode@microsoft.com.

Impressions