2017-08-16 85 views
2

我想重複使用JavaScript的PHP字符串加密。這裏是PHP代碼:在JavaScript中重複PHP openssl_encrypt

<?php 

$iv = "123456789"; 
$key = "aaaaaaaaaaaaaaaa"; 
$input = "texttexttexttext"; 

$encrypted = openssl_encrypt($input, "AES-256-CBC", $key, 0, $iv); 

echo $encrypted; 
// "ZwY1i+vqP3acszeDiscCTx/R4a6d2AtkcInmN9OTCNE=" 

然而,當我試圖複製它在JavaScript中,它提供了不同的密文:

var aesjs = require("aes-js"); 
var base64 = require("js-base64"); 

var iv = aesjs.utils.utf8.toBytes("123456789"); 
var key = aesjs.utils.utf8.toBytes("aaaaaaaaaaaaaaaa"); 
var text = aesjs.utils.utf8.toBytes("texttexttexttext"); 

var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv); 
var encryptedBytes = aesCbc.encrypt(text); 

var b64encoded = base64.Base64.encode(encryptedBytes); 

console.log(b64encoded); 
// "MTcyLDIsNjAsMTU5LDcxLDEwLDE4Myw4LDE…wyMTIsMjIyLDk3LDEyNCw1MywxNzIsMjIy" 

我對如何使它產生相同的輸出沒有任何線索。有任何想法嗎?

+0

'MTcyLDIsNjAsMTU5LDcxLDEwLDE4Myw4LDE ...'解碼爲'172,2,60,159,71,10,183,8,1 ...'。它看起來像'base64.Base64.encode()'編碼'encryptedBytes'的*字符串表示*。 如果你嘗試'console.log(encryptedBytes.toString('base64'));'? – dsprenkels

+0

只是一個想法:在PHP中,您明確使用了一個256位密鑰。使用aesjs時,密鑰長度似乎取決於密鑰變量的字節數/位數 - 在您的情況下,它是16 * 8 = 128位而不是256位。 – gus27

回答

5

有些事情會出錯:

首先,從JavaScript代碼的輸出實際上是字符串172,2,60,159,71,10,183,8,1,…的base64編碼,原始字節緩存的不編碼。我真的不知道該怎麼慣用解決這個問題,但通過使用aes.js十六進制編碼實用功能,我們可以將它轉換爲base64:

var hex = aesjs.utils.hex.fromBytes(encryptedBytes); 
var buf = Buffer.from(hex, 'hex'); 

console.log(buf.toString('base64')); 
// rAI8n0cKtwiu1N5hfDWs3g== 

第二問題是,在aes.js您正在使用AES128加密(aaaaaaaaaaaaaaaa是128位長),但是您在PHP代碼中使用AES256加密。我們應該更新PHP代碼(或JS代碼):

$encrypted = openssl_encrypt($input, "AES-128-CBC", $key, 0, $iv); 
echo $encrypted; 
// rAI8n0cKtwiu1N5hfDWs3rPbz0UmvlbW+LJliYox03c= 

我們幾乎具有相同的輸出。但是等一下,PHP輸出的時間是兩倍。發生了什麼?

那麼,OpenSSL uses PKCS#7 padding。但是,Javascript代碼是未打包的。要解決這個問題,你應該爲JavaScript文本使用PKCS#7填充。爲此,您可以使用pkcs7模塊。另一種選擇是在計數器(CTR)模式下使用AES而不是CBC模式,如果這是您的選擇。

這是PHP代碼,我到底有:

<?php 
$iv = "123456789"; 
$key = "aaaaaaaaaaaaaaaa"; 
$input = "texttexttexttext"; 
$encrypted = openssl_encrypt($input, "AES-128-CBC", $key, 0, $iv); 
echo $encrypted; 
// output: 'rAI8n0cKtwiu1N5hfDWs3rPbz0UmvlbW+LJliYox03c=' 

這是JavaScript代碼:

var aesjs = require("aes-js"); 
var base64 = require("js-base64"); 
var pkcs7 = require("pkcs7"); 

var iv = aesjs.utils.utf8.toBytes("123456789"); 
var key = aesjs.utils.utf8.toBytes("aaaaaaaaaaaaaaaa"); 
var text = aesjs.utils.utf8.toBytes("texttexttexttext"); 

var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv); 
var encryptedBytes = aesCbc.encrypt(pkcs7.pad(text)); 

var hex = aesjs.utils.hex.fromBytes(encryptedBytes); 
var buf = Buffer.from(hex, 'hex'); 

console.log(buf.toString('base64')); 
// output: 'rAI8n0cKtwiu1N5hfDWs3rPbz0UmvlbW+LJliYox03c=' 

PS我個人比較喜歡使用CTR模式,因爲PKCS# 7個實現有時會暴露padding oracles,這會破壞加密。 (我檢查了提到的pkcs#7庫應該是好的,但是please don't try to implement this yourself。)