Docs 菜单
Docs 主页
/
MongoDB Manual
/ / / / /

使用显式加密

在此页面上

  • Overview
  • 开始之前
  • 完整应用程序代码
  • 步骤
  • 创建客户主密钥
  • 在密钥保管库集合上创建唯一索引
  • 创建你的数据加密密钥和加密collection
  • 配置 MongoClient 以进行加密读取和写入
  • 插入具有加密字段的文档
  • 检索具有加密字段的文档
  • 了解详情

本指南向您展示如何使用显式加密和 MongoDB 驱动程序来加密文档。

完成本指南后,您应该能够配置驱动程序,使用显式加密对文档中的字段进行加密。 有了这些知识,您应该能够创建使用显式加密的客户端应用程序。具有自动解密功能。

重要

请勿在生产环境中使用此示例应用程序

本教程中的操作说明包括在不安全的环境中存储加密密钥,因此您不应在生产环境中使用此应用程序的未修改版本。在生产环境中使用此应用程序存在以下风险:未经授权访问加密密钥或丢失解密数据所需的密钥。本教程旨在演示如何使用 Queryable Encryption,而无需设置密钥管理系统。

您可以使用密钥管理系统将加密密钥安全地存储在生产环境中。 KMS是一种远程服务,可安全地存储和管理加密密钥。 要学习;了解如何设立使用KMS且启用了Queryable Encryption的应用程序,请参阅 Queryable Encryption教程。

要完成并运行本指南中的代码,您需要设置开发环境,如“安装 Queryable Encryption 兼容驱动程序”页面中所示。

要查看示例应用程序的完整代码,请在语言选择器中选择您的编程语言。

完整的 C# 应用程序

1

您必须创建客户主密钥 (CMK) 才能执行 Queryable Encryption。

创建 96 字节的客户主密钥并将其保存到文件 master-key.txt中:

using (var randomNumberGenerator = System.Security.Cryptography.RandomNumberGenerator.Create())
{
var bytes = new byte[96];
randomNumberGenerator.GetBytes(bytes);
var localMasterKeyBase64Write = Convert.ToBase64String(bytes);
File.WriteAllText("master-key.txt", localMasterKeyBase64Write);
}
func localMasterKey() []byte {
key := make([]byte, 96)
if _, err := rand.Read(key); err != nil {
log.Fatalf("Unable to create a random 96 byte data key: %v", err)
}
if err := ioutil.WriteFile("master-key.txt", key, 0644); err != nil {
log.Fatalf("Unable to write key to file: %v", err)
}
return key
}
byte[] localMasterKeyWrite = new byte[96];
new SecureRandom().nextBytes(localMasterKeyWrite);
try (FileOutputStream stream = new FileOutputStream("master-key.txt")) {
stream.write(localMasterKeyWrite);
}
const fs = require("fs");
const crypto = require("crypto");
try {
fs.writeFileSync("master-key.txt", crypto.randomBytes(96));
} catch (err) {
console.error(err);
}
path = "master-key.txt"
file_bytes = os.urandom(96)
with open(path, "wb") as f:
f.write(file_bytes)

警告

保护生产环境中的本地密钥文件

我们建议将客户数主密钥存储在远程 密钥管理系统 中( KMS )。要学习;了解如何在Queryable Encryption实施中使用远程KMS ,请参阅教程指南。

如果选择在生产中使用本地密钥提供商,请格外小心,不要将其存储在文件系统中。 请考虑使用 sidecar进程将密钥注入到客户端应用程序中,或使用其他确保密钥安全的方法。

提示

从命令行生成 CMK

使用以下命令从 Unix Shell 或 PowerShell 生成 CMK

  • Unix shell:

    echo $(head -c 96 /dev/urandom | base64 | tr -d '\n')
  • PowerShell:

    $r=[byte[]]::new(64);$g=[System.Security.Cryptography.RandomNumberGenerator]::Create();$g.GetBytes($r);[Convert]::ToBase64String($r)

将上述命令的输出保存到名为 customer-master-key.txt 的文件中。

提示

请参阅:完整代码

要查看创建客户主密钥的完整代码,请参阅 可查询Queryable Encryption示例应用程序存储库。

要查看创建客户主密钥的完整代码,请参阅 可查询Queryable Encryption示例应用程序存储库。

要查看创建客户主密钥的完整代码,请参阅 可查询Queryable Encryption示例应用程序存储库。

要查看创建客户主密钥的完整代码,请参阅 可查询Queryable Encryption示例应用程序存储库。

要查看创建客户主密钥的完整代码,请参阅 可查询Queryable Encryption示例应用程序存储库。

2

encryption.__keyVault 命名空间中的 keyAltNames 字段上创建唯一索引。

选择与所需的 MongoDB 驱动程序对应的标签页:

var connectionString = "<Your MongoDB URI>";
var keyVaultNamespace = CollectionNamespace.FromFullName("encryption.__keyVault");
var keyVaultClient = new MongoClient(connectionString);
var indexOptions = new CreateIndexOptions<BsonDocument>
{
Unique = true,
PartialFilterExpression = new BsonDocument
{{"keyAltNames", new BsonDocument {{"$exists", new BsonBoolean(true)}}}}
};
var builder = Builders<BsonDocument>.IndexKeys;
var indexKeysDocument = builder.Ascending("keyAltNames");
var indexModel = new CreateIndexModel<BsonDocument>(indexKeysDocument, indexOptions);
var keyVaultDatabase = keyVaultClient.GetDatabase(keyVaultNamespace.DatabaseNamespace.DatabaseName);
// Drop the Key Vault Collection in case you created this collection
// in a previous run of this application.
keyVaultDatabase.DropCollection(keyVaultNamespace.CollectionName);
var keyVaultCollection = keyVaultDatabase.GetCollection<BsonDocument>(keyVaultNamespace.CollectionName);
keyVaultCollection.Indexes.CreateOne(indexModel);

