显式加密
在此页面上
Overview
显式加密提供了对安全性的细粒度控制,但代价是在配置集合和为 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, { 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>"] }
创建 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, { useNewUrlParser: true, useUnifiedTopology: true, });
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.foods client_encryption = ClientEncryption( kms_providers, "encryption.___keyVault", client, coll.codec_options, )
注意
CodecOptions
MongoDB Python 驱动程序要求您指定要用于加密和解密文档的 CodecOptions
。
指定您在 MongoClient
、Database
或 Collection
上配置的 CodecOptions
,用于将加密和解密的应用程序数据写入 MongoDB。
加密字段并插入
您想使用以下算法对文档的字段进行加密:
字段名称 | 加密算法 | BSON 字段类型 |
---|---|---|
name | 确定性 | 字符串 |
age | 无加密 | Int |
favorite-foods | 随机 | 阵列 |
以下代码片段显示如何手动加密文档中的字段并将文档插入 MongoDB:
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, });
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 } });
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) }
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));
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, });
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.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 提供程序。