在 node.js 中重新创建 MCRYPT_RIJNDAEL_128

Posted

技术标签:

【中文标题】在 node.js 中重新创建 MCRYPT_RIJNDAEL_128【英文标题】:Recreating MCRYPT_RIJNDAEL_128 in node.js 【发布时间】:2015-02-27 03:55:47 【问题描述】:

尝试在 node.js 中重新创建以下 php 加密代码:

$size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
$msg = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, 'MY_KEY_LONG_STRING', 'PLAINTEXT', MCRYPT_MODE_ECB, $iv));

试过这个:

var text = 'PLAINTEXT';
var len = text.length;
for (var i = 0; i < 16 - len % 16; i++)   // pad to multiple of block size 
    text += '\0';

var key = 'MY_KEY_LONG_STRING';
key = key.substr(0, 16); // trim to expected key size for aes128

var cipher = crypto.createCipher('aes-128-ecb', key);
cipher.setAutoPadding(false); // did our own padding, to match mcrypt_encrypt
var encrypted = cipher.update(text, 'utf8', 'base64');
encrypted += cipher.final('base64');

从 php 中得到不同的结果...

还尝试使用 IV 创建密码(甚至不应该在 aes-128-ecb 中使用):

var cipher = crypto.createCipheriv('aes-128-ecb', key, '');

另外,与 php.ini 的结果不同。任何想法如何使它的行为与 php 版本完全一样?

【问题讨论】:

ECB 不能使用 IV,所以可以压缩你的 php 代码:$msg = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, 'MY_KEY_LONG_STRING', 'PLAINTEXT', MCRYPT_MODE_ECB)); 【参考方案1】:

将两个构造不佳的库相互对抗会很有趣。与其做所有的工作,我会根据你的要求帮助你:

PHP 不是删除密钥字节,而是使用零填充将它们扩展为下一个可用的密钥大小 - 在您的情况下为 192 位或 24 字节;为此,您需要将aes-192-ecb 指定为算法(您需要继续使用MCRYPT_RIJNDAEL_128,但可以替代,PHP 中的 128 是块大小而不是密钥大小) 填充不正确,PHP 填充 0..15 个零字节而不是 1..16 个字节 不能使用第二个参数createCipher,因为第二个参数是密码;如果您使用 node.js 会执行密钥派生,因此您需要使用三个参数 createCipher 并提供任何 16 字节 IV

PHP 中的 IV 代码只是对随机数生成器不必要地征税,IV 没有被使用。


进行填充的代码

var padSize = 16 - ((len + 16 - 1) % 16 + 1);
for (var i = 0; i < padSize; i++)   // pad 0 .. 15 until to multiple of block size 
    text += '\0';

或者您可以使用自己的填充方法(在问题中)除非len % 16 == 0

【讨论】:

默认免责声明:ECB 不安全,在没有 MAC 的情况下传输数据更不安全(我也对使用 javascript 和 PHP 进行加密表示严重怀疑) 嗨 Maarten,感谢您的想法:1)当将密钥填充到 24 个字节并调用 crypto.createCipheriv 时,node.js 给出了错误 node-crypto : Invalid key length 24。不过,php 扩展到 24 字节的事实是有帮助的。 2)“PHP pads 0..15 ...”是什么意思? 3)PHP中的IV代码,基于cmets判断,是为了避免在早期版本的PHP中不提供IV参数时抛出的错误。 @PashaBitz 1) 检查我更改后的答案(在第一项上)以获取 24 字节密钥问题的解决方案。 2) PHP 用零填充,直到达到下一个块大小,但如果大小已经是块大小的 x 倍,它根本不填充。 3) 至于IV,我只给它一个初始化为全零的16 字节的字节数组。如果不使用,您可能需要提供 IV,但将其随机化是没有意义的。 键填充为 24 个字符,crypto.createCipheriv 仍然会抛出错误 node-crypto : Invalid key length 24。但是,将键修剪为 16 个字符后,确实会产生相同的结果,感谢您发现填充错误! 替换为 aes-192-ecb 确实会产生与 PHP 相同的输出,使用 24 字节密钥...不知何故我错过了您原始帖子中的评论。谢谢!!【参考方案2】:

