首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Node.js和Actionscript (as3crypto)之间的AES中断

Node.js和Actionscript (as3crypto)之间的AES中断
EN

Stack Overflow用户
提问于 2013-09-24 07:04:53
回答 1查看 1.1K关注 0票数 1

我正在努力使AES-256加密跨node.js和actionscript工作,但我尝试的每一种方法都会导致死胡同。下面是两次不同的尝试,但都失败了(原因不同)。

需要注意的一件事是,在这两种情况下-- IV、Key和密文是完美匹配的。

请原谅代码重复,但我觉得最好是展示一下我在做什么.

1) 默认填充

在使用默认的Node.JS填充和PKCS5在as3中时,我得到一个错误: PKCS#5:unpad:无效填充值。预期105,发现30。

Node.JS

代码语言:javascript
复制
var CIPHER_METHOD = "aes-256-cbc";

function aesEncryptStringToHex(input, key, iv) {
    var aesCipher = crypto.createCipher(CIPHER_METHOD, key, iv);
    var plainText = new Buffer(input, 'utf8').toString('hex');  
    var output;

    output = aesCipher.update(input, 'utf8', 'hex') + aesCipher.final('hex');

    console.log('IV: ' + iv.toString('hex'));
    console.log('Key: ' + key.toString('hex'));
    console.log('Plaintext: ' + plainText);
    console.log('Ciphertext: ' + output);
    sendToFlash(iv.toString('hex') + output);
}

AS3

代码语言:javascript
复制
private function aesDecryptToBytes(cipherBA:ByteArray, key:ByteArray):ByteArray {
    var IV:ByteArray = new ByteArray();
    var finalBytes:ByteArray = new ByteArray();
    var retBytes:ByteArray;
    var aesKey:AESKey;
    var cbcMode:CBCMode;
    var pad:PKCS5;
    var testOnly:ByteArray = new ByteArray();
    testOnly.writeUTFBytes('Hello World');

    if(key.length != 32) {
        throw new Error("INVALID KEY!");
    }


    if(cipherBA.length < 17) {
        throw new Error("INVALID CONTENT!");
    }
    cipherBA.readBytes(IV,0,16);
    cipherBA.readBytes(finalBytes, 0);

    IV.position = finalBytes.position = 0;

    trace('IV:', Hex.fromArray(IV));
    trace('Key:', Hex.fromArray(key));
    trace('Ciphertext:', Hex.fromArray(finalBytes));
    trace('Decrypted Plaintext Should Be:', Hex.fromArray(testOnly));

    pad = new PKCS5();
    aesKey = new AESKey(key);
    cbcMode = new CBCMode(aesKey,pad);
    cbcMode.IV = IV;
    pad.setBlockSize(cbcMode.getBlockSize());

    cbcMode.decrypt(finalBytes);

    retBytes = finalBytes;

    retBytes.position = 0;

    trace('But instead it is:', Hex.fromArray(retBytes));

    return(retBytes);
}

当使用“你好世界!”对于输入和两个输入的相同键,我将得到

Node.JS侧输出

IV: 87134386f7bf12dffc9b87b49da86d10 密钥: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7 明文: 48454c4c4f20574f524c4421 密文: d68db4542be683a80bceb0b8ca900d5c

AS3侧输出

IV: 87134386f7bf12dffc9b87b49da86d10 密钥: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7 密文: d68db4542be683a80bceb0b8ca900d5c 解密明文应为: 48454c4c4f20574f524c4421 错误: PKCS#5:unpad:无效的填充值。预期105,发现30

2) 自定义和零填充

当禁用默认的Node.JS填充并填充空字符,然后在as3上使用NullPad时,我不会收到任何错误,但解密失败。

Node.JS

代码语言:javascript
复制
var CIPHER_METHOD = "aes-256-cbc";
var AES_BLOCK_SIZE = 16;
var AES_PAD_STARTER = Array(16).join('\0');

function aesEncryptStringToHex(input, key, iv) {
    var aesCipher = crypto.createCipher(CIPHER_METHOD, key, iv);
    var plainText = new Buffer(input, 'utf8').toString('hex');
    var padLength = AES_BLOCK_SIZE - (input.length % AES_BLOCK_SIZE);
    var output;

    aesCipher.setAutoPadding(false);
    input += AES_PAD_STARTER.substr(0, padLength);

    output = aesCipher.update(input, 'utf8', 'hex') + aesCipher.final('hex');

    console.log('IV: ' + iv.toString('hex'));
    console.log('Key: ' + key.toString('hex'));
    console.log('Plaintext: ' + plainText);
    console.log('Ciphertext: ' + output);
    sendToFlash(iv.toString('hex') + output);
}

AS3

代码语言:javascript
复制
private function aesDecryptToBytes(cipherBA:ByteArray, key:ByteArray):ByteArray {
    var IV:ByteArray = new ByteArray();
    var finalBytes:ByteArray = new ByteArray();
    var retBytes:ByteArray;
    var aesKey:AESKey;
    var cbcMode:CBCMode;
    var pad:NullPad;
    var testOnly:ByteArray = new ByteArray();
    testOnly.writeUTFBytes("HELLO WORLD!");

    if(key.length != 32) {
        throw new Error("INVALID KEY!");
    }


    if(cipherBA.length < 17) {
        throw new Error("INVALID CONTENT!");
    }
    cipherBA.readBytes(IV,0,16);
    cipherBA.readBytes(finalBytes, 0);

    IV.position = finalBytes.position = 0;

    trace('IV:', Hex.fromArray(IV));
    trace('Key:', Hex.fromArray(key));
    trace('Ciphertext:', Hex.fromArray(finalBytes));
    trace('Decrypted Plaintext Should Be:', Hex.fromArray(testOnly));

    pad = new NullPad();
    aesKey = new AESKey(key);
    cbcMode = new CBCMode(aesKey,pad);
    cbcMode.IV = IV;
    pad.setBlockSize(cbcMode.getBlockSize());

    cbcMode.decrypt(finalBytes);

    retBytes = finalBytes;

    retBytes.position = 0;

    trace('But instead it is:', Hex.fromArray(retBytes));

    return(retBytes);
}

