У нас есть устаревший инструмент, написанный на ActionScript, который шифрует ввод с помощью AES-CB C и жестко закодированный общий ключ. Я пытаюсь написать соответствующую функцию расшифровки в Typescript, но не удалось. Сторона AS3 использует библиотеку Hurlant as3crypto с NullPad и пустой (например, длина 0) IV. Также стоит отметить, что общий секрет составляет всего 15 байтов вместо 16. as3crypto, похоже, не возражает против этого, и я также попытался с 16-байтовым ключом без успеха. Моя цель - исправить функцию aesDecrypt () в Javascript, чтобы я мог успешно расшифровать вывод функции AS3 aesEncrypt ().
Ниже у меня есть encypt (а также рабочий аналог расшифровки) в AS3, за которым следует моя попытка расшифровать (и соответствующее шифрование) в Typescript. В этом примере вход «test» зашифрован как «ryhkw3BmJ85 + qBr0E9bYqw ==» в AS3, но расшифровка 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>
Node.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, но с удовольствием использовал бы Crypto JS или Forge, если любой из них сработает (у меня тоже не получилось использовать их для этой задачи).