首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AES在Node.js中加密,在PHP中解密。失败。

AES在Node.js中加密,在PHP中解密。失败。
EN

Stack Overflow用户
提问于 2011-05-18 00:23:12
回答 5查看 13.9K关注 0票数 12

在node.js中,我使用build函数加密数据,如下所示:

代码语言:javascript
复制
var text = "Yes";
var password = "123456";
var encrypt = crypto.createCipher('aes-256-cbc', password);
var encryptOutput1 = encrypt.update(text, 'base64', 'base64');
var encryptOutput2 = encrypt.final('base64');
var encryptedText = encryptOutput1 + encryptOutput2;

输出(加密文本)是: OnNINwXf6U8XmlgKJj48iA==

然后在PHP中使用解密:

代码语言:javascript
复制
$encrypted = 'OnNINwXf6U8XmlgKJj48iA==';
(or $encrypted = base64_decode('OnNINwXf6U8XmlgKJj48iA==')  );
$dtext2 = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC);
echo "Decrypted: $dtext2";

我会得到一些有趣的人物,但我无法解密。我试过用/不带base64_decode或MCRYPT_RIJNDAEL_128。都失败了。

然后我检查PHP中的加密方式,它看起来与来自node.js的输出非常不同。

代码语言:javascript
复制
$text = "Yes";
    $key = "123456"; 


    $eText = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC);
    echo "Encrypted: $eText \n";
    echo "base64: " . base64_encode($eText) . " \n";

    $dtext1 = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $eText, MCRYPT_MODE_CBC);
    echo "Decrypted: $dtext1 \n\n";

它可以加密和解密。加密数据为: njCE/fk3pLD1/JfiQuyVa6w5H+Qb/utBIT3m7LAcetM=

这与node.js的输出非常不同,请告诉我如何在node.js & php之间进行加密和解密。谢谢。:)

@Mel这里是我在PHP中拥有的内容:

代码语言:javascript
复制
$text = "Yes";

$key = "32BytesLongKey560123456789ABCDEF"; 
$iv =  "sixteenbyteslong";

/* Open the cipher */
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

/* Intialize encryption */
mcrypt_generic_init($td, $key, $iv);

/* Encrypt data */
$eText = mcrypt_generic($td, $text);

echo "Encrypted Data: $eText \n";
echo "base64: " . base64_encode($eText) . " \n";

/* Terminate encryption handler */
mcrypt_generic_deinit($td);

/* Initialize encryption module for decryption */
mcrypt_generic_init($td, $key, $iv);

/* Decrypt encrypted string */
$dText = mdecrypt_generic($td, $eText);

/* Terminate decryption handle and close module */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

/* Show string */
echo trim($dText) . "\n";

然而,它仍然不起作用。

PHP中的加密基64是:80022AGM4/4qQtiGU5oJDQ==,nodejs中的加密基64是:EoYRm5SCK7EPe847CwkffQ==

因此,我无法在PHP中解密nodejs。

我想知道是不是因为nodejs不需要$iv?

EN

回答 5

Stack Overflow用户

发布于 2012-01-07 16:02:35

晚了七个月,但我也在苦苦挣扎,并找到了解决办法。显然,PHP用零字节填充输入,使其大小为块大小的倍数。例如,使用AES-128,14字节输入的“对比者”将被填充两个零字节,如下所示:

代码语言:javascript
复制
"contrabassists\0\0"

一个N*块大小的字节输入是单独的。

然而,标准的节点加密函数使用了另一种称为PKCS5的填充方案。PKCS5不添加零,而是添加填充的长度,因此再次使用AES-128,“对比者”将变成:

代码语言:javascript
复制
"contrabassists\2\2"

即使是N*块大小的字节输入也会在PKCS5中填充。否则,解码后不可能移除填充物。然后输入的“光谱图”将变成:

代码语言:javascript
复制
"spectroheliogram\16\16\16\16\16\16\16\16\16\16\16\16\16\16\16\16"

要使PHP加密与节点解密兼容,您必须自己填充输入:

代码语言:javascript
复制
$pad = $blocksize - (strlen($input) % $blocksize);
$input = $input . str_repeat(chr($pad), $pad);

相反,您必须读取解码数据的最后一个字节,并亲自切断填充。

示例函数:(添加01-14-2012)

在PHP中,此函数将返回AES-128加密的十六进制编码数据,这些数据可以由Node解密:

代码语言:javascript
复制
function nodeEncrypt($data, $key, $iv) {
    $blocksize = 16; // AES-128
    $pad = $blocksize - (strlen($data) % $blocksize);
    $data = $data . str_repeat(chr($pad), $pad);
    return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv));
}