重要

使用 go buildgo run 构建或运行本指南中的Go代码时,请始终包含 cse构建约束以启用Queryable Encryption 。 有关包含构建约束的示例,请参阅以下shell命令:

go run -tags cse make-data-key.go
uri := "<Your MongoDB URI>"
keyVaultClient, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
if err != nil {
return fmt.Errorf("Connect error for regular client: %v", err)
}
defer func() {
_ = keyVaultClient.Disconnect(context.TODO())
}()
keyVaultDb := "encryption"
keyVaultColl := "__keyVault"
keyVaultNamespace := keyVaultDb + "." + keyVaultColl
keyVaultIndex := mongo.IndexModel{
Keys: bson.D{{"keyAltNames", 1}},
Options: options.Index().
SetUnique(true).
SetPartialFilterExpression(bson.D{
{"keyAltNames", bson.D{
{"$exists", true},
}},
}),
}
// Drop the Key Vault Collection in case you created this collection
// in a previous run of this application.
if err = keyVaultClient.Database(keyVaultDb).Collection(keyVaultColl).Drop(context.TODO()); err != nil {
log.Fatalf("Collection.Drop error: %v", err)
}
_, err = keyVaultClient.Database(keyVaultDb).Collection(keyVaultColl).Indexes().CreateOne(context.TODO(), keyVaultIndex)
if err != nil {
panic(err)
}
String keyVaultDb = "encryption";
String keyVaultColl = "__keyVault";
MongoClient keyVaultClient = MongoClients.create(connectionString);
String encryptedDbName = "medicalRecords";
String encryptedCollName = "patients";
// Drop the Key Vault Collection in case you created this collection
// in a previous run of this application.
keyVaultClient.getDatabase(keyVaultDb).getCollection(keyVaultColl).drop();
MongoCollection keyVaultCollection = keyVaultClient.getDatabase(keyVaultDb).getCollection(keyVaultColl);
IndexOptions indexOpts = new IndexOptions().partialFilterExpression(new BsonDocument("keyAltNames", new BsonDocument("$exists", new BsonBoolean(true) ))).unique(true);
keyVaultCollection.createIndex(new BsonDocument("keyAltNames", new BsonInt32(1)), indexOpts);
keyVaultClient.close();
const uri = "<Your Connection String>";
const keyVaultClient = new MongoClient(uri);
await keyVaultClient.connect();
const keyVaultDB = keyVaultClient.db(keyVaultDatabase);
// Drop the Key Vault Collection in case you created this collection
// in a previous run of this application.
await keyVaultDB.dropDatabase();
const keyVaultColl = keyVaultDB.collection(keyVaultCollection);
await keyVaultColl.createIndex(
{ keyAltNames: 1 },
{
unique: true,
partialFilterExpression: { keyAltNames: { $exists: true } },
}
);
connection_string = "<your connection string here>"
key_vault_coll = "__keyVault"
key_vault_db = "encryption"
key_vault_namespace = f"{key_vault_db}.{key_vault_coll}"
key_vault_client = MongoClient(connection_string)
# Drop the Key Vault Collection in case you created this collection
# in a previous run of this application.
key_vault_client.drop_database(key_vault_db)
key_vault_client[key_vault_db][key_vault_coll].create_index(
[("keyAltNames", ASCENDING)],
unique=True,
partialFilterExpression={"keyAltNames": {"$exists": True}},
)
3
  1. 读取客户主密钥并指定 KMS 提供商设置

    检索您在本指南的创建客户主密钥步骤中生成的客户主密钥文件的内容。

    使用KMS提供商设置中的集合扫描值。客户端使用这些设置来发现集合扫描 。将提供商名称设置为 local,以表明您正在使用本地密钥提供程序。

    选择与所需的 MongoDB 驱动程序对应的标签页:

    var kmsProviders = new Dictionary<string, IReadOnlyDictionary<string, object>>();
    const string provider = "local";
    var localMasterKeyBase64Read = File.ReadAllText("master-key.txt");
    var localMasterKeyBytes = Convert.FromBase64String(localMasterKeyBase64Read);
    var localOptions = new Dictionary<string, object>
    {
    {"key", localMasterKeyBytes}
    };
    kmsProviders.Add(provider, localOptions);
    key, err := ioutil.ReadFile("master-key.txt")
    if err != nil {
    log.Fatalf("Could not read the key from master-key.txt: %v", err)
    }
    provider := "local"
    kmsProviders := map[string]map[string]interface{}{"local": {"key": key}}
    String kmsProvider = "local";
    String path = "master-key.txt";
    byte[] localMasterKeyRead = new byte[96];
    try (FileInputStream fis = new FileInputStream(path)) {
    if (fis.read(localMasterKeyRead) < 96)
    throw new Exception("Expected to read 96 bytes from file");
    }
    Map<String, Object> keyMap = new HashMap<String, Object>();
    keyMap.put("key", localMasterKeyRead);
    Map<String, Map<String, Object>> kmsProviders = new HashMap<String, Map<String, Object>>();
    kmsProviders.put("local", keyMap);
    const provider = "local";
    const path = "./master-key.txt";
    // WARNING: Do not use a local key file in a production application
    const localMasterKey = fs.readFileSync(path);
    const kmsProviders = {
    local: {
    key: localMasterKey,
    },
    };
    provider = "local"
    path = "./master-key.txt"
    # WARNING: Do not use a local key file in a production application
    with open(path, "rb") as f:
    local_master_key = f.read()
    kms_providers = {
    "local": {
    "key": local_master_key # local_master_key variable from the previous step
    },
    }
  2. 创建数据加密密钥

    使用 MongoDB 连接字符串和密钥保管库集合命名空间构建客户端,并创建数据加密密钥:

    注意

    密钥保管库集合命名空间权限

    要完成本教程,您的应用程序用于连接到 MongoDB 的数据库用户必须具有对以下命名空间的dbAdmin权限:

    • encryption.__keyVault

    • medicalRecords database

    var clientEncryptionOptions = new ClientEncryptionOptions(
    keyVaultClient,
    keyVaultNamespace,
    kmsProviders: kmsProviders
    );
    var clientEncryption = new ClientEncryption(clientEncryptionOptions);
    var dataKeyOptions1 = new DataKeyOptions(alternateKeyNames: new List<string> { "dataKey1" });
    var dataKeyOptions2 = new DataKeyOptions(alternateKeyNames: new List<string> { "dataKey2" });
    BsonBinaryData CreateKeyGetID(DataKeyOptions options)
    {
    var dateKeyGuid = clientEncryption.CreateDataKey(provider, options, CancellationToken.None);
    return new BsonBinaryData(dateKeyGuid, GuidRepresentation.Standard);
    }
    var dataKeyId1 = CreateKeyGetID(dataKeyOptions1);
    var dataKeyId2 = CreateKeyGetID(dataKeyOptions2);
    var dataKeyId3 = CreateKeyGetID(dataKeyOptions3);
    var dataKeyId4 = CreateKeyGetID(dataKeyOptions4);
    clientEncryptionOpts := options.ClientEncryption().SetKeyVaultNamespace(keyVaultNamespace).
    SetKmsProviders(kmsProviders)
    clientEnc, err := mongo.NewClientEncryption(keyVaultClient, clientEncryptionOpts)
    if err != nil {
    return fmt.Errorf("NewClientEncryption error %v", err)
    }
    defer func() {
    _ = clientEnc.Close(context.TODO())
    }()
    dataKeyOpts1 := options.DataKey().
    SetKeyAltNames([]string{"demoDataKey1"})
    dataKeyID1, err := clientEnc.CreateDataKey(context.TODO(), provider, dataKeyOpts1)
    if err != nil {
    return fmt.Errorf("create data key error %v", err)
    }
    dataKeyOpts2 := options.DataKey().
    SetKeyAltNames([]string{"demoDataKey2"})
    dataKeyID2, err := clientEnc.CreateDataKey(context.TODO(), provider, dataKeyOpts2)
    if err != nil {
    return fmt.Errorf("create data key error %v", err)
    }
    String keyVaultNamespace = keyVaultDb + "." + keyVaultColl;
    ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
    .keyVaultMongoClientSettings(MongoClientSettings.builder()
    .applyConnectionString(new ConnectionString(connectionString))
    .build())
    .keyVaultNamespace(keyVaultNamespace)
    .kmsProviders(kmsProviders)
    .build();
    ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings);
    List<String> keyAlts1 = new ArrayList<String>();
    keyAlts1.add("dataKey1");
    BsonBinary dataKeyId1 = clientEncryption.createDataKey(kmsProvider, new DataKeyOptions()
    .keyAltNames(keyAlts1));
    List<String> keyAlts2 = new ArrayList<String>();
    keyAlts2.add("dataKey2");
    BsonBinary dataKeyId2 = clientEncryption.createDataKey(kmsProvider, new DataKeyOptions()
    .keyAltNames(keyAlts2));
    const clientEnc = new ClientEncryption(keyVaultClient, {
    keyVaultNamespace: keyVaultNamespace,
    kmsProviders: kmsProviders,
    });
    const dek1 = await clientEnc.createDataKey(provider, {
    keyAltNames: ["dataKey1"],
    });
    const dek2 = await clientEnc.createDataKey(provider, {
    keyAltNames: ["dataKey2"],
    });
    client = MongoClient(connection_string)
    client_encryption = ClientEncryption(
    kms_providers, # pass in the kms_providers variable from the previous step
    key_vault_namespace,
    client,
    CodecOptions(uuid_representation=STANDARD),
    )
    data_key_id_1 = client_encryption.create_data_key(provider, key_alt_names=["dataKey1"])
    data_key_id_2 = client_encryption.create_data_key(provider, key_alt_names=["dataKey2"])
  3. 创建加密集合

    使用已启用 Queryable EncryptionMongoClient 的实例指定必须加密的字段并创建加密的 collection:

    var encryptedCollectionNamespace = CollectionNamespace.FromFullName("medicalRecords.patients");
    var encryptedFieldsMap = new Dictionary<string, BsonDocument>
    {
    {
    encryptedCollectionNamespace.FullName, new BsonDocument
    {
    {
    "fields", new BsonArray
    {
    new BsonDocument
    {
    {"keyId", dataKeyId1},
    {"path", new BsonString("patientId")},
    {"bsonType", new BsonString("int")},
    {
    "queries", new BsonDocument
    {
    {"queryType", new BsonString("equality")}
    }
    }
    },
    new BsonDocument
    {
    {"keyId", dataKeyId2},
    {"path", new BsonString("medications")},
    {"bsonType", new BsonString("array")},
    },
    }
    }
    }
    }
    };
    var extraOptions = new Dictionary<string, object>()
    {
    { "cryptSharedLibPath", "<path to crypt_shared library>" },
    };
    var autoEncryptionOptions = new AutoEncryptionOptions(
    keyVaultNamespace,
    kmsProviders,
    encryptedFieldsMap: encryptedFieldsMap,
    extraOptions: extraOptions);
    var clientSettings = MongoClientSettings.FromConnectionString(connectionString);
    clientSettings.AutoEncryptionOptions = autoEncryptionOptions;
    var secureClient = new MongoClient(clientSettings);
    var encryptedDatabase = secureClient.GetDatabase(encryptedCollectionNamespace.DatabaseNamespace.DatabaseName);
    // Drop the encrypted collection in case you created this collection
    // in a previous run of this application.
    encryptedDatabase.DropCollection(encryptedCollectionNamespace.CollectionName);
    encryptedDatabase.CreateCollection(encryptedCollectionNamespace.CollectionName);
    Console.WriteLine("Created encrypted collection!");
    dbName := "medicalRecords"
    collName := "patients"
    encNamespace := (dbName + "." + collName)
    encryptedFieldsMap := bson.M{
    encNamespace: bson.M{
    "fields": []bson.M{
    {
    "path": "patientId",
    "bsonType": "int",
    "keyId": dataKeyID1,
    "queries": []bson.M{
    {
    "queryType": "equality",
    },
    },
    },
    {
    "path": "medications",
    "bsonType": "array",
    "keyId": dataKeyID2,
    },
    },
    },
    }
    extraOptions := map[string]interface{}{
    "cryptSharedLibPath": "<Your Crypt Shared lib Path>",
    }
    autoEncryptionOpts := options.AutoEncryption().
    SetKmsProviders(kmsProviders).
    SetKeyVaultNamespace(keyVaultNamespace).
    SetEncryptedFieldsMap(encryptedFieldsMap).
    SetExtraOptions(extraOptions)
    secureClient, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri).SetAutoEncryptionOptions(autoEncryptionOpts))
    if err != nil {
    return fmt.Errorf("Connect error for encrypted client: %v", err)
    }
    defer func() {
    _ = secureClient.Disconnect(context.TODO())
    }()
    // Drop the encrypted collection in case you created this collection
    // in a previous run of this application.
    if err = secureClient.Database(dbName).Collection(collName).Drop(context.TODO()); err != nil {
    log.Fatalf("Collection.Drop error: %v", err)
    }
    err = secureClient.Database(dbName).CreateCollection(context.TODO(), collName)
    if err != nil {
    return fmt.Errorf("Error creating collection: %v", err)
    }
    String encryptedNameSpace = encryptedDbName + "." + encryptedCollName;
    BsonDocument encFields = new BsonDocument().append("fields",
    new BsonArray(Arrays.asList(
    new BsonDocument().append("keyId", dataKeyId1)
    .append("path", new BsonString("patientId"))
    .append("bsonType", new BsonString("int"))
    .append("queries", new BsonDocument().append("queryType", new BsonString("equality"))),
    new BsonDocument().append("keyId", dataKeyId2)
    .append("path", new BsonString("medications"))
    .append("bsonType", new BsonString("array")),
    )));
    Map<String, BsonDocument> encryptedFieldsMap = new HashMap<String, BsonDocument>();
    encryptedFieldsMap.put(encryptedNameSpace, encFields);
    Map<String, Object> extraOptions = new HashMap<String, Object>();
    extraOptions.put("cryptSharedLibPath", "<path to crypt_shared>");
    MongoClientSettings clientSettings = MongoClientSettings.builder()
    .applyConnectionString(new ConnectionString(connectionString))
    .autoEncryptionSettings(AutoEncryptionSettings.builder()
    .keyVaultNamespace(keyVaultNamespace)
    .kmsProviders(kmsProviders)
    .encryptedFieldsMap(encryptedFieldsMap)
    .extraOptions(extraOptions)
    .build())
    .build();
    MongoClient mongoClientSecure = MongoClients.create(clientSettings);
    MongoDatabase encDb = mongoClientSecure.getDatabase(encryptedDbName);
    // Drop the encrypted collection in case you created this collection
    // in a previous run of this application.
    encDb.getCollection(encryptedCollName).drop();
    encDb.createCollection(encryptedCollName);
    const encryptedFieldsMap = {
    [`${secretDB}.${secretCollection}`]: {
    fields: [
    {
    keyId: dek1,
    path: "patientId",
    bsonType: "int",
    queries: { queryType: "equality" },
    },
    {
    keyId: dek2,
    path: "medications",
    bsonType: "array",
    },
    ],
    },
    };
    const extraOptions = {
    cryptSharedLibPath: "<path to FLE Shared Library>",
    };
    const encClient = new MongoClient(uri, {
    autoEncryption: {
    keyVaultNamespace,
    kmsProviders,
    extraOptions,
    encryptedFieldsMap,
    },
    });
    await encClient.connect();
    const newEncDB = encClient.db(secretDB);
    // Drop the encrypted collection in case you created this collection
    // in a previous run of this application.
    await newEncDB.dropDatabase();
    await newEncDB.createCollection(secretCollection);
    console.log("Created encrypted collection!");
    encrypted_db_name = "medicalRecords"
    encrypted_coll_name = "patients"
    encrypted_fields_map = {
    f"{encrypted_db_name}.{encrypted_coll_name}": {
    "fields": [
    {
    "keyId": data_key_id_1,
    "path": "patientId",
    "bsonType": "int",
    "queries": {"queryType": "equality"},
    },
    {
    "keyId": data_key_id_2,
    "path": "medications",
    "bsonType": "array",
    },
    ],
    },
    }
    key_vault_namespace = "encryption.__keyVault"
    auto_encryption = AutoEncryptionOpts(
    kms_providers,
    key_vault_namespace,
    encrypted_fields_map=encrypted_fields_map,
    crypt_shared_lib_path="<path to FLE Shared Library>",
    )
    secure_client = MongoClient(connection_string, auto_encryption_opts=auto_encryption)
    # Drop the encrypted collection in case you created this collection
    # in a previous run of this application.
    secure_client.drop_database(encrypted_db_name)
    encrypted_db = secure_client[encrypted_db_name]
    encrypted_db.create_collection(encrypted_coll_name)
    print("Created encrypted collection!")