我尝试了其他选项,但没有任何效果,但这对我有用。

function encrypt(text) 
  text = '' + text;
  var crypto = require('crypto');
  var len = text.length;
  var padSize = 16 - (((len + 16 - 1) % 16) + 1);
  var data = String.fromCharCode(padSize);
  var text = text + data.repeat(padSize);

  var cipher = crypto.createCipheriv('aes-128-ecb','secretKey', '');
  cipher.setAutoPadding(false);
  var encrypted = cipher.update(text, 'utf8', 'base64');
  encrypted += cipher.final('base64');
  return encrypted;

PHP 类似

function aes128Encrypt($str,$key)
$block = mcrypt_get_block_size('rijndael_128', 'ecb');
$pad = $block - (strlen($str) % $block);
$str .= str_repeat(chr($pad), $pad);
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB));

【讨论】:

你拯救了我的一天。我打算在 php 中创建一个 api 并从 nodejs 调用它。这节省了我的时间和精力,谢谢老兄 很高兴我能帮上忙 :)【参考方案3】:

这是我解决从 PHP 迁移到 NodeJS 问题的代码。我有严格的 32 字节密钥,所以我必须使用 aes-256-ecb

我要重写的 PHP 代码:

$text = "Some super mega text I want to encode";
$skey = "rcbTw667C7zxghZ5U3gwhQlp22t8c5Rq";

function encode($text,$skey) 
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $skey, $text, MCRYPT_MODE_ECB, $iv);
    return base64_encode($crypttext);


echo encode($text,$skey);

输出:

dU1eOy+YwkYvm/KCTB8aqR1UsisyrHrvBu+E/H3G/s0aagMDKlNFekGXNQyFMFJD

NodeJS:

var crypto = require('crypto');

var text = "Some super mega text I want encode";
var skey = "rcbTw667C7zxghZ5U3gwhQlp22t8c5Rq";

function encode(text, skey) 
    var len = text.length;
    var padSize = 16 - ((len + 16 - 1) % 16 + 1);
    for (var i = 0; i < padSize; i++)  
        text += '\0';
    
    var cipher = crypto.createCipheriv('aes-256-ecb', skey, '');
    cipher.setAutoPadding(false);
    var encrypted = cipher.update(text, 'utf8', 'base64');
    encrypted += cipher.final('base64');
    return encrypted;


console.log(encode(text, skey));

输出:

dU1eOy+YwkYvm/KCTB8aqR1UsisyrHrvBu+E/H3G/s0aagMDKlNFekGXNQyFMFJD

NodeJS mcrypt 包也有效:

npm install mcrypt

代码:

var text = "Some super mega text I want encode";
var skey = "rcbTw667C7zxghZ5U3gwhQlp22t8c5Rq";

function encode(text, skey) 
    var MCrypt = require('mcrypt').MCrypt;
    var rijEcb = new MCrypt('rijndael-128', 'ecb');
    rijEcb.open(skey);
    var ciphertext = rijEcb.encrypt(text);
    return ciphertext.toString('base64');


console.log(encode(text, skey));

输出:

dU1eOy+YwkYvm/KCTB8aqR1UsisyrHrvBu+E/H3G/s0aagMDKlNFekGXNQyFMFJD

【讨论】:

你有没有解码?

以上是关于在 node.js 中重新创建 MCRYPT_RIJNDAEL_128的主要内容,如果未能解决你的问题,请参考以下文章

Discord.js / Node.js FS - 在同一会话中重新加载文件?

Node.js:如何重新加载模块

node-1

Node.JS MySql 在循环中等待预期结果

为 node.js 重新加载。有可能吗? [复制]

如何在Node.js的回调中正确测试代码?