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

AES-256-GCM在PHP与TypeScript Crypto中结果不一致的解决求助

问题

我尝试将PHP中基于AES-256-GCM的加密逻辑迁移至Node.js(TypeScript),但两者生成的加密结果存在差异,具体如下:

PHP示例代码

<?php

function Encrypt(string $data = "", string $merKey = "", string $merIV = "")
{
    $tag = ""; // default empty
    $encrypted = openssl_encrypt($data, "aes-256-gcm", trim($merKey), 0, trim($merIV), $tag);

    return trim(bin2hex($encrypted . ":::" . base64_encode($tag)));
}

// For example
$data = "exampleData";
// Secret
$merKey = "12345678901234567890123456789012";
$merIV  = "1234567890123456";

$encryptStr = Encrypt($data, $merKey, $merIV);
print_r($encryptStr);
// 输出:4d38727078724f39655347563066513d3a3a3a5a46415a536c374a35326c36513344655833754768413d3d
?>

Node.js(TypeScript)示例代码

import { createCipheriv } from "crypto";

let authTag: Buffer;

class SymmetricEncryptor {
  private readonly algorithm = "aes-256-gcm";
  private readonly secretKey: Buffer;
  private readonly iv: Buffer;

  public constructor(strKey: string, strIV: string) {
    this.secretKey = Buffer.from(strKey);
    this.iv = Buffer.from(strIV);
  }

  public encrypt(original: Buffer): Buffer {
    const cipher = createCipheriv(this.algorithm, this.secretKey, this.iv);
    let cipherText = cipher.update(original);
    cipherText = Buffer.concat([cipherText, cipher.final()]);
    authTag = cipher.getAuthTag();

    return Buffer.concat([
      cipherText,
      Buffer.from(":::"),
      Buffer.from(authTag.toString("base64"))
    ]);
  }
}

const merKey = "12345678901234567890123456789012";
const merIV = "1234567890123456";
const symmetricEncryptor = new SymmetricEncryptor(merKey, merIV);

let data = "exampleData";
const encryptedData = symmetricEncryptor.encrypt(
  Buffer.from(data, "utf-8")
);
console.log(encryptedData.toString('hex'));
// 输出:33cae9c6b3bd792195d1f43a3a3a5a46415a536c374a35326c36513344655833754768413d3d

其中:::分隔符后的编码数据是正确的,但分隔符前的内容存在差异。我推测问题可能出在TypeScript的填充设置上,但不知如何修复;我尝试修改PHP中openssl_encrypt的options参数从0改为3,结果仍一致,但无法修改PHP的逻辑。请问如何修改上述Node.js代码以生成与PHP相同的加密结果?

解决方案

问题核心在于PHP的openssl_encrypt默认会把加密后的原始二进制数据做Base64编码,而当前Node.js代码直接使用了加密后的原始二进制字节,没有做Base64编码,这就导致了分隔符前的内容差异。

修改后的Node.js(TypeScript)代码如下:

import { createCipheriv } from "crypto";

let authTag: Buffer;

class SymmetricEncryptor {
  private readonly algorithm = "aes-256-gcm";
  private readonly secretKey: Buffer;
  private readonly iv: Buffer;

  public constructor(strKey: string, strIV: string) {
    this.secretKey = Buffer.from(strKey);
    this.iv = Buffer.from(strIV);
  }

  public encrypt(original: Buffer): Buffer {
    const cipher = createCipheriv(this.algorithm, this.secretKey, this.iv);
    let cipherText = cipher.update(original);
    cipherText = Buffer.concat([cipherText, cipher.final()]);
    authTag = cipher.getAuthTag();

    // 关键改动:将加密后的原始二进制数据做Base64编码,再转成Buffer
    const base64CipherText = Buffer.from(cipherText.toString('base64'));
    
    return Buffer.concat([
      base64CipherText,
      Buffer.from(":::"),
      Buffer.from(authTag.toString("base64"))
    ]);
  }
}

const merKey = "12345678901234567890123456789012";
const merIV = "1234567890123456";
const symmetricEncryptor = new SymmetricEncryptor(merKey, merIV);

let data = "exampleData";
const encryptedData = symmetricEncryptor.encrypt(
  Buffer.from(data, "utf-8")
);
console.log(encryptedData.toString('hex'));
// 现在输出会和PHP一致:4d38727078724f39655347563066513d3a3a3a5a46415a536c374a35326c36513344655833754768413d3d

改动说明

  1. PHP的openssl_encrypt函数在默认选项(options=0)下,返回的是Base64编码后的密文,而非原始二进制密文。
  2. 原Node.js代码直接使用了cipher.update()和cipher.final()得到的原始二进制数据,没有做Base64编码,导致两者密文部分的编码方式不一致。
  3. 修改后,先将原始二进制密文转成Base64字符串,再转成Buffer,这样拼接后的内容就和PHP的$encrypted部分完全一致了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:18:15