本节中代码的输出应类似于以下内容:

Created encrypted collection!

提示

请参阅:完整代码

要查看制作数据加密密钥的完整代码,请参阅 Queryable Encryption 示例应用程序存储库。

要查看制作数据加密密钥的完整代码,请参阅 Queryable Encryption 示例应用程序存储库。

要查看制作数据加密密钥的完整代码,请参阅 Queryable Encryption 示例应用程序存储库。

要查看制作数据加密密钥的完整代码,请参阅 Queryable Encryption 示例应用程序存储库。

要查看制作数据加密密钥的完整代码,请参阅 Queryable Encryption 示例应用程序存储库。

4
  1. 指定密钥保管库集合命名空间

    指定 encryption.__keyVault 以作为密钥保管库集合命名空间。

    var connectionString = "<Your MongoDB URI>";
    var keyVaultNamespace = CollectionNamespace.FromFullName("encryption.__keyVault");
    var coll = "patients";
    var db = "medicalRecords";
    keyVaultColl := "__keyVault"
    keyVaultDb := "encryption"
    keyVaultNamespace := keyVaultDb + "." + keyVaultColl
    dbName := "medicalRecords"
    collName := "patients"
    String db = "medicalRecords";
    String coll = "patients";
    String keyVaultDb = "encryption";
    String keyVaultColl = "__keyVault";
    String keyVaultNamespace = String.format("%1$s.%2$s", keyVaultDb, keyVaultColl);
    String connectionString = "<Your MongoDB URI>";
    const eDB = "encryption";
    const eKV = "__keyVault";
    const keyVaultNamespace = `${eDB}.${eKV}`;
    const secretDB = "medicalRecords";
    const secretCollection = "patients";
    key_vault_namespace = "encryption.__keyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)
  2. 指定客户主密钥

    指定 KMS 提供商并以内联方式指定客户主密钥:

    var kmsProviders = new Dictionary<string, IReadOnlyDictionary<string, object>>();
    const string provider = "local";
    const string localMasterKeyPath = "master-key.txt";
    var localMasterKeyBase64Read = File.ReadAllText(localMasterKeyPath);
    var localMasterKeyBytes = Convert.FromBase64String(localMasterKeyBase64Read);
    var localOptions = new Dictionary<string, object>
    {
    {"key", localMasterKeyBytes}
    };
    kmsProviders.Add(provider, localOptions);
    key, err := ioutil.ReadFile("master-key.txt")
    if err != nil {
    log.Fatalf("Could not read the key from master-key.txt: %v", err)
    }
    kmsProviders := map[string]map[string]interface{}{"local": {"key": key}}
    String kmsProvider = "local";
    String path = "master-key.txt";
    byte[] localMasterKeyRead = new byte[96];
    try (FileInputStream fis = new FileInputStream(path)) {
    if (fis.read(localMasterKeyRead) < 96)
    throw new Exception("Expected to read 96 bytes from file");
    }
    Map<String, Object> keyMap = new HashMap<>();
    keyMap.put("key", localMasterKeyRead);
    Map<String, Map<String, Object>> kmsProviders = new HashMap<>();
    kmsProviders.put(kmsProvider, keyMap);
    const fs = require("fs");
    const path = "./master-key.txt";
    // WARNING: Do not use a local key file in a production application
    const localMasterKey = fs.readFileSync(path);
    const kmsProviders = {
    local: {
    key: localMasterKey,
    },
    };
    path = "./master-key.txt"
    with open(path, "rb") as f:
    local_master_key = f.read()
    kms_providers = {
    "local": {
    "key": local_master_key # local_master_key variable from the previous step
    },
    }
  3. 检索数据加密密钥

    检索在本指南的“创建数据加密密钥”步骤中创建的数据加密密钥

    var regularClient = new MongoClient(connectionString);
    var keyVaultCollection = regularClient.GetDatabase(keyVaultNamespace.DatabaseNamespace.DatabaseName)
    .GetCollection<BsonDocument>(keyVaultNamespace.CollectionName);
    Guid GetKeyId(string altName)
    {
    var filter = Builders<BsonDocument>.Filter.Eq<BsonString>("keyAltNames", altName);
    return keyVaultCollection.Find(filter).First<BsonDocument>()["_id"].AsGuid;
    }
    var dataKeyId1 = GetKeyId("dataKey1");
    var dataKeyId2 = GetKeyId("dataKey2");
    uri := "<Your MongoDB URI>"
    regularClient, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
    if err != nil {
    panic(fmt.Errorf("Client connect error %v", err))
    }
    var foundDoc1 bson.M
    err = regularClient.Database(keyVaultDb).Collection(keyVaultColl).FindOne(context.TODO(), bson.D{{"keyAltNames", "demoDataKey1"}}).Decode(&foundDoc1)
    if err != nil {
    panic(err)
    }
    var dataKeyID1 = foundDoc1["_id"].(primitive.Binary)
    var foundDoc2 bson.M
    err = regularClient.Database(keyVaultDb).Collection(keyVaultColl).FindOne(context.TODO(), bson.D{{"keyAltNames", "demoDataKey2"}}).Decode(&foundDoc2)
    if err != nil {
    panic(err)
    }
    var dataKeyID2 = foundDoc2["_id"].(primitive.Binary)
    MongoClient client = MongoClients.create(connectionString);
    MongoCollection<Document> keyVaultClient = client.getDatabase(keyVaultDb).getCollection(keyVaultColl);
    BsonBinary dataKeyId1 = new BsonBinary(BsonBinarySubType.UUID_STANDARD, keyVaultClient.find(eq("keyAltNames", "dataKey1")).first().get("_id", Binary.class).getData());
    BsonBinary dataKeyId2 = new BsonBinary(BsonBinarySubType.UUID_STANDARD, keyVaultClient.find(eq("keyAltNames", "dataKey2")).first().get("_id", Binary.class).getData());
    const uri = "<Your MongoDB URI>";
    const unencryptedClient = new MongoClient(uri);
    await unencryptedClient.connect();
    const keyVaultClient = unencryptedClient.db(eDB).collection(eKV);
    const dek1 = await keyVaultClient.findOne({ keyAltNames: "dataKey1" });
    const dek2 = await keyVaultClient.findOne({ keyAltNames: "dataKey2" });
    connection_string = "<your connection string here>"
    client = MongoClient(connection_string)
    key_vault = client[key_vault_db_name][key_vault_coll_name]
    data_key_id_1 = key_vault.find_one({"keyAltNames": "dataKey1"})["_id"]
    data_key_id_2 = key_vault.find_one({"keyAltNames": "dataKey2"})["_id"]
  4. 指定自动加密共享库的路径

    var extraOptions = new Dictionary<string, object>()
    {
    {"cryptSharedLibPath", "<path to crypt_shared library>"},
    };
    extraOptions := map[string]interface{}{
    "cryptSharedLibPath": "<path to crypt_shared library>",
    }
    Map<String, Object> extraOptions = new HashMap<>();
    extraOptions.put("cryptSharedLibPath", "<path to crypt_shared library>");
    const extraOptions = {
    cryptSharedLibPath: "<path to crypt_shared library>",
    };
    opts = AutoEncryptionOpts(
    kms_providers,
    key_vault.full_name,
    bypass_query_analysis=True,
    key_vault_client=client,
    crypt_shared_lib_path="<path to FLE Shared Library>",
    )

    提示

    了解详情

    要学习;了解有关此路径引用的库的更多信息,请参阅自动加密共享库页面。

  5. 创建 MongoClient 对象

    使用以下自动加密设置实例化MongoClient对象:

    var clientSettings = MongoClientSettings.FromConnectionString(connectionString);
    var autoEncryptionOptions = new AutoEncryptionOptions(
    keyVaultNamespace,
    kmsProviders,
    bypassQueryAnalysis: true,
    extraOptions: extraOptions);
    clientSettings.AutoEncryptionOptions = autoEncryptionOptions;
    var secureClient = new MongoClient(clientSettings);
    var collection = secureClient.GetDatabase(db).GetCollection<BsonDocument>(coll);
    autoEncryptionOpts := options.AutoEncryption().
    SetKmsProviders(kmsProviders).
    SetKeyVaultNamespace(keyVaultNamespace).
    SetExtraOptions(extraOptions).
    SetBypassQueryAnalysis(true)
    secureClient, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri).SetAutoEncryptionOptions(autoEncryptionOpts))
    if err != nil {
    return fmt.Errorf("Connect error for encrypted client: %v", err)
    }
    defer func() {
    _ = secureClient.Disconnect(context.TODO())
    }()
    var coll = secureClient.Database(dbName).Collection(collName)
    MongoClientSettings clientSettings = MongoClientSettings.builder()
    .applyConnectionString(new ConnectionString(connectionString))
    .autoEncryptionSettings(AutoEncryptionSettings.builder()
    .keyVaultNamespace(keyVaultNamespace)
    .kmsProviders(kmsProviders)
    .extraOptions(extraOptions)
    .bypassQueryAnalysis(true)
    .build())
    .build();
    MongoClient mongoClientSecure = MongoClients.create(clientSettings);
    const encryptedClient = new MongoClient(uri, {
    autoEncryption: {
    kmsProviders: kmsProviders,
    keyVaultNamespace: keyVaultNamespace,
    bypassQueryAnalysis: true,
    keyVaultClient: unencryptedClient,
    extraOptions: extraOptions,
    },
    });
    await encryptedClient.connect();
    encrypted_client = MongoClient(connection_string, auto_encryption_opts=opts)
    db = encrypted_client.medicalRecords
    coll = db.patients

    注意

    自动解密

    MongoClient我们使用启用了自动加密的实例来执行自动解密。

    要了解有关使用自动解密的显式加密的更多信息,请参阅“基础知识”部分。

  6. 创建 ClientEncryption 对象

    按如下方式实例化一个ClientEncryption对象:

    var clientEncryptionOptions = new ClientEncryptionOptions(
    keyVaultClient: regularClient,
    keyVaultNamespace: keyVaultNamespace,
    kmsProviders: kmsProviders
    );
    var clientEncryption = new ClientEncryption(clientEncryptionOptions);
    clientEncryptionOpts := options.ClientEncryption().SetKeyVaultNamespace(keyVaultNamespace).SetKmsProviders(kmsProviders)
    clientEnc, err := mongo.NewClientEncryption(regularClient, clientEncryptionOpts)
    if err != nil {
    panic(fmt.Errorf("NewClientEncryption error %v", err))
    }
    defer func() {
    _ = clientEnc.Close(context.TODO())
    }()
    ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
    .keyVaultMongoClientSettings(MongoClientSettings.builder()
    .applyConnectionString(new ConnectionString(connectionString))
    .build())
    .keyVaultNamespace(keyVaultNamespace)
    .kmsProviders(kmsProviders)
    .build();
    ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings);
    const encryption = new ClientEncryption(unencryptedClient, {
    keyVaultNamespace,
    kmsProviders,
    });
    client_encryption = ClientEncryption(
    kms_providers, key_vault_namespace, client, client.codec_options
    )
