首页 > 解决方案 > 节点 Google Cloud KMS 加密似乎有效,但解密失败

问题描述

这是我的第一个 Stack Overflow 问题!

无论如何,我正在尝试使用 Cloud KMS 为我在 App Engine 中运行的节点 API 设置数据库连接机密的解密。为了让它工作,我一直在本地测试它。我使用 gcloud CLI 对机密进行加密,然后将它们上传到 Cloud Storage 存储桶(如果重要,则在与 API 不同的项目下)。在 API 中提取加密的秘密很顺利,但是当我尝试解密这些秘密时,我得到了:

Error: 3 INVALID_ARGUMENT: Decryption failed: verify that 'name' refers to the correct CryptoKey.

我检查并重新检查了我的项目 ID、密钥环 ID、密钥 ID 是否正确。

在上传到存储桶之前,我尝试在 base64 中对加密的秘密进行编码。我尝试在 API 中对编码和加密的秘密进行硬编码。这些都不起作用。

因此,为了进行完整性检查,我重写了代码以简单地加密一个字符串,然后在 API 中使用相同的 cryptoKeyPath 对其进行解密。加密似乎有效,但在解密过程中我仍然收到上述错误。

(一些云存储代码仍然存在,但在解密之前不会被使用)。

const Storage = require('@google-cloud/storage');
console.log(process.env.GOOGLE_APPLICATION_CREDENTIALS);

// if running in production we need to get the .env file from a storage bucket and decrypt.
const addSecretsToEnv = async () => {
    // setup for storage bucket
    const bucketName=<bucketName>;
    const fileName=<fileName>;
    const storage = new Storage.Storage();
    const file = storage.bucket(bucketName).file(fileName);

    // setup for KMS
    const client = new kms.KeyManagementServiceClient();
    const locationId = 'global';
    const projectId = <projectId>;
    const keyRingID = <keyRingID>;
    const keyID = <keyID>;

    try {
        const formattedName = client.cryptoKeyPath(
            projectId,
            locationId,
            keyRingID,
            keyID,
        );

        const [result] = await client.encrypt({
            name: formattedName,
            plainText: 'help me!!!'
        });

        console.log(typeof result);
        console.log(result);

        const cipherText = result.ciphertext;
        console.log(typeof cipherText);
        console.log(cipherText);

        const [decrypted] = await client.decrypt({
            name: formattedName,  
            cipherText,
        });

        console.log(decrypted);

    } catch(error) {
        console.log(error);
    }
}

module.exports = {
    addSecretsToEnv
};

我通过 GOOGLE_APPLICATION_CREDENTIALS env 变量设置了身份验证,该变量指向一个服务帐户的 JSON 密钥文件,该服务帐户同时具有 Cloud KMS CryptoKey Encrypter/Decrypter 和 Cloud KMS 管理员角色(在绝望中添加了管理员角色)。

有人可以帮我吗?

提前致谢。

标签: node.jsencryptiongoogle-cloud-platformgoogle-cloud-kms

解决方案


资本T是你的罪魁祸首。在 Node 中,没有值的键会扩展为它们的对象名称。例如,给定:

let foo = "banana";

传递foo给这样的对象:

doTheThing({ foo });

扩展为:

doTheThing({ foo: foo }); // which is { foo: "banana" }

当您使用plainTextorcipherText时,它们分别在 Node 对象中扩展为{plainText: "..."}和。{cipherText: "..."}不幸的是,这些不是公认的领域,但它们被默默地忽略了。因此,实际上,您不会将任何明文或密文传递给任何一个 API 调用。

加密空字符串有效的,但解密空字符串是无效的。这就是为什么您在加密时没有收到错误的原因。

要解决此问题,请分别替换plainText和和。我个人的建议是,您应该明确说明对函数的参数调用:cipherTextplaintextciphertext

const [decrypted] = await client.decrypt({
  name: "projects/p/...",  
  ciphertext: myCiphertext,
});

否则,对变量进行细微的重命名可能会以非常晦涩的方式彻底破坏代码。


推荐阅读