Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
C#chevron-right

Como usar a criptografia no nível do campo do lado do cliente (CSFLE) do MongoDB com C#

Adrienne Tacke18 min read • Published Feb 18, 2022 • Updated Sep 23, 2022
MongoDBSegurançaC#
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse exemplo de código
star-empty
star-empty
star-empty
star-empty
star-empty
A criptografia no nível do campo do lado do cliente (CSFLE) fornece uma camada adicional de segurança para seus dados mais confidenciais. Usando um driverMongoDB compatível, o CSFLE criptografa determinados campos que você especifica, garantindo que eles nunca sejam transmitidos sem criptografia, nem vistos sem criptografia pelo servidor MongoDB.
Esta pode ser a única vez que uso um Gif dos Transformers. GIFs de criptografia são difíceis de encontrar!
Esta pode ser a única vez que uso um Gif dos Transformers. GIFs de criptografia são difíceis de encontrar!
Isso também significa que é quase impossível obter informações confidenciais do servidor de banco de dados. Sem acesso a uma chave específica, os dados não podem ser descriptografados e expostos, tornando os dados interceptados do cliente infrutíferos. A leitura de dados diretamente do disco, mesmo com credenciais de DBA ou root, também será impossível, pois os dados são armazenados em um estado criptografado.
Os principais aplicativos que mostram o poder da criptografia em nível de campo do lado do cliente são aqueles na área médica. Se você pensar rapidamente na última vez que visitou uma clínica, já tem um caso de uso eficaz para um aplicativo que requer uma combinação de campos criptografados e não criptografados. Quando você faz check-in em uma consulta, a pessoa pode precisar procurar por você por nome ou fornecedor de seguro. Esses são campos de dados comuns que geralmente não são criptografados. Depois, há informações mais óbvias que exigem criptografia: coisas como o número do Seguro Social, registros médicos ou o número da apólice de seguro. Para estes campos de dados, a criptografia é necessária.
Este tutorial orientará você na configuração de um sistema médico semelhante que usa criptografiaautomática em nível de campo do lado do cliente no MongoDB .NET Driver (para criptografia explícita, ou seja, manual em nível de campo no lado do cliente, confira esses Docs).
Nele, você vai:
Prepare um aplicativo de console do .NET Core
Gere chaves aleatórias seguras necessárias para o CSFLE
Configure o CSFLE no MongoClient
Veja o CSFLE em ação
💡️ Esse pode ser um tutorial intimidante, então não hesite em fazer quantas pausas precisar; na verdade, conclua as etapas em alguns dias! Eu tentei o meu melhor para garantir que cada etapa concluída atue como um ponto de salvamento natural durante todo o tutorial. :)
Vamos fazer isso passo a passo!

Pré-requisitos

. . O código deste tutorial está disponível neste repositório.

Crie um aplicativo de console .NET Core

Vamos começar criando o scaffolding do nosso aplicativo de console. Abra o Visual Studio (estou usando o Visual Studio 2019 Community Edition) e crie um novo projeto. Ao selecionar um modelo, escolha a opção " Console App (.NET Core) " e siga as instruções para nomear seu projeto.
O Visual Studio 2019 cria um novo prompt de projeto; A opção Aplicativo de console (.NET Core) está realçada.
O Visual Studio 2019 cria um novo prompt de projeto; A opção Aplicativo de console (.NET Core) está realçada.

Instalar dependências CSFLE

Depois que o modelo do projeto for carregado, precisaremos instalar uma de nossas dependências. No Console do Gerenciador de pacotes, use o seguinte comando para instalar o driver MongoDB:
1Install-Package MongoDB.Driver -Version 2.12.0-beta1
💡️ Se o console do gerenciador de pacotes não estiver visível no IDE, você poderá acessá-lo em Exibir > Outro Windows > Console do gerenciador de pacotes no menu Arquivo.
A próxima dependência que você precisará instalar é mongocryptd, que é um aplicativo fornecido como parte do MongoDB Enterprise e é necessário para a criptografia automática em nível de campo. Siga as instruções para instalar o mongocryptd na sua máquina. Em um ambiente de produção, é recomendável executar o mongocryptd como um serviço na inicialização de sua VM ou container.
Agora que nosso projeto base e dependências estão definidos, podemos passar para a criação e configuração de nossas diferentes chaves de criptografia.
A criptografia em nível de campo do lado do cliente MongoDB usa uma estratégia de criptografia chamada criptografiade envelope. Esta estratégia usa dois tipos diferentes de chaves.
A primeira chave é chamada de chave de criptografia de dados, usada para criptografar/descriptografar os dados que você armazenará no MongoDB. A outra chave é chamada de chave mestra e é usada para criptografar a chave de criptografia de dados. Essa é a chave de texto simples de nível superior que sempre será necessária e é a chave que geraremos na próxima etapa.
🚨️ Antes de prosseguirmos, é importante observar que este tutorial demonstrará a geração de um arquivo de chave mestra armazenado como texto simples na raiz de nossa aplicação. Isso é adequado para fins educacionais e dedesenvolvimento , como este tutorial. Entretanto, isso NÃO deve ser feito em um ambiente deprodução !
Por quê? Nesse cenário, qualquer pessoa que obtiver uma cópia do disco ou um instantâneo de VM do servidor de aplicativos que hospeda nosso aplicativo também terá acesso a esse arquivo de chave, possibilitando o acesso aos dados do aplicativo.
Em vez disso, você deve configurar uma chave mestra em um sistema de gerenciamento de chaves, como o Azure Key Vault ou o AWS KMS, para produção.
Tenha isso em mente e assista a outra publicação que mostra como implementar o CSFLE com o Azure Key Vault!

