AES-CB C шифруется в ActionScript, дешифруется в Javascript - PullRequest
1 голос
/ 18 марта 2020

У нас есть устаревший инструмент, написанный на 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, если любой из них сработает (у меня тоже не получилось использовать их для этой задачи).

...