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

将CryptoJS加密逻辑转为PHP后服务端拒绝请求的问题排查

问题描述

我有一套可正常完成数据加解密的CryptoJS代码,将加密逻辑迁移到PHP实现后,发现PHP加密的结果能被JS正常解密,但会被服务端拒绝;而JS加密的结果则可被服务端正常接受。无法访问后端服务,以下是可用的JS加密解密代码及对应的PHP实现代码,请问问题出在哪里?最初怀疑是单双引号的问题,但不清楚后端逻辑。

可用的JS代码

let u = CryptoJS;

function decrypt(e) {
  let t = (e = u.enc.Base64.parse(e)).toString(u.enc.Utf8);
  t = JSON.parse(t);
  let a = u.enc.Base64.parse(t.iv);
  var n = u.AES.decrypt(
    t.value,
    u.enc.Utf8.parse([KEY]),
    {
      iv: a,
      mode: u.mode.CBC,
      padding: u.pad.Pkcs7
    }
  );
  return (n = u.enc.Utf8.stringify(n));
}

function encrypt(e) {
  let t = u.lib.WordArray.random(16),
    a = u.enc.Utf8.parse([KEY]),
    n = { iv: t, mode: u.mode.CBC, padding: u.pad.Pkcs7 },
    l = u.AES.encrypt(e, a, n);
  (l = l.toString()), (t = u.enc.Base64.stringify(t));
  let r = {
    iv: t,
    value: l,
    mac: u.HmacSHA256(t + l, a).toString()
  };
  return (
    (r = JSON.stringify(r)),
    (r = u.enc.Utf8.parse(r)),
    u.enc.Base64.stringify(r)
  );
}

原PHP实现代码

function enc($plainData,$secrete_key){

    $key = base64_decode($secrete_key);
    $iv = base64_encode(random_bytes(openssl_cipher_iv_length('AES-256-CBC')));

    $value = openssl_encrypt($plainData, 'AES-256-CBC', $secrete_key, 0, base64_decode($iv));

    $mac = hash('sha256', $iv.$value.$secrete_key);
    $json = json_encode(["iv"=>$iv,"value"=>$value,"mac"=>$mac], JSON_UNESCAPED_UNICODE);
    return base64_encode($json);
}

$data = [
    "start_date" => "2023-07-23 00:00:00",
    "end_date" => "2023-07-23 23:59:59",
    "graph_uuid" => "1234567890",
    "ignore_zero" => true,
];

$enc = enc(json_encode($data),env(KEY))
问题排查与修正

对比JS和PHP代码,存在以下几个关键差异,这些差异是服务端拒绝PHP加密结果的核心原因:

  • 密钥处理逻辑不一致
    JS直接将KEY的UTF-8字节作为密钥,未做Base64解码;PHP中先对密钥做Base64解码,加密时却又用原始密钥,逻辑混乱且和JS不符。

  • MAC算法错误
    JS使用的是HMAC-SHA256算法,而PHP用的是普通SHA256哈希,二者完全不是同一算法,服务端校验MAC时会失败。

  • AES加密参数不匹配
    JS会根据密钥字节长度自动选择AES-128/192/256;PHP硬编码使用AES-256-CBC,若密钥长度不符合32字节,openssl会自动调整,导致加密结果和JS不一致。

  • JSON序列化细节差异
    PHP使用JSON_UNESCAPED_UNICODE参数,和JS默认的JSON序列化行为不同,可能导致后续Base64编码结果不一致。

修正后的PHP代码

function enc($plainData, $secret_key){
    // 生成16字节随机IV,和JS的WordArray.random(16)逻辑一致
    $iv_bytes = random_bytes(16);
    $iv = base64_encode($iv_bytes);
    
    // 密钥处理和JS对齐:取KEY的UTF-8字节
    $key = mb_convert_encoding($secret_key, 'UTF-8');
    
    // 根据密钥字节长度自动匹配AES版本
    $key_len = strlen($key);
    $cipher = match(true) {
        $key_len == 16 => 'AES-128-CBC',
        $key_len == 24 => 'AES-192-CBC',
        $key_len == 32 => 'AES-256-CBC',
        default => throw new Exception("密钥长度无效:必须是16、24或32字节")
    };
    
    // 加密:结果为Base64编码,和JS的l.toString()一致
    $value = openssl_encrypt($plainData, $cipher, $key, 0, $iv_bytes);
    
    // 计算HMAC-SHA256,和JS的HmacSHA256(t + l, a)逻辑一致
    $mac = hash_hmac('sha256', $iv . $value, $key);
    
    // JSON序列化保持默认,和JS的JSON.stringify行为一致
    $json = json_encode(["iv" => $iv, "value" => $value, "mac" => $mac]);
    
    // 最后转Base64输出,和JS逻辑对齐
    return base64_encode($json);
}

$data = [
    "start_date" => "2023-07-23 00:00:00",
    "end_date" => "2023-07-23 23:59:59",
    "graph_uuid" => "1234567890",
    "ignore_zero" => true,
];

$enc = enc(json_encode($data), env('KEY'));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:14:51