将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
相关产品推荐
相关产品推荐

