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

Node+TypeScript开发WhatsApp Bot:音频解密后OGG文件损坏问题

WhatsApp音频解密后文件损坏问题

我使用Node.js和TypeScript开发WhatsApp聊天机器人,整体功能运行正常,但接收的音频为加密格式。消息中包含url、mimetype、mediaKey等可用于解密的信息,我已实现主处理、加密文件下载、解密三个方法。执行后虽能生成对应的文件,但得到的OGG音频文件已损坏无法播放。

现有代码实现

音频处理代码

import fs from 'fs/promises';
import fetch from 'node-fetch';
import crypto from 'crypto';
import MessageBotAudio from '../interfaces/messageBotAudio';

export async function downloadFile(url: string, filePath: string): Promise<string> {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
    }

    const buffer = await response.buffer();
    await fs.writeFile(filePath, buffer);

    return filePath;
}

const decryptAudio = async (encryptedFilePath: string, decryptedFilePath: string, audioMessage: MessageBotAudio) => {
    try {
        const key = Buffer.from(audioMessage.audioMessage.mediaKey);
        const iv = Buffer.alloc(16, 0);

        const decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
        const encryptedData = await fs.readFile(encryptedFilePath);
        const decryptedData = Buffer.concat([decipher.update(encryptedData), decipher.final()]);

        await fs.writeFile(decryptedFilePath, decryptedData);

        return decryptedFilePath;
    } catch (error: any) {
        console.error('Error al desencriptar el archivo:', error.message);
        throw error;
    }
};

export const handleAudioMessage = async (audioMessage: MessageBotAudio): Promise<string> => {
    const encryptedFilePath = './audio.enc';
    const decryptedFilePath = './audio.ogg';

    await downloadFile(audioMessage.audioMessage.url, encryptedFilePath);

    return await decryptAudio(encryptedFilePath, decryptedFilePath, audioMessage);
};

消息结构定义

export default interface MessageBotAudio {
    audioMessage: AudioMessage;
    messageContextInfo: MessageContextInfo;
}

interface AudioMessage {
    url: string;
    mimetype: string;
    fileEncSha256: Uint8Array;
    fileLength: Long;
    seconds: number;
    ptt: boolean;
    mediaKey: Uint8Array;
    fileSha256: Uint8Array;
    directPath: string;
    mediaKeyTimestamp: Long;
    streamingSidecar: Uint8Array;
    waveform: Uint8Array;
}

interface MessageContextInfo {
    deviceListMetadata: DeviceListMetadata;
    deviceListMetadataVersion: number;
}

interface DeviceListMetadata {
    senderKeyIndexes: number[];
    recipientKeyIndexes: number[];
    senderKeyHash: Uint8Array[];
    recipientKeyHash: Uint8Array[];
    recipientTimestamp: Long[];
}

问题原因及修复方案

核心问题

  1. MediaKey未正确衍生:WhatsApp的mediaKey不能直接作为AES密钥使用,需要通过HKDF算法衍生出实际的加密密钥。
  2. IV生成错误:不能使用全0的IV,需要结合mediaKeyTimestamp生成符合要求的初始化向量。

修正后的解密代码

const decryptAudio = async (encryptedFilePath: string, decryptedFilePath: string, audioMessage: MessageBotAudio) => {
    try {
        const mediaKey = Buffer.from(audioMessage.audioMessage.mediaKey);
        // 1. 通过HKDF衍生AES密钥
        const hkdfSalt = Buffer.from('WhatsApp Audio', 'utf8');
        const hkdfInfo = Buffer.from('WhatsApp Keys', 'utf8');
        const derivedKey = crypto.hkdfSync('sha256', mediaKey, hkdfSalt, hkdfInfo, 32);

        // 2. 生成正确的IV:mediaKeyTimestamp转8字节大端,前补8个0凑16字节
        const timestampBuffer = Buffer.alloc(8);
        timestampBuffer.writeBigInt64BE(BigInt(audioMessage.audioMessage.mediaKeyTimestamp.toString()), 0);
        const iv = Buffer.concat([Buffer.alloc(8), timestampBuffer]);

        // 3. 执行解密
        const decipher = crypto.createDecipheriv('aes-256-ctr', derivedKey, iv);
        const encryptedData = await fs.readFile(encryptedFilePath);
        // CTR模式不需要自动填充,显式关闭避免数据冗余
        decipher.setAutoPadding(false);
        const decryptedData = Buffer.concat([decipher.update(encryptedData), decipher.final()]);

        await fs.writeFile(decryptedFilePath, decryptedData);

        return decryptedFilePath;
    } catch (error: any) {
        console.error('Error al desencriptar el archivo:', error.message);
        throw error;
    }
};

关键说明

  • HKDF参数:WhatsApp对音频媒体固定使用salt=WhatsApp Audio、info=WhatsApp Keys,输出32字节对应AES-256密钥。
  • IV构造:将mediaKeyTimestamp转为8字节大端数组后,拼接8个字节的0,组成符合要求的16字节IV。
  • 自动填充:CTR模式不需要自动填充,关闭后可避免解密数据出现多余填充字节,导致音频文件结构损坏。

内容的提问来源于stack exchange,提问作者Juan Antonio Marquez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:03:13