Criar uma chave mestra local

Nesta etapa, geramos uma chave mestra de 96bytes, gerenciada localmente. Em seguida, a salvamos em um arquivo local chamado master-key.txt. Faremos mais algumas coisas com as chaves, portanto, crie uma classe separada chamadaKmsKeyHelper.cs. Em seguida, adicione o seguinte código a ela:
1// KmsKeyHelper.cs
2
3using System;
4using System.IO;
5
6namespace EnvoyMedSys
7{
8 public class KmsKeyHelper
9 {
10 private readonly static string __localMasterKeyPath = "../../../master-key.txt";
11
12 public void GenerateLocalMasterKey()
13 {
14 using (var randomNumberGenerator = System.Security.Cryptography.RandomNumberGenerator.Create())
15 {
16 var bytes = new byte[96];
17 randomNumberGenerator.GetBytes(bytes);
18 var localMasterKeyBase64 = Convert.ToBase64String(bytes);
19 Console.WriteLine(localMasterKeyBase64);
20 File.WriteAllText(__localMasterKeyPath, localMasterKeyBase64);
21 }
22 }
23 }
24}
Então, o que está acontecendo aqui? Vamos dividi-lo, linha por linha:
Primeiro, declaramos e definimos uma variável privada chamada __localMasterKeyPath. Este contém o caminho para onde salvamos nossa chave mestra.
Em seguida, criamos um métodoGenerateLocalMasterKey(). Neste método, usamos os serviços de criptografiado .NET para criar uma instância de um RandomNumberGenerator. Usando este RandomNumberGenerator, geramos uma chave criptograficamente forte de 96bytes. Depois de convertê-la em uma representação do Base64 , salvamos a chave no arquivomaster-key.txt.
Ótimo! Agora temos uma maneira de gerar uma chave mestra local. Vamos modificar o programa principal para usá-lo. No arquivoProgram.cs, adicione o seguinte código:
1// Program.cs
2
3using System;
4using System.IO;
5
6namespace EnvoyMedSys
7{
8 class Program
9 {
10 public static void Main()
11 {
12 var kmsKeyHelper = new KmsKeyHelper();
13
14 // Ensure GenerateLocalMasterKey() only runs once!
15 if (!File.Exists("../../../master-key.txt"))
16 {
17 kmsKeyHelper.GenerateLocalMasterKey();
18 }
19
20 Console.ReadKey();
21 }
22 }
23}
No método Main, criamos uma instância de nossoKmsKeyHelper e, em seguida, chamamos nosso métodoGenerateLocalMasterKey(). Muito simples!
Salve todos os arquivos e, em seguida, execute o programa. Se tudo der certo, você verá um console aparecer e a representação Base64 da sua chave mestra recém-gerada será impressa no console. Você também verá um novo arquivomaster-key.txtaparecer no explorador de soluções.
Agora que temos uma chave mestre, podemos criar uma chave de criptografia de dados.

Criar uma chave de criptografia de dados

A próxima chave que precisamos gerar é uma chave de criptografia de dados. Essa é a chave que o driver do MongoDB armazena em uma collection de cofre de chaves e é usada para criptografia e descriptografia automáticas.
A criptografia automática requer MongoDB Enterprise 4.2 ou um MongoDB 4.2 Atlas cluster.No entanto, adescriptografiaautomática é suportada para todos os usuários. Veja como configurar a descriptografia automática sem criptografia automática.
Vamos adicionar mais algumas linhas de código ao arquivoProgram.cs:
1using System;
2using System.IO;
3using MongoDB.Driver;
4
5namespace EnvoyMedSys
6{
7 class Program
8 {
9 public static void Main()
10 {
11 var connectionString = Environment.GetEnvironmentVariable("MDB_URI");
12 var keyVaultNamespace = CollectionNamespace.FromFullName("encryption.__keyVault");
13
14 var kmsKeyHelper = new KmsKeyHelper(
15 connectionString: connectionString,
16 keyVaultNamespace: keyVaultNamespace);
17
18 string kmsKeyIdBase64;
19
20 // Ensure GenerateLocalMasterKey() only runs once!
21 if (!File.Exists("../../../master-key.txt"))
22 {
23 kmsKeyHelper.GenerateLocalMasterKey();
24 }
25
26 kmsKeyIdBase64 = kmsKeyHelper.CreateKeyWithLocalKmsProvider();
27
28 Console.ReadKey();
29 }
30 }
31}
Então, o que mudou? Primeiro, adicionamos uma importação adicional (MongoDB.Driver). Em seguida, declaramos uma variávelconnectionString e uma keyVaultNamespace.
Para o namespace do key vault, o MongoDB criará automaticamente o banco de dados encryption e a coleção __keyVault se ele não existir atualmente. Os nomes do banco de dados e da coleção eram puramente minha preferência. Você pode optar por nomeá-los com outro nome, se desejar!
Em seguida, modificamos a instanciação KmsKeyHelperpara aceitar dois parâmetros: a string de conexão e o namespace do cofre de chaves que declaramos anteriormente. Não se preocupe, em breve alteraremos nosso arquivoKmsKeyHelper.cs para corresponder a isso.
Finalmente, declaramos uma variávelkmsKeyIdBase64 e a definimos para um novo método que criaremos em breve: CreateKeyWithLocalKmsProvider();. Isso manterá nossa chave de criptografia de dados.