在Node中,以下内容将解密数据:

代码语言:javascript
复制
function nodeDecrypt(data, key, iv) {
    var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    var chunks = []
    chunks.push(decipher.update(data.toString(),'hex','binary'))
    chunks.push(decipher.final('binary'))
    return chunks.join('')
}

我还没有做过相反的事情,但是一旦你理解了填充方案,它应该是简单明了的。我还没有对关键的第四代做出任何假设。

票数 19
EN

Stack Overflow用户

发布于 2015-01-28 00:21:08

如果对其他人有帮助的话,我还有另一个工作示例在另一个岗位上

如果确保在padding中使用32个字符长度"key/secret“和16个字符长度IV,并在Node中使用base64加密编码和utf8消息编码,则在填充模式中不会出现任何问题。

你好,伊格纳西奥

票数 2
EN

Stack Overflow用户

发布于 2015-02-11 13:29:26

我发现了几件事情,这可能是为什么在PHP和Node.js上解密/加密不一样的原因。

PHP使用MCRYPT_RIJNDAEL_256算法。AES 256是基于MCRYPT_RIJNDAEL_256的,但不是相同的。这是加密标准,但不是算法。

如果您试图通过使用standart简单函数(例如,PHP上的“mcrypt_encrypt”和"mcrypt_decrypt“)来加密某些东西,那么您不可能看到所有的步骤,并且您肯定不知道为什么不能解密您所加密的内容。这对于Node.js来说也是一样的,因为需要使用可以逐步加密的函数来防止对默认参数的替换。

要加密/解密一些您需要知道的内容(设置):

代码语言:javascript
复制
encryption method (algorythm)
encryption mode (CBF, ECB, CBC...)
key to decryption
key lenght
initialisation vector lenght

在两边检查一下。应该是一样的。也需要找到正确的组合“加密方法”+“加密模式”,这肯定对双方都有作用。

我的解决方案是RIJNDAEL_256 + ECB。您应该安装节点-rijndael,因为它确实使用了RIJNDAEL_256。如果没有-我的榜样是行不通的。

这里是加密的Node.js示例。

在某个文件夹中安装节点-rijndael,其中应该有两个.js文件。

r256.js -它是用于加密/解密的函数。我找到它了这里

代码语言:javascript
复制
var Rijndael = require('node-rijndael');

/**
 * Pad the string with the character such that the string length is a multiple
 * of the provided length.
 *
 * @param {string} string The input string.
 * @param {string} chr The character to pad with.
 * @param {number} length The base length to pad to.
 * @return {string} The padded string.
 */
function rpad(string, chr, length) {
  var extra = string.length % length;
  if (extra === 0)
    return string;

  var pad_length = length - extra;
  // doesn't need to be optimized because pad_length will never be large
  while (--pad_length >= 0) {
    string += chr;
  }
  return string;
}

/**
 * Remove all characters specified by the chr parameter from the end of the
 * string.
 *
 * @param {string} string The string to trim.
 * @param {string} chr The character to trim from the end of the string.
 * @return {string} The trimmed string.
 */
function rtrim(string, chr) {
  for (var i = string.length - 1; i >= 0; i--)
    if (string[i] !== chr)
      return string.slice(0, i + 1);

  return '';
}

/**
 * Encrypt the given plaintext with the base64 encoded key and initialization
 * vector.
 *
 * Null-pads the input plaintext. This means that if your input plaintext ends
 * with null characters, they will be lost in encryption.
 *
 * @param {string} plaintext The plain text for encryption.
 * @param {string} input_key Base64 encoded encryption key.
 * @param {string} input_iv Base64 encoded initialization vector.
 * @return {string} The base64 encoded cipher text.
 */
function encrypt(plaintext, input_key, input_iv) {
  var rijndael = new Rijndael(input_key, {
    mode: Rijndael.MCRYPT_MODE_ECB,
    encoding: 'base64',
    iv: input_iv
  });
console.log("Rijndael.blockSize", Rijndael.blockSize);
  var padded = rpad(plaintext, '\0', Rijndael.blockSize);

  return rijndael.encrypt(padded, 'binary', 'base64');
}

/**
 * Decrypt the given ciphertext with the base64 encoded key and initialization
 * vector.
 *
 * Reverses any null-padding on the original plaintext.
 *
 * @param {string} ciphertext The base64 encoded ciphered text to decode.
 * @param {string} input_key Base64 encoded encryption key.
 * @param {string} input_iv Base64 encoded initialization vector.
 * @param {string} The decrypted plain text.
 */
function decrypt(ciphertext, input_key, input_iv) {
  var rijndael = new Rijndael(input_key, {
    mode: Rijndael.MCRYPT_MODE_ECB,
    encoding: 'base64',
    iv: input_iv
  });
console.log('lol', rijndael.decrypt(ciphertext, 'base64', 'binary'));
  return rtrim(rijndael.decrypt(ciphertext, 'base64', 'binary'), '\0');
}

exports.decrypt = decrypt;
exports.encrypt = encrypt;

encrypt.js --这是加密的例子。

代码语言:javascript
复制
var crypto = require('crypto');

var key = new Buffer('theonetruesecretkeytorulethemall', 'utf-8').toString('base64'); //secret key to decrypt

var iv = crypto.randomBytes(32).toString('base64');

console.log({"key":key, "iv":iv});
var rijndael = require('./r256'); 
var plaintext = 'lalala'; //text to encrypt

var ciphertext = rijndael.encrypt(plaintext, key, iv);
console.log({"ciphertext":ciphertext});

这里是解密的例子。

代码语言:javascript
复制
<?php
echo "<PRE>";
$mcrypt_method = MCRYPT_RIJNDAEL_256;
$mcrypt_mode = MCRYPT_MODE_ECB;
$mcrypt_iv = '123456'; //needed only for encryption, but needed for mcrypt_generic_init, so for decryption doesn't matter what is IV, main reason it is IV can exist.
$mcrypt_key = 'theonetruesecretkeytorulethemall';
$data_to_decrypt = base64_decode('ztOS/MQgJyKJNFk073oyO8KklzNJxfEphu78ok6iRBU='); //node.js returns base64 encoded cipher text


$possible_methods = array_flip(mcrypt_list_algorithms());

if(empty($possible_methods[$mcrypt_method]))
{
    echo "method $mcrypt_method is impossible".PHP_EOL;
    exit();
}

$possible_modes = array_flip(mcrypt_list_modes());
if(empty($possible_modes[$mcrypt_mode]))
{
    echo "mode $mcrypt_mode is impossible".PHP_EOL;
    exit();
}

if(!@mcrypt_get_block_size($mcrypt_method, $mcrypt_mode))
{
    echo "method $mcrypt_method does not support mode $mcrypt_mode".PHP_EOL;
    exit();
}

$mcrypt = mcrypt_module_open($mcrypt_method,'', $mcrypt_mode, '');

$ivsize = mcrypt_enc_get_iv_size($mcrypt);

if($ivsize != strlen($mcrypt_iv))
{
    $mcrypt_iv = str_pad($mcrypt_iv, $ivsize, '#');
}

if($ivsize < strlen($mcrypt_iv))
{
    $mcrypt_iv=substr($mcrypt_iv,0,$ivsize);
}

$keysize = mcrypt_enc_get_key_size($mcrypt);
if($keysize != strlen($mcrypt_key))
{
    $mcrypt_key = str_pad($mcrypt_key, $keysize, '#');
}

if($keysize < strlen($mcrypt_key))
{
    $mcrypt_key=substr($mcrypt_key,0,$keysize);
}


$mcrypt_isblock = (int)mcrypt_enc_is_block_mode($mcrypt);
$mcrypt_blocksize = mcrypt_enc_get_block_size($mcrypt);
$mcrypt_method = mcrypt_enc_get_algorithms_name($mcrypt);
$mcrypt_mode = mcrypt_enc_get_modes_name($mcrypt);

echo "used method=$mcrypt_method  \nmode=$mcrypt_mode \niv=$mcrypt_iv \nkey=$mcrypt_key \nkey with blocksize=$mcrypt_blocksize \nisblock=$mcrypt_isblock".PHP_EOL;

if(mcrypt_generic_init($mcrypt,$mcrypt_key,$mcrypt_iv)< 0)
{
    echo "mcrypt_generic_init failed...".PHP_EOL;
    exit();
}


$result = mdecrypt_generic($mcrypt, $data_to_decrypt);

echo PHP_EOL."decryption result|".$result.'|';

mcrypt_generic_deinit($mcrypt);

我不知道为什么,但是Node.js忽略了IV (在我的例子中),所以密码总是一样的。PHP总是使用IV,它应该是严格的长度,所以PHP总是返回不同的密码。但是我尝试了另一种方法(用PHP加密,用Node.js解密),它起作用了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6038620

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档