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

将PHP AES256解密函数转为Ionic5 TypeScript时遇md5调用错误求解

问题:PHP AES256解密函数转TypeScript时MD5调用报错

报错提示:This expression is not callable. Type 'any[]' has no call signatures.,触发位置为两处MD5调用:md5(concatedPassphrase, true) 和 md5(md5[i - 1] + concatedPassphrase, true)

PHP原解密函数

function decrypt(string $jsonStr, string $passphrase)
{
    $json = json_decode($jsonStr, true);
    $salt = hex2bin($json["s"]);
    $iv = hex2bin($json["iv"]);
    $ct = base64_decode($json["ct"]);
    $concatedPassphrase = $passphrase . $salt;
    $md5 = [];
    $md5[0] = md5($concatedPassphrase, true);
    $result = $md5[0];
    for ($i = 1; $i < 3; $i++) {
        $md5[$i] = md5($md5[$i - 1] . $concatedPassphrase, true);
        $result .= $md5[$i];
    }
    $key = substr($result, 0, 32);
    $data = openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv);
    return json_decode($data, true);
}

转换后的TypeScript函数(存在问题版本)

async dcrypt(data: string, passphrase: string){

    const json = JSON.parse(data);
    const salt = decode(json["s"]);
    const iv = decode(json["iv"]);
    const ct = decode(json["ct"]);
    const concatedPassphrase = passphrase + salt;
    const md5 = [];
    md5[0] = md5(concatedPassphrase, true);
    let result = md5[0];
    for (let i = 1; i < 3; i++) {
      md5[i] = md5(md5[i - 1] + concatedPassphrase, true);
      result += md5[i];
    }
    const key = result.substr(0, 32);
    const importedKey = await window.crypto.subtle.importKey(
      'raw',
      new Uint8Array(key),
      {
        name: 'AES-CBC'
      },
      false,
      ['decrypt']
    );
    const data = await window.crypto.subtle.decrypt(
      {
        name: 'AES-CBC',
        iv: new Uint8Array(iv)
      },
      importedKey,
      new Uint8Array(ct)
    );
    return JSON.parse(new TextDecoder().decode(data));

  }

解决方法

1. 修复变量名冲突

你定义了数组const md5 = [],又试图调用同名的md5()函数,导致TS认为你在调用数组而非函数,直接修改数组变量名即可,比如改为md5Hashes。

2. 引入MD5实现

浏览器/TypeScript没有内置MD5函数,需要使用第三方库(比如crypto-js):

  • 安装依赖:npm install crypto-js
  • 导入库:import * as CryptoJS from 'crypto-js';

3. 匹配PHP的MD5二进制输出

PHP中md5($str, true)返回原始二进制数据,对应crypto-js中需要将哈希结果转换为Latin1编码的字符串(等价于原始字节序列),使用CryptoJS.MD5(str).toString(CryptoJS.enc.Latin1)。

4. 修正解码函数

PHP的hex2bin和base64_decode需要对应TS的二进制转换逻辑,实现两个工具函数:

// 对应PHP hex2bin
function hexToUint8Array(hex: string): Uint8Array {
  const bytes = new Uint8Array(hex.length / 2);
  for (let i = 0; i < hex.length; i += 2) {
    bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
  }
  return bytes;
}

// 对应PHP base64_decode
function base64ToUint8Array(base64: string): Uint8Array {
  const binaryStr = atob(base64);
  const bytes = new Uint8Array(binaryStr.length);
  for (let i = 0; i < binaryStr.length; i++) {
    bytes[i] = binaryStr.charCodeAt(i);
  }
  return bytes;
}

5. 完整修正后的TypeScript函数

import * as CryptoJS from 'crypto-js';

// 工具函数:十六进制转Uint8Array
function hexToUint8Array(hex: string): Uint8Array {
  const bytes = new Uint8Array(hex.length / 2);
  for (let i = 0; i < hex.length; i += 2) {
    bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
  }
  return bytes;
}

// 工具函数:Base64转Uint8Array
function base64ToUint8Array(base64: string): Uint8Array {
  const binaryStr = atob(base64);
  const bytes = new Uint8Array(binaryStr.length);
  for (let i = 0; i < binaryStr.length; i++) {
    bytes[i] = binaryStr.charCodeAt(i);
  }
  return bytes;
}

// 字符串转Uint8Array(用于拼接密码和盐)
function stringToUint8Array(str: string): Uint8Array {
  return new TextEncoder().encode(str);
}

// Uint8Array转字符串(用于哈希拼接)
function uint8ArrayToString(arr: Uint8Array): string {
  return new TextDecoder('latin1').decode(arr);
}

async decrypt(data: string, passphrase: string) {
  const json = JSON.parse(data);
  // 对应PHP的hex2bin和base64_decode
  const salt = hexToUint8Array(json["s"]);
  const iv = hexToUint8Array(json["iv"]);
  const ct = base64ToUint8Array(json["ct"]);

  // 拼接密码和盐:先转成字符串再拼接(和PHP的字符串拼接逻辑一致)
  const passphraseStr = passphrase;
  const saltStr = uint8ArrayToString(salt);
  const concatedPassphrase = passphraseStr + saltStr;

  // 替换数组变量名,避免和MD5函数冲突
  const md5Hashes: string[] = [];
  // 对应PHP md5($str, true)的二进制输出
  md5Hashes[0] = CryptoJS.MD5(concatedPassphrase).toString(CryptoJS.enc.Latin1);
  let result = md5Hashes[0];

  for (let i = 1; i < 3; i++) {
    const prevHash = md5Hashes[i - 1];
    md5Hashes[i] = CryptoJS.MD5(prevHash + concatedPassphrase).toString(CryptoJS.enc.Latin1);
    result += md5Hashes[i];
  }

  // 取前32字节作为密钥,转成Uint8Array
  const keyStr = result.substr(0, 32);
  const key = stringToUint8Array(keyStr);

  // Web Crypto API导入密钥
  const importedKey = await window.crypto.subtle.importKey(
    'raw',
    key,
    { name: 'AES-CBC' },
    false,
    ['decrypt']
  );

  // 解密
  const decryptedBuffer = await window.crypto.subtle.decrypt(
    { name: 'AES-CBC', iv: iv },
    importedKey,
    ct
  );

  // 转成字符串并解析JSON
  return JSON.parse(new TextDecoder().decode(decryptedBuffer));
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:16:10