Configurando com segurança a conexão do MongoDB

Em nosso código, definimos nosso URI MongoDB extraindo de variáveis de ambiente. Isso é muito mais seguro do que colar uma string de conexão diretamente em nosso código e é dimensionável em uma variedade de cenários de implementação automatizada.
Para nossos propósitos, criaremos um arquivolaunchSettings.json.
} Não confirme o arquivolaunchSettings.json em um repositório público! Na verdade, adicione-o ao seu arquivo.gitignore agora, se você tiver um ou planeja compartilhar este aplicativo. Caso contrário, você exporá seu URI MongoDB para o mundo!
Clique com o botão direito do mouse em seu projeto e selecione " Propriedades " no menu de contexto.
As propriedades do projeto serão abertas na seção "Debug". Na área "Environment variables:", adicione uma variável chamada MDB_URI, seguida do URI de conexão:
Adicionando uma variável de ambiente às configurações do projeto no Visual Studio 2019.
Adicionando uma variável de ambiente às configurações do projeto no Visual Studio 2019.
Que valor você define para a variável de ambienteMDB_URI?
  • MongoDB Atlas: Se estiver usando um MongoDB Atlas cluster, cole seu URI do Atlas.
  • Local: se estiver executando uma instância local do MongoDB e não tiver alterado nenhuma configuração padrão, poderá usar a connection string padrão: mongodb://localhost:27017.
