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

显式加密

在此页面上

  • Overview
  • 使用显式加密
  • 创建 ClientEncryption 实例
  • 加密读写操作中的字段
  • 手动解密
  • 自动解密
  • 例子
  • 创建 MongoClient 实例
  • 创建 ClientEncryption 实例
  • 加密字段并插入
  • 检索文档和解密字段
  • 服务器端字段级加密实施
  • 了解详情

显式加密提供了对安全性的细粒度控制,但代价是在配置集合和为 MongoDB 驱动程序编写代码时增加了复杂性。 通过显式加密,您可以为对数据库执行的每个操作指定如何加密文档中的字段,并在整个应用程序中包含此逻辑。

以下 MongoDB 产品提供显式加密:

  • MongoDB 社区服务器

  • MongoDB Enterprise Advanced

  • MongoDB Atlas

要使用显式加密,您必须在启用 CSFLE 的应用程序中执行以下操作:

  • 创建 ClientEncryption 实例

  • 加密读写操作中的字段

  • 手动自动解密文档中的字段

要使用显式加密,必须创建一个 ClientEncryption实例。 ClientEncryption是跨驱动程序和mongosh使用的抽象,用于封装显式加密中涉及的 Key Vault 集合和 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

要查看演示如何启用自动解密的代码片段,请选择与首选语言对应的标签页:

var autoEncryptionOpts = {
keyVaultNamespace: keyVaultNamespace,
kmsProviders: kmsProviders,
bypassAutoEncryption: true,
};
var encryptedClient = Mongo(
connectionString,
autoEncryptionOpts
);
var clientSettings = MongoClientSettings.FromConnectionString(connectionString);
var autoEncryptionOptions = new AutoEncryptionOptions(
keyVaultNamespace: keyVaultNamespace,
kmsProviders: kmsProviders,
bypassAutoEncryption: true);
clientSettings.AutoEncryptionOptions = autoEncryptionOptions;
var client = new MongoClient(clientSettings);
autoEncryptionOpts := options.AutoEncryption().
SetKmsProviders(kmsProviders).
SetKeyVaultNamespace(KeyVaultNamespace).
SetBypassAutoEncryption(true)
client, 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() {
_ = client.Disconnect(context.TODO())
}()
MongoClientSettings clientSettings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(connectionString))
.autoEncryptionSettings(AutoEncryptionSettings.builder()
.keyVaultNamespace(keyVaultNamespace)
.kmsProviders(kmsProviders).bypassAutoEncryption(true)
.build())
.build();
MongoClient mongoClient = MongoClients.create(clientSettings);
const client = new MongoClient(connectionString, {
monitorCommands: true,
autoEncryption: {
keyVaultNamespace,
kmsProviders,
bypassAutoEncryption: true,
},
});
auto_encryption_opts = AutoEncryptionOpts(
kms_providers=kms_providers,
key_vault_namespace=key_vault_namespace,
bypass_auto_encryption=True,
)
client = MongoClient(auto_encryption_opts=auto_encryption_opts)

假设您想在 MongoDB 实例中插入结构如下的文档:

{
"name": "<name of person>",
"age": <age of person>,
"favorite-foods": ["<array of foods>"]
}
1

在此示例中,您使用相同的 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)
2

以下代码片段展示了如何创建 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.foods
client_encryption = ClientEncryption(
kms_providers,
"encryption.___keyVault",
client,
coll.codec_options,
)

注意

CodecOptions

MongoDB Python 驱动程序要求您指定要用于加密和解密文档的 CodecOptions

指定您在 MongoClientDatabaseCollection 上配置的 CodecOptions,用于将加密和解密的应用程序数据写入 MongoDB。

3

您想使用以下算法对文档的字段进行加密:

字段名称
加密算法
BSON 字段类型

name

确定性

字符串

age

无加密

Int

favorite-foods

随机

阵列

以下代码片段显示如何手动加密文档中的字段并将文档插入 MongoDB:

注意

以下示例中的 dataKeyId 变量指的是数据加密密钥 (DEK)。要了解如何使用本地密钥提供程序生成 DEK,请参阅快速入门。要了解如何使用特定密钥管理系统创建 DEK,请参阅教程

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,请参阅教程

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,请参阅教程

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,请参阅教程

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,请参阅教程

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,请参阅教程

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})
4

以下代码片段说明了如何检索插入的文档,并手动解密加密的字段:

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.M
err = 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"] = foodsDecrypted
result["name"] = nameDecrypted
fmt.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对象的更多信息,请参阅KMS 提供程序。

后退

自动加密