NodeJS WhatsApp流解密错误-错误:02000079:rsa例程::oaep解码错误

n53p2ov0  于 12个月前  发布在  Node.js
关注(0)|答案(3)|浏览(187)

我利用WhatsApp Flows API将流集成到我们的应用程序中。发布流时,我需要使用以下结构对其进行解密:

{
    encrypted_flow_data: "<ENCRYPTED FLOW DATA>",
    encrypted_aes_key: "<ENCRYPTED_AES_KEY>",
    initial_vector: "<INITIAL VECTOR>"
 }

字符串
参考此文档(https://developers. facebook.com/docs/whatsapp/flows/guides/implementingyourflowendpoint #nodejs-express-example)来解密密钥。
但是,我遇到了以下错误:

{
"code": "ERR_OSSL_RSA_OAEP_DECODING_ERROR",
"library": "rsa routines",
"reason": "oaep decoding error",
"message": "error:02000079:rsa routines::oaep decoding error",
"stack": "Error: error:02000079:rsa routines::oaep decoding error\n    at Object.privateDecrypt (node:internal/crypto/cipher:79:12)\n    at decryptRequest"


}
作为参考,这里是相关的代码片段:

const express = require("express");
const crypto = require("crypto");
const fs = require('fs');
const util = require('util');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);

require("dotenv").config();

const PORT = 3000;
const app = express();
app.use(express.json());

app.post("/data", async ({ body }, res) => {
    const PRIVATE_KEY_PATH = 'path/to/your/private_key.pem'; 
    const PRIVATE_KEY_PASSPHRASE = 'your_private_key_passphrase';

    const PRIVATE_KEY = await readFile(PRIVATE_KEY_PATH, 'utf-8');

    const { decryptedBody, aesKeyBuffer, initialVectorBuffer } = decryptRequest(
        body,
        PRIVATE_KEY,
        PRIVATE_KEY_PASSPHRASE,
    );

    const { screen, data, version, action } = decryptedBody;
    // Return the next screen & data to the client
    const screenData = {
        version,
        screen: "SCREEN_NAME",
        data: {
            some_key: "some_value",
        },
    };

    // Return the response as plaintext
    res.send(encryptResponse(screenData, aesKeyBuffer, initialVectorBuffer));
});

const decryptRequest = async (body, privatePem, passphrase) => {
    try {
        const { encrypted_aes_key, encrypted_flow_data, initial_vector } = body;

        // Decrypt the AES key created by the client
        const decryptedAesKey = crypto.privateDecrypt(
            {
                key: crypto.createPrivateKey(
                    {
                        key: privatePem,
                        passphrase: passphrase,
                    }
                ),
                padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
                oaepHash: "sha256",
            },
            Buffer.from(encrypted_aes_key, "base64"),
        );

        // Decrypt the Flow data
        const flowDataBuffer = Buffer.from(encrypted_flow_data, "base64");
        const initialVectorBuffer = Buffer.from(initial_vector, "base64");

        const TAG_LENGTH = 16;
        const encrypted_flow_data_body = flowDataBuffer.subarray(0, -TAG_LENGTH);
        const encrypted_flow_data_tag = flowDataBuffer.subarray(-TAG_LENGTH);

        const decipher = crypto.createDecipheriv(
            "aes-128-gcm",
            decryptedAesKey,
            initialVectorBuffer,
        );
        decipher.setAuthTag(encrypted_flow_data_tag);

        const decryptedJSONString = Buffer.concat([
            decipher.update(encrypted_flow_data_body),
            decipher.final(),
        ]).toString("utf-8");

        return {
            decryptedBody: JSON.parse(decryptedJSONString),
            aesKeyBuffer: decryptedAesKey,
            initialVectorBuffer,
        };
    } catch (e) {
        console.log('Error decoding:', e)
    }
};

const encryptResponse = (
    response,
    aesKeyBuffer,
    initialVectorBuffer,
) => {
    // Flip the initialization vector
    const flipped_iv = [];
    for (const pair of initialVectorBuffer.entries()) {
        flipped_iv.push(~pair[1]);
    }
    // Encrypt the response data
    const cipher = crypto.createCipheriv(
        "aes-128-gcm",
        aesKeyBuffer,
        Buffer.from(flipped_iv),
    );
    return Buffer.concat([
        cipher.update(JSON.stringify(response), "utf-8"),
        cipher.final(),
        cipher.getAuthTag(),
    ]).toString("base64");
};

app.listen(PORT, () => {
    console.log(`App is listening on port ${PORT}!`);
});


你能帮我解决这个问题吗?我很感激你的帮助。谢谢。

u7up0aaq

u7up0aaq1#

如果您绝对确定您使用的私钥与Meta使用的公钥相对应,则继续使用方法1,否则首先执行方法2,如果错误仍然存在,则返回方法1。

方法一:

我也面临着同样的问题,现在虽然我不能具体解决这个错误,但我发现了一个来自Meta的小故障示例,这似乎对我有效(它不是抛出这个错误)。
访问故障项目(Meta版本)
我唯一要做改变是,当我把私钥放到环境变量中时,它抛出了错误,所以我把它放到了assets文件夹中,然后用fs来读取它作为参考,您可以访问我在Glitch上项目
访问故障项目(我的版本)

方法二:

  1. Regenerate the key pair, then upload the public key to Meta server, as mentioned here的一个。
    1.更新服务器中的私钥。
    1.使其工作的关键步骤:从WhatsApp客户端发出数据交换请求,并从您的webhook发送一个状态代码为421的错误,这将迫使WhatsApp客户端重新获取公钥。
res.sendStatus(421) // Express.js

字符串
1.要有耐心:通常,新生成的密钥对需要大约30分钟才能生效(从webhook回复状态代码421时算起)。

xlpyo6sf

xlpyo6sf2#

我觉得解密代码没问题。问题可能出在私钥上。
1.你能验证私钥和公钥是否匹配吗?你可以记录私钥以确认它是正确的。
1.你能用公钥加密任何任意的有效载荷,并验证你能用当前的代码解密它吗?

mpbci0fu

mpbci0fu3#

我发现这取决于WELJ的版本。如果你使用的是版本100,那就不行了。但是使用版本200,反之亦然。但是现在,似乎版本100已经被弃用了。所以在你的WELJ文件中,放:

{
    version: 200,
    data_api_version: 200
}

字符串
应该能用
还发现更新一个publicKey到Meta,我们需要等到他们的缓存生效。否则,所有的martedRequest都会用旧的publicKey加密。

相关问题