Após adicionar o MDB_URI, salve as propriedades do projeto. Você verá que um arquivolaunchSettings.json será gerado automaticamente para você! Agora, quaisquer chamadasEnvironment.GetEnvironmentVariable() serão extraídas deste arquivo.
Com estas alterações, agora temos que modificar e adicionar mais alguns métodos à classeKmsKeyHelper. Vamos fazer isso agora.
Primeiro, adicione essas importações adicionais:
1// KmsKeyHelper.cs
2
3using System.Collections.Generic;
4using System.Threading;
5using MongoDB.Bson;
6using MongoDB.Driver;
7using MongoDB.Driver.Encryption;
Em seguida, declare duas variáveis privadas e crie um construtor que aceite uma connection string e um namespace do cofre de chaves. Precisaremos dessas informações para criar nossa chave de criptografia de dados; isso também facilita a extensão e a integração com um KMS remoto posteriormente.
1// KmsKeyhelper.cs
2
3private readonly string _mdbConnectionString;
4private readonly CollectionNamespace _keyVaultNamespace;
5
6public KmsKeyHelper(
7 string connectionString,
8 CollectionNamespace keyVaultNamespace)
9{
10 _mdbConnectionString = connectionString;
11 _keyVaultNamespace = keyVaultNamespace;
12}
Após o método GenerateLocalMasterKey(), adicione os seguintes novos métodos. Não se preocupar, analisaremos cada um deles:
1// KmsKeyHelper.cs
2
3public string CreateKeyWithLocalKmsProvider()
4{
5 // Read Master Key from file & convert
6 string localMasterKeyBase64 = File.ReadAllText(__localMasterKeyPath);
7 var localMasterKeyBytes = Convert.FromBase64String(localMasterKeyBase64);
8
9 // Set KMS Provider Settings
10 // Client uses these settings to discover the master key
11 var kmsProviders = new Dictionary<string, IReadOnlyDictionary<string, object>>();
12 var localOptions = new Dictionary<string, object>
13 {
14 { "key", localMasterKeyBytes }
15 };
16 kmsProviders.Add("local", localOptions);
17
18 // Create Data Encryption Key
19 var clientEncryption = GetClientEncryption(kmsProviders);
20 var dataKeyid = clientEncryption.CreateDataKey("local", new DataKeyOptions(), CancellationToken.None);
21 clientEncryption.Dispose();
22 Console.WriteLine($"Local DataKeyId [UUID]: {dataKeyid}");
23
24 var dataKeyIdBase64 = Convert.ToBase64String(GuidConverter.ToBytes(dataKeyid, GuidRepresentation.Standard));
25 Console.WriteLine($"Local DataKeyId [base64]: {dataKeyIdBase64}");
26
27 // Optional validation; checks that key was created successfully
28 ValidateKey(dataKeyid);
29 return dataKeyIdBase64;
30}
Este método é o que chamamos no programa principal. É aqui que geramos nossa chave de criptografia de dados. As linhas 6a7 leem a chave mestra local do nosso arquivomaster-key.txt e a convertem em uma array de bytes.
As linhas 11a16 definem as configurações do provedor de KMS que o cliente precisa para descobrir a chave mestre. Como você pode ver, adicionamos o provedor local e a chave mestra local correspondente que acabamos de recuperar.
Com essas configurações do provedor KMS, construímos configurações adicionais de criptografia do cliente. Fazemos isso em um método separado chamado GetClientEncryption(). Depois de criada, finalmente geramos uma chave criptografada.
Como medida adicional, chamamos um terceiro novo método ValidateKey(), apenas para garantir que a chave de criptografia de dados foi criada. Após essas etapas, e se for bem-sucedido, o métodoCreateKeyWithLocalKmsProvider()retorna o ID da nossa chave de dados codificado no formato Base64.
Após o método CreateKeyWithLocalKmsProvider(), adicione o seguinte método:
1// KmsKeyHelper.cs
2
3private ClientEncryption GetClientEncryption(
4 Dictionary<string, IReadOnlyDictionary<string, object>> kmsProviders)
5{
6 var keyVaultClient = new MongoClient(_mdbConnectionString);
7 var clientEncryptionOptions = new ClientEncryptionOptions(
8 keyVaultClient: keyVaultClient,
9 keyVaultNamespace: _keyVaultNamespace,
10 kmsProviders: kmsProviders);
11
12 return new ClientEncryption(clientEncryptionOptions);
13}
No método CreateKeyWithLocalKmsProvider(), chamamosGetClientEncryption() (o método que acabamos de adicionar) para criar nossas configurações de criptografia de cliente. Essas configurações incluem o cliente do cofre de chaves, o namespace do cofre de chaves e os provedores de KMS a serem usados.
Neste método, construímos um MongoClient usando a connection string e, em seguida, definimos como um cliente de cofre de chaves. Também usamos o namespace do cofre de chaves que foi passado e os provedores KMS locais que construímos anteriormente. Essas opções de criptografia do cliente são então retornadas.
Por último, mas não menos importante, depois de GetClientEncryption(), adicione o método final:
1// KmsKeyHelper.cs
2
3private void ValidateKey(Guid dataKeyId)
4{
5 var client = new MongoClient(_mdbConnectionString);
6 var collection = client
7 .GetDatabase(_keyVaultNamespace.DatabaseNamespace.DatabaseName)
8 #pragma warning disable CS0618 // Type or member is obsolete
9 .GetCollection<BsonDocument>(_keyVaultNamespace.CollectionName, new MongoCollectionSettings { GuidRepresentation = GuidRepresentation.Standard });
10 #pragma warning restore CS0618 // Type or member is obsolete
11
12 var query = Builders<BsonDocument>.Filter.Eq("_id", new BsonBinaryData(dataKeyId, GuidRepresentation.Standard));
13 var keyDocument = collection
14 .Find(query)
15 .Single();
16
17 Console.WriteLine(keyDocument);
18}
Embora opcional, esse método verifica convenientemente se a chave de criptografia de dados foi criada corretamente. Ele faz isso construindo um MongoClient usando a connection string especificada e, em seguida, consulta o banco de dados em busca da chave de criptografia de dados. Se tivesse sido criada com sucesso, a chave de criptografia de dados teria sido inserida como um documento em seu conjunto de réplicas e seria recuperada na consulta.
Com essas mudanças, estamos prontos para gerar nossa chave de criptografia de dados. Certifique-se de salvar todos os arquivos e, em seguida, execute seu programa. Se tudo correr bem, seu console imprimirá dois DataKeyIds (UUID e64base), bem como um documento semelhante ao seguinte:
1{
2 "_id" : CSUUID("aae4f3b4-91b6-4cef-8867-3113a6dfb27b"),
3 "keyMaterial" : Binary(0, "rcfTQLRxF1mg98/Jr7iFwXWshvAVIQY6JCswrW+4bSqvLwa8bQrc65w7+3P3k+TqFS+1Ce6FW4Epf5o/eqDyT//I73IRc+yPUoZew7TB1pyIKmxL6ABPXJDkUhvGMiwwkRABzZcU9NNpFfH+HhIXjs324FuLzylIhAmJA/gvXcuz6QSD2vFpSVTRBpNu1sq0C9eZBSBaOxxotMZAcRuqMA=="),
4 "creationDate" : ISODate("2020-11-08T17:58:36.372Z"),
5 "updateDate" : ISODate("2020-11-08T17:58:36.372Z"),
6 "status" : 0,
7 "masterKey" : {
8 "provider" : "local"
9 }
10}
Para referência, aqui está a aparência da saída do meu console:
Saída do console mostrando dois IDs de chave de dados e um objeto de dados; esses são sinais bem-sucedidos de uma chave de encriptação de dados gerada corretamente.
Saída do console mostrando dois IDs de chave de dados e um objeto de dados; esses são sinais bem-sucedidos de uma chave de encriptação de dados gerada corretamente.
Se você quiser ter mais certeza, também poderá verificar seu cluster para ver se sua chave de criptografia de dados está armazenada como um documento no banco de dados de criptografia recém-criado e __keyVault coleção. Como estou me conectando com meu Atlas cluster, aqui está a aparência:
Chave de encriptação de dados salva no MongoDB Atlas
Chave de encriptação de dados salva no MongoDB Atlas
Doce! Agora que geramos uma chave de criptografia de dados, que foi criptografada com nossa chave mestre local, a próxima etapa é especificar quais campos em nosso aplicativo devem ser criptografados.