5

使用已启用 Queryable Encryption 的MongoClient实例,通过以下代码段将具有加密字段的文档插入到medicalRecords.patients命名空间中:

var patientId = 12345678;
var medications = new BsonArray
{
new BsonString("Atorvastatin"),
new BsonString("Levothyroxine")
};
var indexedEncrypted = clientEncryption.Encrypt(
patientId,
new EncryptOptions(algorithm: "Indexed", keyId: dataKeyId1, contentionFactor: 1),
CancellationToken.None);
var unindexedEncrypted = clientEncryption.Encrypt(
medications,
new EncryptOptions(algorithm: "Unindexed", keyId: dataKeyId2),
CancellationToken.None);
collection.InsertOne(new BsonDocument { { "firstName", "Jon" }, { "patientId", indexedEncrypted }, { "medications", unindexedEncrypted } });
patientIdRawValueType, patientIdRawValueData, err := bson.MarshalValue(12345678)
if err != nil {
panic(err)
}
patientIdRawValue := bson.RawValue{Type: patientIdRawValueType, Value: patientIdRawValueData}
patientIdEncryptionOpts := options.Encrypt().
SetAlgorithm("Indexed").
SetKeyID(dataKeyID1).
SetContentionFactor(1)
patientIdEncryptedField, err := clientEnc.Encrypt(
context.TODO(),
patientIdRawValue,
patientIdEncryptionOpts)
if err != nil {
panic(err)
}
medicationsRawValueType, medicationsRawValueData, err := bson.MarshalValue([]string{"Atorvastatin", "Levothyroxine"})
if err != nil {
panic(err)
}
medicationsRawValue := bson.RawValue{Type: medicationsRawValueType, Value: medicationsRawValueData}
medicationsEncryptionOpts := options.Encrypt().
SetAlgorithm("Unindexed").
SetKeyID(dataKeyID2)
medicationsEncryptedField, err := clientEnc.Encrypt(
context.TODO(),
medicationsRawValue,
medicationsEncryptionOpts)
if err != nil {
panic(err)
}
_, err = coll.InsertOne(
context.TODO(),
bson.D{{"firstName", "Jon"}, {"patientId", patientIdEncryptedField}, {"medications", medicationsEncryptedField}})
if err != nil {
panic(err)
}
BsonInt32 patientId = new BsonInt32(12345678);
ArrayList<BsonString> medications = new ArrayList<>();
medications.add(new BsonString("Atorvastatin"));
medications.add(new BsonString("Levothyroxine"));
BsonBinary indexedEncrypted = clientEncryption.encrypt(patientId, new EncryptOptions("Indexed").keyId(dataKeyId1).contentionFactor(1L));
BsonBinary unindexedEncrypted = clientEncryption.encrypt(new BsonArray(medications), new EncryptOptions("Unindexed").keyId(dataKeyId2));
MongoCollection<BsonDocument> collection = mongoClientSecure.getDatabase(db).getCollection(coll, BsonDocument.class);
collection.insertOne(new BsonDocument("firstName", new BsonString("Jon")).append("patientId", indexedEncrypted).append("medications", unindexedEncrypted));
const patientId = 12345678;
const medications = ["Atorvastatin", "Levothyroxine"];
const indexedInsertPayload = await encryption.encrypt(patientId, {
algorithm: "Indexed",
keyId: dek1._id,
contentionFactor: 1,
});
const unindexedInsertPayload = await encryption.encrypt(medications, {
algorithm: "Unindexed",
keyId: dek2._id,
});
const encryptedColl = encryptedClient
.db(secretDB)
.collection(secretCollection);
await encryptedColl.insertOne({
firstName: "Jon",
patientId: indexedInsertPayload,
medications: unindexedInsertPayload,
});
patientId = 12345678
medications = ["Atorvastatin", "Levothyroxine"]
indexed_insert_payload = client_encryption.encrypt(
patientId, Algorithm.INDEXED, data_key_id_1, contention_factor=1
)
unindexed_insert_payload = client_encryption.encrypt(
medications, Algorithm.UNINDEXED, data_key_id_2
)
coll.insert_one(
{
"firstName": "Jon",
"patientId": indexed_insert_payload,
"medications": unindexed_insert_payload,
}
)

