显式加密
在本页面上
概述
显式加密提供了对安全性的精细控制,但需要在配置集合和为MongoDB驱动程序编写代码时承担增加的复杂性。使用显式加密时,您需要指定如何对数据库中每个操作执行的文档字段进行加密,并在您的整个应用程序中包含此逻辑。
以下MongoDB产品支持显式加密
MongoDB社区服务器
MongoDB企业高级版
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企业高级版
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>"] }
创建 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.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 提供者。