You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB本地MasterKey轮换失败:报Unexpected field: 'key'错误

问题:本地MasterKey轮换时调用rewrapManyDataKey报错"Unexpected field: 'key'"

我的应用使用本地masterKey解密MongoDB服务器中的数据,计划每年手动触发独立API轮换密钥文件。尝试用Java 8(依赖mongodb-driver-sync-5.0.0、mongodb-crypt-1.8.0)和Node.js(Mongosh 2.3.1)创建新本地masterKey文件并重加密keyVault集合中的DEK,但均触发相同错误,不确定实现是否正确。

原Java代码

kmsProviders.put("local", newlyCreatedMasterKey);

ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
                .keyVaultMongoClientSettings(MongoClientSettings.builder()
                        .applyConnectionString(new ConnectionString(connectionString))
                        .build())
                .keyVaultNamespace(keyVaultDb + "." + keyVaultColl)
                .kmsProviders(kmsProviders)
                .build();

MongoCollection<Document> keyVault = mongoClient.getDatabase(keyVaultDb).getCollection(keyVaultColl);
for (Document dataKeyDoc : keyVault.find()) {
    Binary id = dataKeyDoc.get("_id", Binary.class);
    BsonBinary dataKeyId = new BsonBinary(id.getType(), id.getData());

    clientEncryption.rewrapManyDataKey(
        Filters.eq("_id", dataKeyId),
        new RewrapManyDataKeyOptions()
            .provider("local")
            .masterKey(new BsonDocument("key", new BsonBinary(newlyCreatedMasterKey)))
    );
}

原Node.js代码

// Creating a new key
const key = require("crypto").randomBytes(96);
fs.writeFileSync('keyfile.txt', key);

// Connection options
var autoEncryptionOpts = {
   "keyVaultNamespace" : "encryption.__keyvault",
   "kmsProviders" : {
     "local" : {
       "key" : BinData(0, key.toString("base64"))
     }
   }
 }

// Created the encrypted client
const client = new Mongo(uri, autoEncryptionOpts);
const keyVault = client.getKeyVault();
const result = keyVault.rewrapManyDataKey({}, {
  provider: 'local',
  masterKey: {
    keyMaterial: BinData(0, key.toString("base64"))
  }
})

错误信息

Error rewrapping data keys: Unexpected field: 'key'

Exception in thread "main" com.mongodb.MongoClientException: Exception in encryption library: Unexpected field: 'key'
        at com.mongodb.client.internal.Crypt.wrapInMongoException(Crypt.java:375)
        at com.mongodb.client.internal.Crypt.rewrapManyDataKey(Crypt.java:260)
        at com.mongodb.client.internal.ClientEncryptionImpl.rewrapManyDataKey(ClientEncryptionImpl.java:173)
        at RotateMasterKey.reEncryptDataKeys(RotateMasterKey.java:122)
        at RotateMasterKey.main(RotateMasterKey.java:56)
Caused by: com.mongodb.crypt.capi.MongoCryptException: Unexpected field: 'key'
        at com.mongodb.crypt.capi.MongoCryptContextImpl.throwExceptionFromStatus(MongoCryptContextImpl.java:156)
        at com.mongodb.crypt.capi.MongoCryptImpl.configure(MongoCryptImpl.java:376)
        at com.mongodb.crypt.capi.MongoCryptImpl.createRewrapManyDatakeyContext(MongoCryptImpl.java:311)
        at com.mongodb.client.internal.Crypt.rewrapManyDataKey(Crypt.java:251)
        ... 3 more

问题分析

错误核心是本地KMS的masterKey参数格式错误。与云KMS(如AWS KMS)不同,MongoDB本地KMS不需要将密钥包装在带字段名的文档中,直接传入二进制密钥数据即可。代码中错误地给masterKey添加了key或keyMaterial字段,导致加密库无法识别。


修正后的代码

Java版本

import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import org.bson.BsonBinary;
import org.bson.Document;
import com.mongodb.client.vault.ClientEncryption;
import com.mongodb.client.vault.ClientEncryptions;
import com.mongodb.client.vault.RewrapManyDataKeyOptions;
import com.mongodb.MongoClientSettings;
import com.mongodb.ConnectionString;

import java.util.HashMap;
import java.util.Map;

public class RotateMasterKey {
    public static void main(String[] args) {
        String connectionString = "mongodb://localhost:27017";
        String keyVaultDb = "encryption";
        String keyVaultColl = "__keyvault";
        
        // 生成新的96字节本地masterKey
        byte[] newlyCreatedMasterKey = new byte[96];
        new java.security.SecureRandom().nextBytes(newlyCreatedMasterKey);

        // 初始化KMS提供者:local直接绑定密钥字节数组
        Map<String, Object> kmsProviders = new HashMap<>();
        kmsProviders.put("local", newlyCreatedMasterKey);

        // 配置ClientEncryption
        ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
                .keyVaultMongoClientSettings(MongoClientSettings.builder()
                        .applyConnectionString(new ConnectionString(connectionString))
                        .build())
                .keyVaultNamespace(keyVaultDb + "." + keyVaultColl)
                .kmsProviders(kmsProviders)
                .build();

        try (MongoClient mongoClient = MongoClients.create(connectionString);
             ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings)) {

            // 批量重加密所有DEK,无需循环单个处理
            RewrapManyDataKeyOptions options = new RewrapManyDataKeyOptions()
                    .provider("local")
                    .masterKey(new BsonBinary(newlyCreatedMasterKey)); // 直接传入二进制密钥,无需包装文档
            clientEncryption.rewrapManyDataKey(Filters.empty(), options);
            
            System.out.println("所有数据密钥已完成重加密");
        }
    }
}

Node.js版本

const crypto = require("crypto");
const fs = require("fs");
const { MongoClient } = require("mongodb");

const uri = "mongodb://localhost:27017";
const keyVaultNamespace = "encryption.__keyvault";

// 生成新的96字节本地masterKey
const newMasterKey = crypto.randomBytes(96);
fs.writeFileSync('new-local-keyfile.bin', newMasterKey);

async function rotateMasterKey() {
    // 配置自动加密选项:local KMS直接传入Buffer类型的密钥
    const autoEncryptionOpts = {
        keyVaultNamespace: keyVaultNamespace,
        kmsProviders: {
            local: {
                key: newMasterKey // 直接传Buffer,无需BinData包装
            }
        }
    };

    const client = new MongoClient(uri, autoEncryptionOpts);
    try {
        await client.connect();
        const keyVault = client.getKeyVault();

        // 批量重加密所有DEK
        const result = await keyVault.rewrapManyDataKey({}, {
            provider: 'local',
            masterKey: newMasterKey // 直接传入Buffer,无需keyMaterial字段
        });

        console.log(`成功重加密 ${result.modifiedCount} 个数据密钥`);
    } finally {
        await client.close();
    }
}

rotateMasterKey().catch(err => console.error("重加密失败:", err));

额外注意事项

  1. 密钥规格:本地masterKey必须是96字节的随机字节数组,符合MongoDB客户端字段加密的要求。
  2. 批量处理:无需循环单个DEK调用rewrapManyDataKey,传入Filters.empty()(Java)或{}(Node.js)即可批量处理所有密钥,提升效率。
  3. 密钥存储:新生成的masterKey需要妥善存储,后续应用解密必须使用该密钥。

内容的提问来源于stack exchange,提问作者user3549302

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 13:37:03