插入文档时,启用了 Queryable Encryption 的客户端会对文档的字段进行加密,如下所示:

{
"_id": {
"$oid": "6303e36053cc7ec2e6a630bd"
},
"firstName": "Jon",
"patientId": {
"$binary": {
"base64": "BxLJUBmg703civqMz8ASsD4QEYeSneOGiiYHfLE77ELEkp1EC/fXPrKCNRQl2mAFddszqDJ0P3znKrq0DVMEvJoU6wa0Ra+U+JjNVr8NtJE+TpTLCannY5Av6iGfLAaiHbM/E8Ftz1YCQsArQwuNp3wIV/GJPLa2662xsyk0wz7F6IRGC3FlnxpN4UIFaHE1M7Y6kEnx3tEy5uJBvU4Sex7I2H0kqHthClH77Q6xHIHc8H9d6upvgnEbkKBCnmc24A2pSG/xZ7LBsV3j5aOboPISuN/lvg==",
"subType": "06"
}
},
"medications": {
"$binary": {
"base64": "BvOsveapfUxiuQxCMSM2fYIEyRlQaSqR+0NxlMarwurBflvoMz1FrSjSGgCVCpK8X+YrilP6Bac99kkaUmRJfjo4savxcjpOfEnUj5bHciPyfQBYmYF4PMLDtTTzGZpPilb9d5KgpIMBXxHi+dIcog==",
"subType": "06"
}
},
"__safeContent__": [
{
"$binary": {
"base64": "ZLPIpgxzXpHUGrvdIHetwmMagR+mqvuUj5nzXNGf/WM=",
"subType": "00"
}
}
]
}

