2013-09-24 43 views
1

我试图让AES-256加密工作跨越node.js和actionscript,但是我尝试的每种方法都是导致死亡。以下是两种不同的尝试,都是失败的(出于不同的原因)。AES在Node.js和Actionscript之间断开(as3crypto)

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

借口代码的重复,但我想它是更好地只显示正是我与......工作

1)默认填充

当使用缺省Node.js的填充和PKCS5在as3中,我得到一个错误:PKCS#5:unpad:无效的填充值。预计[105],找到[30]。

Node.js的

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

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

Key: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7

Plaintext: 48454c4c4f20574f524c4421

Ciphertext: d68db4542be683a80bceb0b8ca900d5c

在AS3侧输出

IV: 87134386f7bf12dffc9b87b49da86d10

Key: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7

Ciphertext: d68db4542be683a80bceb0b8ca900d5c

Decrypted Plaintext Should Be: 48454c4c4f20574f524c4421

Error: PKCS#5:unpad: Invalid padding value. expected [105], found [30]

2)自定义和空

输出Padding

当禁用默认Node.JS p添加和使用空字符填充,然后在使用AS3 NullPad,我没有得到任何错误,但解密失败

Node.js的

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

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

Key: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7

Plaintext: 48454c4c4f20574f524c4421

Ciphertext: 8daf432aad551e333818c42d3190dca5

输出上AS3侧

IV: cfa6cfee9f81d81d7e3b651e57b6f42d

Key: 56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7

Ciphertext: 8daf432aad551e333818c42d3190dca5

Decrypted Plaintext Should Be: 48454c4c4f20574f524c4421

But instead it is: 70a4716a7a7d7156bca075efe90041a3

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

任何方式使AES加密跨两个平台工作?


编辑:为子孙后代着想,这适用于加密和解密,有评论一些节点代码来说明潜在的陷阱:

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("ABCDEF", 'utf8'); 
testRun(original, key); 

console.log(""); 

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

回答

0

好了,我种具有相同node.js和Objective-C之间存在问题,但是在nodeJS代码中,您应该使用crypto.CreateCypheriv,而不仅仅是createCypher,也许不仅可以使用键和IV作为缓冲区,而且还可以将键和IV更改为在两端作为工作数组有时可能会很麻烦......我希望这给你一些想法来解决你的问题m

+0

LOL ... man ...在所有的测试和来回之后......我猜最后几轮我不小心让它createCipher而不是createCipheriv。使用Node.JS上的默认填充和as3中的PKCS5(上面的#1),完美地工作。 hehehe – davidkomer

+0

很高兴你想通了,你是什么意思默认填充?,也许这也是我的问题的答案太:)你介意分享你的最终nodeJS代码吗? – heczaco

+0

hey heczaco,上面就是这样,只是使用createCipheriv代替createCipher ...这是示例#1唯一的问题。 (我没有测试示例#2,因为现在没有必要) – davidkomer