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

显式加密

在此页面上

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

了解如何使用客户端字段级加密 (CSFLE) 的显式加密机制。

显式加密是一种机制,您可以在其中指定在对数据库执行的每次操作中希望如何加密和解密文档中的字段。

显式加密是一种机制,您可以在其中指定在对数据库执行的每次操作中希望如何加密和解密文档中的字段。

以下 4.2 或更高版本的 MongoDB 产品提供显式加密:

  • MongoDB 社区服务器

  • MongoDB Enterprise Advanced

  • MongoDB Atlas

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

  • 创建 ClientEncryption 实例

  • 加密读写操作中的字段

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

要使用显式加密,必须创建一个 ClientEncryption实例。 ClientEncryption是跨驱动程序和mongosh使用的抽象,用于封装显式加密中涉及的 Key Vault 集合和 KMS操作。

要创建 ClientEncryption 实例,您必须指定以下信息:

  • 可以访问您的密钥保管库集合的 MongoClient 实例

  • 密钥保管库集合的命名空间

  • 配置为能够访问托管客户主密钥的KMSkmsProviders对象

有关更多 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, {
useNewUrlParser: true,
useUnifiedTopology: true,
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, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
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 对象的更多信息,请参阅 CSFLE KMS 提供程序

后退

自动加密