Especifique campos criptografados usando um JSON schema

Para que a criptografia e a descriptografia automáticas no lado do cliente funcionem, é necessário definir um JSON schema que especifique quais campos devem ser criptografados, quais algoritmos de criptografia devem ser usados e o tipo BSON de cada campo.
Usando nosso aplicativo médico como exemplo, vamos planejar a criptografia dos seguintes campos:
Campos a serem criptografados
Nome do campoAlgoritmos de criptografiaTipo de JSON
SSN (Número de Segurança Social)Determinístico(a)Int
Tipo sanguíneoAleatórioString
Registros médicosAleatórioArray
Seguro: número da apóliceDeterminístico(a)Int (incorporado dentro do objeto de seguro)
Para facilitar um pouco e separar essa funcionalidade do restante do aplicativo, crie outra classe chamada JsonSchemaCreator.cs. Nela, adicione o seguinte código:
1// JsonSchemaCreator.cs
2
3using MongoDB.Bson;
4using System;
5
6namespace EnvoyMedSys
7{
8 public static class JsonSchemaCreator
9 {
10 private static readonly string DETERMINISTIC_ENCRYPTION_TYPE = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic";
11 private static readonly string RANDOM_ENCRYPTION_TYPE = "AEAD_AES_256_CBC_HMAC_SHA_512-Random";
12
13 private static BsonDocument CreateEncryptMetadata(string keyIdBase64)
14 {
15 var keyId = new BsonBinaryData(Convert.FromBase64String(keyIdBase64), BsonBinarySubType.UuidStandard);
16 return new BsonDocument("keyId", new BsonArray(new[] { keyId }));
17 }
18
19 private static BsonDocument CreateEncryptedField(string bsonType, bool isDeterministic)
20 {
21 return new BsonDocument
22 {
23 {
24 "encrypt",
25 new BsonDocument
26 {
27 { "bsonType", bsonType },
28 { "algorithm", isDeterministic ? DETERMINISTIC_ENCRYPTION_TYPE : RANDOM_ENCRYPTION_TYPE}
29 }
30 }
31 };
32 }
33
34 public static BsonDocument CreateJsonSchema(string keyId)
35 {
36 return new BsonDocument
37 {
38 { "bsonType", "object" },
39 { "encryptMetadata", CreateEncryptMetadata(keyId) },
40 {
41 "properties",
42 new BsonDocument
43 {
44 { "ssn", CreateEncryptedField("int", true) },
45 { "bloodType", CreateEncryptedField("string", false) },
46 { "medicalRecords", CreateEncryptedField("array", false) },
47 {
48 "insurance",
49 new BsonDocument
50 {
51 { "bsonType", "object" },
52 {
53 "properties",
54 new BsonDocument
55 {
56 { "policyNumber", CreateEncryptedField("int", true) }
57 }
58 }
59 }
60 }
61 }
62 }
63 };
64 }
65 }
66}
Como antes, vamos percorrer cada linha:
Primeiro, criamos duas variáveis estáticas para manter nossos tipos de criptografia. Usamos a criptografia Deterministicpara campos que podem ser consultados e têm alta cardinalidade. Usamos a criptografiaRandom para campos que não planejamos consultar, que têm baixa cardinalidade ou que são campos de matriz.
Em seguida, criamos um método auxiliar CreateEncryptMetadata() . Isso retornará umBsonDocument que contém nossa chave de dados convertida. Usaremos esta chave no métodoCreateJsonSchema().
As linhas 19-32 compõem outro método auxiliar chamado CreateEncryptedField().Isso gera oBsonDocumentadequado necessário para definir nossos campos criptografados. Ele produzirá um BsonDocument que se assemelha ao seguinte:
1"ssn": {
2 "encrypt": {
3 "bsonType": "int",
4 "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
5 }
6}
Finalmente, o métodoCreateJsonSchema(). Aqui, geramos o esquema completo que nosso aplicativo usará para saber quais campos criptografar e descriptografar. Este método também retorna um BsonDocument.
Algumas coisas a serem observadas sobre este esquema:
Colocar a chaveencryptMetadata na raiz do nosso esquema nos permite criptografar todos os campos com uma única chave de dados. É aqui que você vê a chamada para nosso método auxiliarCreateEncryptMetadata() .
Dentro da chaveproperties Go todos os campos que desejamos criptografar. Portanto, para nossos camposssn, bloodType, medicalRecordse insurance.policyNumber, geramos as respectivas especificaçõesBsonDocument necessárias usando nosso método auxiliarCreateEncryptedField() .
Com nossos campos criptografados definidos e as chaves de criptografia necessárias geradas, podemos agora ativar a criptografia no nível do campo do lado do cliente em nosso cliente MongoDB!
Não se esqueça de fazer uma pausa! ☕️ São muitas informações para absorver, então não se apresse. Certifique-se de salvar todos os seus arquivos, depois pegue um café, espreguice-se e afaste-se do computador. Este tutorial estará aqui esperando quando você estiver pronto. :)