警告

不要修改 __safeContent__ 字段

__safeContent__ 字段对于 Queryable Encryption 至关重要。请勿修改此字段的内容。

提示

请参阅:完整代码

要查看插入通过显式加密加密的文档的完整代码,请参阅 可查询Queryable Encryption 示例应用程序存储库。

要查看插入通过显式加密加密的文档的完整代码,请参阅 可查询Queryable Encryption 示例应用程序存储库。

要查看插入通过显式加密加密的文档的完整代码,请参阅 可查询Queryable Encryption 示例应用程序存储库。

要查看插入通过显式加密加密的文档的完整代码,请参阅 可查询Queryable Encryption 示例应用程序存储库。

要查看插入通过显式加密加密的文档的完整代码,请参阅 可查询Queryable Encryption 示例应用程序存储库。

6

通过对加密字段进行查询,检索在本指南的“插入具有加密字段的文档”步骤中插入的具有加密字段的文档

var findPayload = clientEncryption.Encrypt(
patientId,
new EncryptOptions(algorithm: "Indexed", keyId: dataKeyId1, queryType: "equality", contentionFactor: 1),
CancellationToken.None);
var doc = collection.Find(new BsonDocument { { "patientId", findPayload } }).Single();
Console.WriteLine($"Encrypted document: {doc}");
findPayloadRawValueType, findPayloadRawValueData, err := bson.MarshalValue(12345678)
if err != nil {
panic(err)
}
findPayloadRawValue := bson.RawValue{Type: findPayloadRawValueType, Value: findPayloadRawValueData}
findPayloadEncryptionOpts := options.Encrypt().
SetAlgorithm("Indexed").
SetKeyID(dataKeyID1).
SetQueryType("equality").
SetContentionFactor(1)
findPayloadEncryptedField, err := clientEnc.Encrypt(
context.TODO(),
findPayloadRawValue,
findPayloadEncryptionOpts)
if err != nil {
panic(err)
}
var resultSecure bson.M
coll.FindOne(context.TODO(), bson.D{{"firstName", findPayloadEncryptedField}}).Decode(&resultSecure)
if err != nil {
panic(err)
}
outputSecure, err := json.MarshalIndent(resultSecure, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("\nFound document searching on explicitly encrypted field:\n%s\n", outputSecure)
BsonBinary findPayloadEncrypted = clientEncryption.encrypt(patientId, new EncryptOptions("Indexed").keyId(dataKeyId1).queryType("equality").contentionFactor(1L));
BsonDocument result = collection.find(eq("patientId", findPayloadEncrypted)).first();
System.out.println("Finding a document with manually encrypted field: " + result.toJson());
const findPayload = await encryption.encrypt(patientId, {
algorithm: "Indexed",
keyId: dek1._id,
queryType: "equality",
contentionFactor: 1,
});
console.log("Finding a document with manually encrypted field:");
console.log(await encryptedColl.findOne({ patientId: findPayload }));
find_payload = client_encryption.encrypt(
patientId,
Algorithm.INDEXED,
data_key_id_1,
query_type=QueryType.EQUALITY,
contention_factor=1,
)
doc = coll.find_one({"encryptedIndexed": find_payload})
print("\nReturned document:\n")
pprint.pprint(doc)

上述代码片段的输出应包含以下文档:

{
"__safeContent__": [
{
"Subtype": 0,
"Data": "LfaIuWm9o30MIGrK7GGUoStJMSNOjRgbxy5q2TPiDes="
}
],
"_id": "6303a770857952ca5e363fd2",
"firstName": "Jon",
"medications": ["Atorvastatin", "Levothyroxine"],
"patientId": 12345678
}

提示

请参阅:完整代码

要查看使用加密字段检索文档的代码,请参阅 Queryable Encryption示例应用程序存储库。

要查看使用加密字段检索文档的代码,请参阅 Queryable Encryption示例应用程序存储库。

要查看使用加密字段检索文档的代码,请参阅 Queryable Encryption示例应用程序存储库。

要查看使用加密字段检索文档的代码,请参阅 Queryable Encryption示例应用程序存储库。

要查看使用加密字段检索文档的代码,请参阅 Queryable Encryption示例应用程序存储库。

要查看有关将 Queryable Encryption 与远程 KMS 结合使用的教程,请参阅教程。

要了解Queryable Encryption的工作原理,请参阅显式加密。

如需详细了解本指南中提到的主题,请参阅以下链接:

后退

查询