当使用“你好世界!”对于输入和两个输入的相同键,我将得到

Node.JS侧输出

IV: cfa6cfee9f81d81d7e3b651e57b6f42d 密钥: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7 明文: 48454c4c4f20574f524c4421 密文:8 daf432aad551e333818c42d3190dca 5

AS3侧输出

IV: cfa6cfee9f81d81d7e3b651e57b6f42d 密钥: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7 密文:8 daf432aad551e333818c42d3190dca 5 解密明文应为: 48454c4c4f20574f524c4421 但取而代之的是: 70a4716a7a7d7156bca075efe90041a3

注意,尝试retBytes.readUTFBytes(retBytes.length)也会产生垃圾。

使AES加密在两个平台上都能工作吗?!

编辑:为了后代起见,一些用于加密和解密的节点代码,并附有注释来说明潜在的问题:

代码语言:javascript
复制
const CIPHER_METHOD = "aes-256-cbc";
const AES_BLOCK_SIZE = 16;

let nullPad = new Buffer(AES_BLOCK_SIZE).fill(0);

function aesEncrypt(input, key, iv) {
    if(iv === undefined) {
      //create a random iv.. this is the norm for encryption
      iv = crypto.randomBytes(AES_BLOCK_SIZE);
    }
    let aesCipher = crypto.createCipheriv(CIPHER_METHOD, key, iv);
    let padLength = AES_BLOCK_SIZE - (input.length % AES_BLOCK_SIZE);

    //don't pad if it's an entire block's worth
    if(padLength === AES_BLOCK_SIZE) {
      padLength = 0;
    }

    //we're controlling the padding manually here so we can match it in other environments
    aesCipher.setAutoPadding(false);

    //for now, just a simple null pad. Need to add it before encryption
    //if it were pcks#7 or something, the length would not need to be returned for later use
    if(padLength > 0) {
      input = Buffer.concat([input, nullPad.slice(0, padLength)]);
    }

    //encrypt it
    let cipherText = Buffer.concat([aesCipher.update(input), aesCipher.final()])

    return {
      cipherText: cipherText,
      iv: iv,
      padLength: padLength,
    }
}

function aesDecrypt(cipherText, key, iv, padLength) {
    if(iv === undefined) {
      //strip the iv off the front
      iv = cipherText.slice(0,AES_BLOCK_SIZE);  
      cipherText = cipherText.slice(AES_BLOCK_SIZE);
    }

    let aesCipher = crypto.createDecipheriv(CIPHER_METHOD, key, iv);

    //turn off padding so we can match it in other environments
    aesCipher.setAutoPadding(false);

    //decrypt it
    let plaintext = Buffer.concat([aesCipher.update(cipherText), aesCipher.final()]);

    //for now, just a simple null padding. Need to strip it after decryption
    //if it were pcks#7 or something, the length would be auto-determined
    plaintext = plaintext.slice(0,plaintext.length - padLength);


    return plaintext;
}

function testRun(original, key) {
  //cipher is an object containing ciphertext, iv, and padLength
  let cipher = aesEncrypt(original, key);

  //treat the iv separately from the ciphertext. This is nice though hurlant doesn't support that afaik
  let decryptedSeparate = aesDecrypt(cipher.cipherText, key, cipher.iv, cipher.padLength);

  //combine the iv with the ciphertext directly. aesDecrypt will strip it automatically
  let combinedCipherIv = Buffer.concat([cipher.iv, cipher.cipherText]);
  let decryptedCombined = aesDecrypt(combinedCipherIv, key, undefined, cipher.padLength);

  //Show the results!
  console.log("Original: " + original.toString('utf8'));
  console.log("Encrypted: " + cipher.cipherText.toString('utf8'));
  console.log("Padding size: " + cipher.padLength);
  console.log("Plaintext from combined: " + decryptedCombined.toString('utf8'));
  console.log("Plaintext from separate: " + decryptedSeparate.toString('utf8'));
}

//key should be something more secure than whatever happens to be in memory at the moment ;)
let key = new Buffer(32);

//original is just binary data... doesn't have to be a string, though it's easier to see in the console for testing
//this test is for no padding
let original = new Buffer("0123456789ABCDEF", 'utf8');
testRun(original, key);

console.log("");

//this test is with some padding
original = new Buffer("HELLO WORLD", 'utf8');
testRun(original, key);
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-09-24 07:27:59

嗯,我在node.js和Objective之间也有同样的问题,但是在nodeJS代码中,您应该使用crypto.CreateCypheriv,而不仅仅是createCypher,也许不仅尝试使用key和IV作为缓冲区,还可以在两端将键和IV更改为二合一,因为处理数组有时会很麻烦.我希望这能给你一些解决问题的办法。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18975066

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档