Criar o cliente MongoDB habilitado para CSFLE

Um MongoClienthabilitado para CSFLE não é muito diferente de um cliente padrão. Para criar um cliente com criptografia automática, nós o instanciamos com algumas opções adicionais de criptografia automática.
Como antes, vamos criar uma classe separada para manter essa funcionalidade. Crie um arquivo chamado AutoEncryptHelper.cs e adicione o seguinte código (observe que, como ele é um pouco mais longo do que os outros trechos de código, optei por adicionar comentários em linha para explicar o que está acontecendo em vez de esperar até depois do bloco de código):
1// AutoEncryptHelper.cs
2
3using System;
4using System.Collections.Generic;
5using System.IO;
6using MongoDB.Bson;
7using MongoDB.Driver;
8using MongoDB.Driver.Encryption;
9
10namespace EnvoyMedSys
11{
12 public class AutoEncryptHelper
13 {
14 private static readonly string __localMasterKeyPath = "../../../master-key.txt";
15
16 // Most of what follows are sample fields and a sample medical record we'll be using soon.
17 private static readonly string __sampleNameValue = "Takeshi Kovacs";
18 private static readonly int __sampleSsnValue = 213238414;
19
20 private static readonly BsonDocument __sampleDocFields =
21 new BsonDocument
22 {
23 { "name", __sampleNameValue },
24 { "ssn", __sampleSsnValue },
25 { "bloodType", "AB-" },
26 {
27 "medicalRecords",
28 new BsonArray(new []
29 {
30 new BsonDocument("weight", 180),
31 new BsonDocument("bloodPressure", "120/80")
32 })
33 },
34 {
35 "insurance",
36 new BsonDocument
37 {
38 { "policyNumber", 211241 },
39 { "provider", "EnvoyHealth" }
40 }
41 }
42 };
43
44 // Scaffolding of some private variables we'll need.
45 private readonly string _connectionString;
46 private readonly CollectionNamespace _keyVaultNamespace;
47 private readonly CollectionNamespace _medicalRecordsNamespace;
48
49 // Constructor that will allow us to specify our auto-encrypting
50 // client settings. This also makes it a bit easier to extend and
51 // use with a remote KMS provider later on.
52 public AutoEncryptHelper(string connectionString, CollectionNamespace keyVaultNamespace)
53 {
54 _connectionString = connectionString;
55 _keyVaultNamespace = keyVaultNamespace;
56 _medicalRecordsNamespace = CollectionNamespace.FromFullName("medicalRecords.patients");
57 }
58
59 // The star of the show. Accepts a key location,
60 // a key vault namespace, and a schema; all needed
61 // to construct our CSFLE-enabled MongoClient.
62 private IMongoClient CreateAutoEncryptingClient(
63 KmsKeyLocation kmsKeyLocation,
64 CollectionNamespace keyVaultNamespace,
65 BsonDocument schema)
66 {
67 var kmsProviders = new Dictionary<string, IReadOnlyDictionary<string, object>>();
68
69 // Specify the local master encryption key
70 if (kmsKeyLocation == KmsKeyLocation.Local)
71 {
72 var localMasterKeyBase64 = File.ReadAllText(__localMasterKeyPath);
73 var localMasterKeyBytes = Convert.FromBase64String(localMasterKeyBase64);
74 var localOptions = new Dictionary<string, object>
75 {
76 { "key", localMasterKeyBytes }
77 };
78 kmsProviders.Add("local", localOptions);
79 }
80
81 // Because we didn't explicitly specify the collection our
82 // JSON schema applies to, we assign it here. This will map it
83 // to a database called medicalRecords and a collection called
84 // patients.
85 var schemaMap = new Dictionary<string, BsonDocument>();
86 schemaMap.Add(_medicalRecordsNamespace.ToString(), schema);
87
88 // Specify location of mongocryptd binary, if necessary.
89 // Not required if path to the mongocryptd.exe executable
90 // has been added to your PATH variables
91 var extraOptions = new Dictionary<string, object>()
92 {
93 // Optionally uncomment the following line if you are running mongocryptd manually
94 // { "mongocryptdBypassSpawn", true }
95 };
96
97 // Create CSFLE-enabled MongoClient
98 // The addition of the automatic encryption settings are what
99 // transform this from a standard MongoClient to a CSFLE-enabled
100 // one
101 var clientSettings = MongoClientSettings.FromConnectionString(_connectionString);
102 var autoEncryptionOptions = new AutoEncryptionOptions(
103 keyVaultNamespace: keyVaultNamespace,
104 kmsProviders: kmsProviders,
105 schemaMap: schemaMap,
106 extraOptions: extraOptions);
107 clientSettings.AutoEncryptionOptions = autoEncryptionOptions;
108 return new MongoClient(clientSettings);
109 }
110 }
111}
Tudo bem, estamos quase terminando. Não se lembre de salvar o que você tem até agora! Em nossa próxima (e última) etapa, podemos finalmente experimentar a criptografia no nível do campo do lado do cliente com algumas queries!
🌟 Sabe de que programa esse paciente é? Deixe-me saber sua credibilidade como nerd (e vamos ser amigos, colegas fãs!) em um tweet!

