Overview
了解如何使用客户端字段级加密 (CSFLE) 的显式加密机制。
显式加密是一种机制,您可以在其中为对数据库执行的每个操作指定如何加密和解密文档中的字段。
以下 4.2 或更高版本的 MongoDB 产品提供显式加密:
MongoDB 社区服务器
MongoDB Enterprise Advanced
MongoDB Atlas
使用显式加密
要使用显式加密,您必须在启用 CSFLE 的应用程序中执行以下操作:
创建 ClientEncryption 实例
要使用显式加密,必须创建一个 ClientEncryption 实例。ClientEncryption 是跨驱动程序和 mongosh 使用的抽象,它封装了密钥保管库集合和显式加密涉及到的 KMS 操作。
要创建 ClientEncryption 实例,您必须指定以下信息:
可以访问您的密钥保管库集合的
MongoClient实例密钥保管库集合的命名空间
经过配置,具备您客户主密钥 KMS 托管提供商访问权限的
kmsProviders对象
有关更多 ClientEncryption 选项,请参阅特定于 CSFLE 的 MongoClient 选项。
要查看显示如何创建 ClientEncryption 实例的代码片段,请参阅本指南的示例部分。
加密读写操作中的字段
您必须在整个应用程序中更新读写操作,使应用程序在执行读写操作前对字段进行加密。
要加密字段,请使用 ClientEncryption 实例的 encrypt 方法。
要查看显示如何使用 encrypt 方法的代码片段,请参阅本指南的示例部分。
手动解密
使用显式加密时,您可以手动或自动解密加密字段。
要手动解密字段,请使用 ClientEncryption 实例的 decrypt 方法。
要查看显示如何使用 decrypt 方法的代码片段,请参阅本指南的示例部分。
自动解密
要自动解密字段,请按如下所示配置 MongoClient 实例:
指定密钥保管库集合
指定
kmsProviders对象如果您使用 MongoDB Community Server,请将
bypassAutoEncryption选项设置为True
注意
MongoDB Community Server 支持自动解密
虽然自动加密需要 MongoDB Enterprise 或 MongoDB Atlas,但以下 MongoDB 产品也提供自动解密功能:
MongoDB 社区服务器
MongoDB Enterprise Advanced
MongoDB Atlas
要查看演示如何启用自动解密的代码片段,请选择与首选语言对应的标签页:
例子
假设您想在 MongoDB 实例中插入结构如下的文档:
{ "name": "<name of person>", "age": <age of person>, "favorite-foods": ["<array of foods>"]}创建 MongoClient 实例
在此示例中,您使用相同的 MongoClient 实例来访问密钥保管库集合,并读写加密数据。
以下代码片段展示了如何创建 MongoClient 实例:
const autoEncryptionOpts = { keyVaultNamespace: keyVaultNamespace, kmsProviders: kmsProviders,};const encryptedClient = Mongo(connectionString, autoEncryptionOpts);var client = new MongoClient(connectionString);client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(URI))if err != nil { panic(fmt.Errorf("Client connect error %v", err))}MongoClient client = MongoClients.create(connectionString);const client = new MongoClient(connectionString); client = MongoClient(your_connection_uri)创建 ClientEncryption 实例
以下代码片段展示了如何创建 ClientEncryption 实例:
const clientEncryption = encryptedClient.getClientEncryption();var collection = client.GetDatabase(db).GetCollection<BsonDocument>(coll);var clientEncryptionOptions = new ClientEncryptionOptions( keyVaultClient: client, keyVaultNamespace: keyVaultNamespace, kmsProviders: kmsProviders);var clientEncryption = new ClientEncryption(clientEncryptionOptions);coll := client.Database(DbName).Collection(CollName)clientEncryptionOpts := options.ClientEncryption().SetKeyVaultNamespace(KeyVaultNamespace).SetKmsProviders(kmsProviders)clientEnc, err := mongo.NewClientEncryption(client, clientEncryptionOpts)if err != nil { panic(fmt.Errorf("NewClientEncryption error %v", err))}defer func() { _ = clientEnc.Close(context.TODO())}()MongoCollection<Document> collection = client.getDatabase(db).getCollection(coll);ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder() .keyVaultMongoClientSettings(MongoClientSettings.builder() .applyConnectionString(new ConnectionString(connectionString)) .build()) .keyVaultNamespace(keyVaultNamespace) .kmsProviders(kmsProviders) .build();ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings);const collection = client.db(db).collection(coll);const encryption = new ClientEncryption(client, { keyVaultNamespace, kmsProviders,});coll = client.employees.foodsclient_encryption = ClientEncryption( kms_providers, "encryption.___keyVault", client, coll.codec_options,)注意
CodecOptions
MongoDB Python 驱动程序要求您指定要用于加密和解密文档的 CodecOptions。
指定您在 MongoClient、Database 或 Collection 上配置的 CodecOptions,用于将加密和解密的应用程序数据写入 MongoDB。
加密字段并插入
您想使用以下算法对文档的字段进行加密:
字段名称 | 加密算法 | BSON 字段类型 |
|---|---|---|
| 确定性 | 字符串 |
| 无加密 | Int |
| 随机 | 阵列 |
以下代码片段显示如何手动加密文档中的字段并将文档插入 MongoDB:
注意
以下示例中的 dataKeyId 变量指的是数据加密密钥(DEK)。要学习;了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要学习;了解如何使用特定密钥管理系统创建 DEK,请参阅 CSFLE 教程。
const encName = clientEncryption.encrypt( dataKeyId, "Greg", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic");const encFoods = clientEncryption.encrypt( dataKeyId, ["Cheese", "Grapes"], "AEAD_AES_256_CBC_HMAC_SHA_512-Random");db.getSiblingDB(database).getCollection(collection).insertOne({ name: encName, foods: encFoods,});注意
以下示例中的 dataKeyId 变量指的是数据加密密钥(DEK)。要学习;了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要学习;了解如何使用特定密钥管理系统创建 DEK,请参阅 CSFLE 教程。
var encryptedName = clientEncryption.Encrypt( "Greg", new EncryptOptions(algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", keyId: dataKeyId), CancellationToken.None);var encryptedFoods = clientEncryption.Encrypt( new BsonArray { "Cheese", "Grapes" }, new EncryptOptions(algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Random", keyId: dataKeyId), CancellationToken.None);collection.InsertOne(new BsonDocument { { "name", encryptedName }, { "age", 83 }, { "foods", encryptedFoods } });注意
以下示例中的 dataKeyId 变量指的是数据加密密钥(DEK)。要学习;了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要学习;了解如何使用特定密钥管理系统创建 DEK,请参阅 CSFLE 教程。
nameRawValueType, nameRawValueData, err := bson.MarshalValue("Greg")if err != nil { panic(err)}nameRawValue := bson.RawValue{Type: nameRawValueType, Value: nameRawValueData}nameEncryptionOpts := options.Encrypt(). SetAlgorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"). SetKeyID(dataKeyId)nameEncryptedField, err := clientEnc.Encrypt( context.TODO(), nameRawValue, nameEncryptionOpts)if err != nil { panic(err)}foodsRawValueType, foodsRawValueData, err := bson.MarshalValue(bson.A{"Grapes", "Cheese"})if err != nil { panic(err)}foodsRawValue := bson.RawValue{Type: foodsRawValueType, Value: foodsRawValueData}encryptionOpts := options.Encrypt(). SetAlgorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Random"). SetKeyID(dataKeyId)foodsEncryptedField, err := clientEnc.Encrypt( context.TODO(), foodsRawValue, encryptionOpts)if err != nil { panic(err)}
_, err = coll.InsertOne( context.TODO(), bson.D{{"name", nameEncryptedField}, {"foods", foodsEncryptedField}, {"age", 83}})if err != nil { panic(err)}注意
以下示例中的 dataKeyId 变量指的是数据加密密钥(DEK)。要学习;了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要学习;了解如何使用特定密钥管理系统创建 DEK,请参阅 CSFLE 教程。
BsonBinary encryptedName = clientEncryption.encrypt(new BsonString("Greg"), new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId));BsonBinary encryptedFoods = clientEncryption.encrypt(new BsonArray().parse("[\"Grapes\", \"Foods\"]"), new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Random").keyId(dataKeyId));collection.insertOne(new Document("name", encryptedName).append("foods", encryptedFoods).append("age", 83));注意
以下示例中的 dataKeyId 变量指的是数据加密密钥(DEK)。要学习;了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要学习;了解如何使用特定密钥管理系统创建 DEK,请参阅 CSFLE 教程。
encryptedName = await encryption.encrypt("Greg", { algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", keyId: dataKeyId,});encryptedFoods = await encryption.encrypt(["Cheese", "Grapes"], { algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Random", keyId: dataKeyId,});await collection.insertOne({ name: encryptedName, age: 83, foods: encryptedFoods,});注意
以下示例中的 data_key_id 变量指的是数据加密密钥(DEK)。要学习;了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要学习;了解如何使用特定密钥管理系统创建 DEK,请参阅 CSFLE 教程。
encrypted_name = client_encryption.encrypt( "Greg", Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=data_key_id,)encrypted_foods = client_encryption.encrypt( ["Cheese", "Grapes"], Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random, key_id=data_key_id,)coll.insert_one({"name": encrypted_name, "age": 83, "foods": encrypted_foods})检索文档和解密字段
以下代码片段说明了如何检索插入的文档,并手动解密加密的字段:
const encNameQuery = clientEncryption.encrypt( dataKeyId, "Greg", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic");let doc = db.getSiblingDB(database).getCollection(collection).findOne({ name: encNameQuery,});console.log(doc);doc.name = clientEncryption.decrypt(doc.name);doc.foods = clientEncryption.decrypt(doc.foods);console.log(doc);var nameToQuery = "Greg";var encryptedNameToQuery = clientEncryption.Encrypt( nameToQuery, new EncryptOptions(algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", keyId: dataKeyId), CancellationToken.None);var doc = collection.Find(new BsonDocument { { "name", encryptedNameToQuery } }).Single();Console.WriteLine($"Encrypted document: {doc}");doc["name"] = clientEncryption.Decrypt(doc["name"].AsBsonBinaryData, CancellationToken.None);doc["foods"] = clientEncryption.Decrypt(doc["foods"].AsBsonBinaryData, CancellationToken.None);Console.WriteLine($"Decrypted field: {doc}");nameQueryRawValueType, nameQueryRawValueData, err := bson.MarshalValue("Greg")if err != nil { panic(err)}nameQueryRawValue := bson.RawValue{Type: nameQueryRawValueType, Value: nameQueryRawValueData}nameQueryEncryptionOpts := options.Encrypt(). SetAlgorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"). SetKeyID(dataKeyId)nameQueryEncryptedField, err := clientEnc.Encrypt( context.TODO(), nameQueryRawValue, nameQueryEncryptionOpts)if err != nil { panic(err)}var result bson.Merr = coll.FindOne( context.TODO(), bson.D{{"name", nameQueryEncryptedField}}).Decode(&result)if err != nil { if err == mongo.ErrNoDocuments { return } panic(err)}fmt.Printf("Encrypted Document: %s\n", result)nameDecrypted, err := clientEnc.Decrypt( context.TODO(), result["name"].(primitive.Binary))foodsDecrypted, err := clientEnc.Decrypt( context.TODO(), result["foods"].(primitive.Binary))result["foods"] = foodsDecryptedresult["name"] = nameDecryptedfmt.Printf("Decrypted Document: %s\n", result)BsonBinary encryptedNameQuery = clientEncryption.encrypt(new BsonString("Greg"), new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId));Document result = collection.find(eq("name", encryptedNameQuery)).first();System.out.println("Encrypted Document: " + result.toJson());result.replace("name", clientEncryption.decrypt(new BsonBinary(result.get("name", Binary.class).getData())));result.replace("foods", clientEncryption.decrypt(new BsonBinary(result.get("foods", Binary.class).getData())));System.out.println("Decrypted Document: " + result.toJson());queryEncryptedName = await encryption.encrypt("Greg", { algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", keyId: dataKeyId,});let doc = await collection.findOne({ name: queryEncryptedName });console.log("Encrypted Document: ", doc);doc.name = encryption.decrypt(doc.name);doc.foods = encryption.decrypt(doc.foods);console.log("Decrypted document: ", doc);name_to_query = "Greg"encrypted_name_to_query = client_encryption.encrypt( name_to_query, Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=data_key_id,)doc = client.employees.foods.find_one({"name": encrypted_name_to_query})print("Encrypted document: %s" % (doc,))doc["name"] = client_encryption.decrypt(doc["name"])doc["foods"] = client_encryption.decrypt(doc["foods"])print("Decrypted document: %s" % (doc,))服务器端字段级加密实施
MongoDB 支持使用模式验证来强制加密集合中的特定字段。
在配置为强制执行给某些字段加密的 MongoDB 实例上使用显式加密机制执行客户端字段级加密的客户端必须按照 MongoDB 实例上指定的方式对这些字段进行加密。
要了解如何设置服务器端 CSFLE 强制执行,请参阅 CSFLE 服务器端模式执行。
了解详情
要了解有关密钥保管库集合、数据加密密钥和客户主密钥的详情,请参阅密钥和密钥保管库。
要了解有关 KMS 提供程序和 kmsProviders 对象的详情,请参阅 CSFLE KMS 提供程序。