【问题标题】:Duplicate php openssl_encrypt in JavaScript在 JavaScript 中重复 php openssl_encrypt
【发布时间】:2018-01-24 07:04:12
【问题描述】:

我正在尝试使用 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"

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

【问题讨论】:

  • MTcyLDIsNjAsMTU5LDcxLDEwLDE4Myw4LDE… 解码为172,2,60,159,71,10,183,8,1…。看起来base64.Base64.encode() 编码了encryptedBytes字符串表示。如果你尝试console.log(encryptedBytes.toString('base64'));,输出是什么?
  • 只是一个想法:在 PHP 中,您明确使用 256 位密钥。使用 aesjs,密钥长度似乎由密钥变量的字节数/位数决定 - 在您的情况下,它是 16*8 = 128 位而不是 256 位。

标签: javascript php encryption aes


【解决方案1】:

有些事情出了问题:

首先,JavaScript 代码的输出实际上是字符串172,2,60,159,71,10,183,8,1,… 的base64 编码,而不是原始字节缓冲区的编码。我真的不知道如何习惯性地解决这个问题,但是通过使用aes.js hex-encoding 实用程序函数,我们可以将其转换为 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 = "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我个人更喜欢使用 CTR 模式,因为 PKCS#7 实现有时会暴露 padding oracles 从而破坏加密。 (我检查了提到的 pkcs#7 库应该不错,但是please don't try to implement this yourself。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-22
    • 2021-12-24
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 1970-01-01
    • 2019-06-16
    • 1970-01-01
    相关资源
    最近更新 更多