Executar operações de leitura/gravação criptografadas

Lembra-se dos dados de amostra que preparamos? Vamos fazer bom uso deles! Para testar uma gravação e leitura criptografada desses dados, vamos adicionar outro método à classeAutoEncryptHelper. Logo após o construtor, adicione o seguinte método:
1// AutoEncryptHelper.cs
2
3public async void EncryptedWriteAndReadAsync(string keyIdBase64, KmsKeyLocation kmsKeyLocation)
4{
5 // Construct a JSON Schema
6 var schema = JsonSchemaCreator.CreateJsonSchema(keyIdBase64);
7
8 // Construct an auto-encrypting client
9 var autoEncryptingClient = CreateAutoEncryptingClient(
10 kmsKeyLocation,
11 _keyVaultNamespace,
12 schema);
13
14 var collection = autoEncryptingClient
15 .GetDatabase(_medicalRecordsNamespace.DatabaseNamespace.DatabaseName)
16 .GetCollection<BsonDocument>(_medicalRecordsNamespace.CollectionName);
17
18 var ssnQuery = Builders<BsonDocument>.Filter.Eq("ssn", __sampleSsnValue);
19
20 // Upsert (update document if found, otherwise create it) a document into the collection
21 var medicalRecordUpdateResult = await collection
22 .UpdateOneAsync(ssnQuery, new BsonDocument("$set", __sampleDocFields), new UpdateOptions() { IsUpsert = true });
23
24 if (!medicalRecordUpdateResult.UpsertedId.IsBsonNull)
25 {
26 Console.WriteLine("Successfully upserted the sample document!");
27 }
28
29 // Query by SSN field with auto-encrypting client
30 var result = collection.Find(ssnQuery).Single();
31
32 Console.WriteLine($"Encrypted client query by the SSN (deterministically-encrypted) field:\n {result}\n");
33}
O que está acontecendo aqui? Primeiro, usamos a JsonSchemaCreator classe para construir nosso esquema. Em seguida, criamos um cliente de criptografia automática usando o CreateAutoEncryptingClient() método . Em seguida, as linhas 14a16 definem o banco de dados e a collection em funcionamento com os quais interagiremos. Por fim,alteramos um prontuário médico usando nossos dados de amostra e, em seguida, os recuperamos com o cliente de criptografia automática.
Antes de inserir esse novo registro de paciente, o cliente habilitado para CSFLE criptografa automaticamente os campos apropriados, conforme estabelecido em nosso JSON schema.
Se você curte diagramas, veja o que está rolando:
Fluxo de uma gravação criptografada com dados criptografados no nível do campo
Fluxo de uma gravação criptografada com dados criptografados em nível de campo
Ao recuperar os dados do doente, eles são descriptografados pelo cliente. A melhor parte de ativar o CSFLE em seu aplicativo é que as queries não mudam, o que significa que os métodos do driver com os quais você já está familiarizado ainda podem ser usados.
Para o pessoal do diagrama:
Fluxo de uma leitura criptografada, descriptografando dados em nível de campo
Fluxo de uma leitura criptografada descriptografando dados em nível de campo
Para ver isso em ação, basta modificar ligeiramente o programa principal para podermos chamar o métodoEncryptedWriteAndReadAsync().
De volta ao arquivoProgram.cs , adicione o seguinte código:
1// Program.cs
2
3using System;
4using System.IO;
5using MongoDB.Driver;
6
7namespace EnvoyMedSys
8{
9 public enum KmsKeyLocation
10 {
11 Local,
12 }
13
14 class Program
15 {
16 public static void Main()
17 {
18 var connectionString = "PASTE YOUR MONGODB CONNECTION STRING/ATLAS URI HERE";
19 var keyVaultNamespace = CollectionNamespace.FromFullName("encryption.__keyVault");
20
21 var kmsKeyHelper = new KmsKeyHelper(
22 connectionString: connectionString,
23 keyVaultNamespace: keyVaultNamespace);
24 var autoEncryptHelper = new AutoEncryptHelper(
25 connectionString: connectionString,
26 keyVaultNamespace: keyVaultNamespace);
27
28 string kmsKeyIdBase64;
29
30 // Ensure GenerateLocalMasterKey() only runs once!
31 if (!File.Exists("../../../master-key.txt"))
32 {
33 kmsKeyHelper.GenerateLocalMasterKey();
34 }
35
36 kmsKeyIdBase64 = kmsKeyHelper.CreateKeyWithLocalKmsProvider();
37 autoEncryptHelper.EncryptedWriteAndReadAsync(kmsKeyIdBase64, KmsKeyLocation.Local);
38
39 Console.ReadKey();
40 }
41 }
42}
Tudo bem, é isso! Salve seus arquivos e execute seu programa. Após uma breve espera, você deverá ver a seguinte saída do console:
Saída do console de uma escrita e leitura criptografadas
Saída do console de uma escrita e leitura criptografadas
Funciona! A saída do console que você vê foi descriptografada corretamente pelo nosso MongoClient habilitado para CSFLE. Também podemos verificar se este registro do paciente foi salvo corretamente em nosso banco de dados. Fazendo login no meu cluster Atlas, vejo o registro do paciente de Takeshi armazenado com segurança, com os campos especificados criptografados:
Registro de paciente criptografado armazenado no MongoDB Atlas
Registro de paciente criptografado armazenado no MongoDB Atlas

