AES在Node中加密。js在PHP中解密。失败。
在node.js中,我使用内置函数加密数据,如下所示:AES在Node中加密。js在PHP中解密。失败。,php,encryption,node.js,Php,Encryption,Node.js,在node.js中,我使用内置函数加密数据,如下所示: 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 encryp
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;
输出(加密文本)为:onninwxf6u8xmlgkj48ia==
然后我在PHP中使用decrypt:
$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的输出非常不同
$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中的内容:
$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中加密的base 64是:80022AGM4/4qQtiGU5oJDQ==
nodejs中的加密基64是:eoyrm5sck7epe847cwkfq==
因此,我无法在PHP中解密nodejs
我想知道这是否是因为nodejs不需要$iv AES是rijndael,固定大小为16字节IV。详细信息。
无法用于解密。
更重要的是,我也无法使用openssl解密您的字符串:
% openssl aes-256-cbc -d -in dec.txt -a
enter aes-256-cbc decryption password:
bad magic number
或使用php:
$encrypted = 'OnNINwXf6U8XmlgKJj48iA==';
$text = 'Yes';
$pw = '123456';
$decrypted = @openssl_decrypt($encrypted, 'aes-256-cbc', $pw);
var_dump($decrypted);
var_dump(@openssl_encrypt($text, 'aes-256-cbc', $pw, FALSE, $pw));
var_dump(@openssl_encrypt($text, 'aes-256-cbc', $pw));
输出:
bool(false)
string(24) "xrYdu2UyJfxhhEHAKWv30g=="
string(24) "findrYaZVpZWVhEgOEVQwQ=="
因此,node.js似乎正在使用一些未记录的功能来创建IV,我看不到在node.js中提供IV的方法。我只是开始在node.js上胡闹,但我认为您的问题与不匹配的IV有关。请尝试执行以下操作:
var encrypt = crypto.createCipheriv('aes-256-cbc', password, /* password.createHash('md5').toHex()*/);
PS:我不知道如何在node.js中创建MD5哈希,您必须相应地修改上面的代码
在PHP中:
$decrypt = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, md5($key)), "\0");
这应该确保两个实现使用相同的初始化向量
我还建议您进行以下更改:
- 密码:md5(原始密码)
- iv=md5(md5(原始密码))
这将确保PHP不会抛出任何愚蠢的错误。见晚了七个月,但我也在努力解决这个问题,并找到了解决办法。显然,PHP用零字节填充输入,使其大小为块大小的倍数。例如,使用AES-128,14字节的输入“contrabassists”将填充两个零字节,如下所示:
"contrabassists\0\0"
一个N*块大小的字节输入被单独保留
但是,标准节点加密函数使用一种称为PKCS5的不同填充方案。PKCS5不添加零,但添加了填充的长度,因此再次使用AES-128,“低音提琴手”将变成:
"contrabassists\2\2"
甚至N*块大小的字节输入也会在PKCS5中填充。否则,解码后不可能删除填充。输入的“光谱日光图”将变成:
"spectroheliogram\16\16\16\16\16\16\16\16\16\16\16\16\16\16\16\16"
要使PHP m_crypt加密与节点解密兼容,您必须自己填充输入:
$pad = $blocksize - (strlen($input) % $blocksize);
$input = $input . str_repeat(chr($pad), $pad);
另一种方法是,您必须读取解码数据的最后一个字节,并自己切断填充
功能示例:(2012年1月14日增补)
在PHP中,此函数将返回AES-128加密的十六进制编码数据,该数据可由节点解密:
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));
}
在节点中,以下操作将解密数据:
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('')
}
我还没有做相反的事情,但是一旦你理解了填充方案,它应该很简单。关于密钥/iv生成,我没有做任何假设。Node.js正在使用您的输入密码来派生密钥和iv。除非PHP使用完全相同的密钥和iv派生魔法,否则很难看出这在PHP中是如何工作的 你为什么不改用createCipheriv呢。使用基于密码的密钥派生函数从密码创建密钥。例如: 这种功能在Node.js的更高版本中可用 提供良好的静脉注射;您可以使用crypto.randomBytes创建一个。如果您控制key和iv参数,那么您将更容易确定是否可以将数据往返到PHP 你不能仅仅通过散列密码来生成一个iv。对于每个加密的消息,iv应该是不同的,否则它是无用的
另外,您告诉Node.js您的输入字符串“Yes”是Base64编码的,但我认为它实际上是ASCII或UTF-8。如果您使用的第三方库使用的是MCRYPT_RIJNDAEL_256,请知道256指定的是块大小,而不是键大小。AES使用128位的固定块大小,openssl不实现更通用的Rijndael算法。为了避免这种情况,我发布了一个与PHP一样绑定到
libmcrypt
的模块。这是一个非常有限的用例,但它确保它将与256位块大小的rijndael兼容
如果你在PHP中使用这个
mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_ECB);
mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $ciphertext, MCRYPT_MODE_ECB);
您可以在节点中执行相同的操作:
var rijndael = require('node-rijndael');
// straight through (must be buffers)
rijndael.encrypt(plaintext, key);
rijndael.decrypt(ciphertext, key);
// or bound (can take a string for the key and an encoding)
var rijn = rijndael(key);
rijn.encrypt(plaintext); // gotta be a buffer again for implementation simplicity
rijn.decrypt(ciphertext);
如果对其他人有帮助,我还有另一个工作示例 如果您确保在PHP和Node中使用长度为32个字符的“key/secret”和长度为16个字符的IV,并在Node中使用
base64
加密编码和utf8
消息编码,则填充模式中不应存在任何差异问题
问候,,
Ignacio我发现了两件事,这可能就是为什么PHP和Node.js上的解密/加密不同的原因 PHP使用了MCRYPT_RIJNDAEL_256算法。AES 256基于MCRYPT_RIJNDAEL_256,但不同。AES 256这是加密标准,但不是算法 如果您试图使用标准的简单函数(例如PHP上的“mcrypt_encrypt”和“mcrypt_decrypt”)来加密某些内容,那么您无法看到所有步骤,也无法知道为什么无法解密您加密的内容。Node.js也可以这样,因为需要使用可以逐步加密的函数,以防止替换默认参数 要加密/解密您需要知道的内容(设置): 并在两侧进行检查。应该是一样的。 还需要找到正确的组合“加密”
encryption method (algorythm)
encryption mode (CBF, ECB, CBC...)
key to decryption
key lenght
initialisation vector lenght
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;
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});
<?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);