![](/img/trans.png)
[英]AES - Pycrypto encryption in python with Crypto decryption in node.js
[英]AES broken between Node.js and Actionscript (as3crypto)
我正在嘗試使AES-256加密能夠跨node.js和actionscript工作,但是我嘗試的每種方法都將導致死胡同。 下面是兩種不同的嘗試,但都失敗了(由於不同的原因)。
需要注意的重要一件事是,在兩種情況下,IV,Key和密文均完美匹配。
對不起代碼重復,但我認為最好只顯示我正在使用的內容...
1) 默認填充
在as3中使用默認的Node.JS填充和PKCS5時,出現錯誤:PKCS#5:unpad:無效的填充值。 預期[105],發現[30]。
節點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);
}
使用“ HELLO WORLD!”時 對於輸入和相同的鍵,我得到
在Node.JS端輸出
IV:87134386f7bf12dffc9b87b49da86d10
密鑰:56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7
明文:48454c4c4f20574f524c4421
密文:d68db4542be683a80bceb0b8ca900d5c
在AS3側輸出
IV:87134386f7bf12dffc9b87b49da86d10
密鑰:56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7
密文:d68db4542be683a80bceb0b8ca900d5c
解密的純文本應為:48454c4c4f20574f524c4421
錯誤:PKCS#5:unpad:無效的填充值。 預期的[105],發現的[30]
2) 自定義和空填充
禁用默認的Node.JS填充並用空字符填充,然后在as3上使用NullPad時,我沒有收到任何錯誤,但解密失敗
節點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);
}
使用“ HELLO WORLD!”時 對於輸入和相同的鍵,我得到
在Node.JS端輸出
IV:cfa6cfee9f81d81d7e3b651e57b6f42d
密鑰:56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7
明文:48454c4c4f20574f524c4421
密文:8daf432aad551e333818c42d3190dca5
在AS3側輸出
IV:cfa6cfee9f81d81d7e3b651e57b6f42d
密鑰:56036ce4ddab006af7b0924ddad511adbea3fba97f672db4040102a1978e41f7
密文:8daf432aad551e333818c42d3190dca5
解密的純文本應為:48454c4c4f20574f524c4421
但實際上是: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("0123456789ABCDEF", 'utf8');
testRun(original, key);
console.log("");
//this test is with some padding
original = new Buffer("HELLO WORLD", 'utf8');
testRun(original, key);
好吧,我在node.js和Objective-C之間也有同樣的問題,但是在nodeJS代碼中,您應該使用crypto.CreateCypheriv
,而不僅僅是createCypher,也許不僅嘗試使用key和IV作為緩沖區,而且還要更改兩端的密鑰和IV到BYNARY有時會很麻煩...我希望這會給您一些解決問題的想法
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.