Bônus: qual é a diferença com um cliente não criptografado?

Para ver o desempenho dessas queries ao usar um cliente não criptografado, vamos adicionar mais um método à classeAutoEncryptHelper. Logo após o métodoEncryptedWriteAndReadAsync(), adicione o seguinte:
1// AutoEncryptHelper.cs
2
3public void QueryWithNonEncryptedClient()
4{
5 var nonAutoEncryptingClient = new MongoClient(_connectionString);
6 var collection = nonAutoEncryptingClient
7 .GetDatabase(_medicalRecordsNamespace.DatabaseNamespace.DatabaseName)
8 .GetCollection<BsonDocument>(_medicalRecordsNamespace.CollectionName);
9 var ssnQuery = Builders<BsonDocument>.Filter.Eq("ssn", __sampleSsnValue);
10
11 var result = collection.Find(ssnQuery).FirstOrDefault();
12 if (result != null)
13 {
14 throw new Exception("Expected no document to be found but one was found.");
15 }
16
17 // Query by name field with a normal non-auto-encrypting client
18 var nameQuery = Builders<BsonDocument>.Filter.Eq("name", __sampleNameValue);
19 result = collection.Find(nameQuery).FirstOrDefault();
20 if (result == null)
21 {
22 throw new Exception("Expected the document to be found but none was found.");
23 }
24
25 Console.WriteLine($"Query by name (non-encrypted field) using non-auto-encrypting client returned:\n {result}\n");
26}
Aqui, instanciamos um MongoClient padrão sem configurações de criptografia automática. Observe que consultamos o camponamenão criptografado ; isso ocorre porque não podemos consultar campos criptografados usando um MongoClient sem o CSFLE ativado.
Por fim, adicione uma chamada para este novo método no arquivoProgram.cs:
1// Program.cs
2
3// Comparison query on non-encrypting client
4autoEncryptHelper.QueryWithNonEncryptedClient();
Salve todos os seus arquivos e execute o programa novamente. Você verá que sua última consulta retorna um registro criptografado do paciente, conforme o esperado. Como estamos usando um MongoClient não habilitado para CSFLE, nenhuma descriptografia acontece, deixando apenas os campos não criptografados legíveis para nós:
Consulta de saída usando um MongoClient não habilitado para CSFLE. Como não ocorre nenhuma descriptografia, os dados são retornados adequadamente em um estado criptografado.
Consulta de saída usando um MongoClient não habilitado para CSFLE. Como não ocorre nenhuma descriptografia, os dados são retornados adequadamente em um estado criptografado.

Vamos recapitular.

Parabéns! Você chegou até aqui!
Sério, dê um tapinha nas costas. Este foi um tutorial grave!
Sério, dê um tapinha nas costas. Este foi um tutorial grave!
Este tutorial mostrou a você:
  • Criando um aplicativo de console .NET Core.
  • Instalar dependências necessárias para habilitar a criptografia em nível de campo do lado do cliente para seu aplicativo .NET core.
  • Criando uma chave mestra local.
  • Criando uma chave de criptografia de dados.
  • Construção de um JSON schema para estabelecer quais campos criptografar.
  • Configurando um MongoClient habilitado para CSFLE.
  • Realizar leitura e escrita criptografada de uma amostra do registro de um cliente.
  • Realizando uma leitura usando um MongoClient não habilitado para CSFLE para ver a diferença nos dados recuperados.
Com esse conhecimento de criptografia em nível de campo do lado do cliente, você poderá proteger melhor os aplicativos e entender como ele funciona!
Esperemos que este tutorial tenha simplificado a integração da criptografia em nível de campo do lado do cliente em seu aplicativo .NET! Se você tiver mais dúvidas ou estiver preso em algo,acesse os Fóruns daMongoDB Community e inicie um tópico. Uma comunidade inteira de engenheiros do MongoDB (incluindo a equipe DevRel) e outros desenvolvedores com certeza ajudarão!
Caso você queira aprender um pouco mais, aqui estão os recursos que foram cruciais para me ajudar a escrever este tutorial:

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse exemplo de código
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Tutorial do provedor MongoDB para o EF Core: criação de um aplicativo com CRUD e controle de alterações


Jan 24, 2024 | 18 min read
Artigo

MongoDB Provider para EF Core: As atualizações mais recentes


Aug 29, 2024 | 6 min read
Tutorial

Manipulação de aggregation pipelines complexas com c# C#


Nov 15, 2024 | 5 min read
Tutorial

Projetando e desenvolvendo níveis de jogos 2D com Unity e C#


Feb 03, 2023 | 7 min read
Tecnologias Utilizadas
Linguagens
Sumário