我们有一个用 ActionScript 编写的遗留工具,它使用 AES-CBC 和硬编码的共享密钥对输入进行加密。我正在尝试在 Typescript 中编写相应的解密函数但没有成功。AS3 端使用带有 NullPad 和空(例如长度 0)IV 的 Hurlant as3crypto 库。另外值得注意的是,共享密钥只有 15 个字节而不是 16 个。as3crypto 似乎并不介意这一点,我也尝试过使用 16 字节密钥但没有成功。我的目标是修复 Javascript 中的 aesDecrypt() 函数,以便我可以成功解密 AS3 aesEncrypt() 函数的输出。
下面我有 AS3 中的 encypt(以及工作解密对应物),然后是我在 Typescript 中尝试的解密(和相应的加密)函数。在此示例中,输入“test”在 AS3 中被加密为“ryhkw3BmJ85+qBr0E9bYqw==”,但 Javascript 解密不会产生“test”。
AS3
<?xml version="1.0"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" visible="false">
<fx:Script><![CDATA[
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.crypto.symmetric.IPad;
import com.hurlant.crypto.symmetric.IVMode;
import com.hurlant.crypto.symmetric.NullPad;
import com.hurlant.util.Hex;
import mx.utils.Base64Decoder;
import mx.utils.Base64Encoder;
static var KEY:String = "vI^diTubIwH]Gag";
internal static function aesEncrypt(decoded:String):String
{
var pad:IPad = new NullPad();
var mode:ICipher = Crypto.getCipher("aes-cbc", Hex.toArray(Hex.fromString(KEY)), pad);
pad.setBlockSize(mode.getBlockSize());
var iv:ByteArray = new ByteArray();
if (mode is IVMode) {
(mode as IVMode).IV = iv;
}
var encoder:Base64Encoder = new Base64Encoder();
var ba:ByteArray = Hex.toArray(Hex.fromString(decoded));
mode.encrypt(ba);
encoder.reset();
encoder.encodeBytes(ba);
return encoder.toString();
}
internal static function aesDecrypt(encoded:String):String
{
var pad:IPad = new NullPad();
var mode:ICipher = Crypto.getCipher("aes-cbc", Hex.toArray(Hex.fromString(KEY)), pad);
pad.setBlockSize(mode.getBlockSize());
var iv:ByteArray = new ByteArray();
if (mode is IVMode) {
(mode as IVMode).IV = iv;
}
var decoder:Base64Decoder = new Base64Decoder();
decoder.reset();
decoder.decode(encoded);
var ba:ByteArray = decoder.toByteArray();
mode.decrypt(ba);
return Hex.toString(Hex.fromArray(ba));
}
trace(aesEncrypt('test'));
trace(aesDecrypt('ryhkw3BmJ85+qBr0E9bYqw=='));
]]></fx:Script>
</s:Application>
节点.js
let crypto = require('crypto');
function aesEncrypt(cleartext:string, cipherType:string = 'AES', keyBitLength:number = 128, mode:string = 'CBC', cryptkey:string = 'vI^diTubIwH]Gag') {
const algorithm = `${cipherType.toLowerCase()}-${keyBitLength}-${mode.toLowerCase()}`;
const blockSize = keyBitLength / 8;
const key = Buffer.from(cryptkey);
const paddedKey = nullPad(key, blockSize);
const iv = Buffer.alloc(blockSize, 0);
const cipher = crypto.createCipheriv(algorithm, paddedKey, iv);
cipher.setAutoPadding(false);
const encodedInputBuffer = Buffer.from(cleartext, 'utf8');
const encodedInput = encodedInputBuffer.toString('hex');
const paddedInputBuffer = nullPad(Buffer.from(encodedInput), blockSize);
const encrypted = Buffer.concat([cipher.update(paddedInputBuffer), cipher.final()])
return encrypted.toString('base64');
}
function aesDecrypt(encoded:string, cipherType:string = 'AES', keyBitLength:number = 128, mode:string = 'CBC', cryptkey:string = 'vI^diTubIwH]Gag') {
const algorithm = `${cipherType.toLowerCase()}-${keyBitLength}-${mode.toLowerCase()}`;
const blockSize = keyBitLength / 8;
const key = Buffer.from(cryptkey);
const paddedKey = nullPad(key, blockSize);
let iv = Buffer.alloc(blockSize, 0);
const decipher = crypto.createDecipheriv(algorithm, paddedKey, iv);
decipher.setAutoPadding(false);
const decodedInput = Buffer.from(encoded, 'base64').toString( 'binary');
let decrypted = Buffer.concat([decipher.update(decodedInput, 'binary'), decipher.final()]);
return decrypted.toString().trim();
}
function nullPad(input:Buffer, length:number) {
const nullPad = Buffer.alloc(length);
let padLength = length - (input.length % length);
if (padLength == length) {
padLength = 0;
}
if(padLength > 0) {
input = Buffer.concat([input, nullPad.slice(0, padLength)]);
}
return input;
}
// should output "ryhkw3BmJ85+qBr0E9bYqw=="
console.log("Encrypt 'test'\t\t\t\t>>>\t" + aesEncrypt("test"));
// should output "test"
console.log("Decrypt 'ryhkw3BmJ85+qBr0E9bYqw=='\t>>>\t" + aesDecrypt("ryhkw3BmJ85+qBr0E9bYqw=="));
// working roundtrips
console.log("Decrypt '+3C9rjmO7W7hIQqcJMPgXQ=='\t>>>\t" + Buffer.from(aesDecrypt("+3C9rjmO7W7hIQqcJMPgXQ=="), 'hex').toString());
console.log("Decrypt 'elaKkhcvG75EBFvZwB1KiA=='\t>>>\t" + aesDecrypt("elaKkhcvG75EBFvZwB1KiA=="));
不幸的是,我们无法像我们希望的那样更新 ActionScript 工具的旧代码(尤其是因为空 IV 和空填充都不好,更不用说非标准密钥长度了)。如果有人可以帮助向我展示如何修复 Javascript 端以便能够解密,我将不胜感激。我更喜欢纯 Node-crypto 解决方案,但很高兴使用 CryptoJS 或 Forge(如果两者都可以的话)(我也没有成功使用它们来完成这项任务)。