在JavaScript中复制php openssl_encrypt

时间:2017-08-16 08:15:15

标签: javascript php encryption aes

我正在尝试使用JavaScript复制PHP字符串加密。这是PHP代码:

<?php

$iv = "1234567890123456";
$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("1234567890123456");
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"

我不知道如何让它给出相同的输出。有什么想法吗?

1 个答案:

答案 0 :(得分: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代码是未填充的。要解决此问题,您应该使用PKCS#7填充作为javascript文本。为此,您只需使用pkcs7模块即可。另一种选择是在计数器(CTR)模式下使用AES而不是CBC模式,如果这是您的选择。

这是我最终的PHP代码:

<?php
$iv = "1234567890123456";
$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("1234567890123456");
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 我个人更喜欢使用点击率模式,因为PKCS#7实施有时会暴露padding oracles,从而破坏加密。 (我检查了提到的pkcs#7库,这应该是好的,但是please don't try to implement this yourself。)