PHP NODEJS AES 加密兼容问题

  偶尔需要用户AES进行数据加密,加密数据对于PHP的mcrypt拓展来说,很简单了,如果图省事不封装一行代码就能搞定了,解密同理。下面这四行代码就是,实现了对ABCDEFG加密为wQji/D1x7Le7n/UAetNW8w==


<?php
$key = "12345678901234567890123456789012";
$iv = "1234567890123456";
echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, "ABCDEFG", MCRYPT_MODE_CBC, $iv));
echo rtrim(mcrypt_decrypt(
                MCRYPT_RIJNDAEL_128, $key,
                base64_decode("wQji/D1x7Le7n/UAetNW8w=="),
                MCRYPT_MODE_CBC, $iv),"\0");

  然后后看看nodejs的实现,同样对ABCDEFG进行加密,得到的数据是17nWXByAszz4sR+fUn3L+g==,结果和PHP得到的数据不一样了,代码如下,简要看看还是有所不一样的,明显的不同就是aes-256-cbc与PHP的RIJNDAEL_128,两个数据长度不一致,但实际上是一致的PHP也同时使用的CBC模式,而PHP的128就是对应NODEJS的aes-256,原因不知道,估计PHP定义的问题,这里需要的密钥和向量长度都是一致的。


var crypto = require('crypto');
var key = '12345678901234567890123456789012';
var iv = '1234567890123456';
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
var crypt = cipher.update("ABCDEFG", 'utf8', 'base64');
    crypt += cipher.final("base64");
    console.log(crypt);

  问题来了,然后怎么解决的问题。PHP代码中有一段为rtrim(str,”\0″),这个是什么意思呢,就是将字符串结尾的\0字符替换掉。问题就是来自这里,为什么末尾会多出空白符呢。这就和其对应的填充模式有关了,而在描述的加密算法中是没有对应的填充模式相关规定的,各种语言的各个库都有自己默认实现。很显然,PHP使用的是填充0字符,就是ZeroPadding,对于PHP还真是有个性!相关填充模式的介绍可以由 https://zh.wikipedia.org/wiki/块密码的工作模式https://zh.wikipedia.org/wiki/填充_(密码学)

  AES最小的块长度为16,那么试着加密0123456789ABCDEF的结果为+rLCjPIFE6x/1fyhpyZOhw==,而NODEJS的结果为+rLCjPIFE6x/1fyhpyZOh9tRf2wN5a/xqB7Rbdjl6V4=,数据中前一段是一模一样的,而之前的完全不一样。

  NODEJS中使用的填充模式为PKCS模式,和在最后面填充零时不一样的。这时候处理有两种方法,一种是在PHP中实现PKCS的填充模式,一种是在NODEJS中实现零填充模式,很显然NODEJS填0比较容易,而PHP的rtrim也相对容易理解。修正后的NODEJS代码如下,计算的加密结果为:wQji/D1x7Le7n/UAetNW8w==


var crypto = require('crypto');
var key = '12345678901234567890123456789012';
var iv = '1234567890123456';
var zero_padding = function(str){
    var length = str.length;
    var padding = (16-length%16)%16;
    return str+"\0".repeat(padding);
};
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv).setAutoPadding(false);
var str = zero_padding("ABCDEFG");
var crypt = cipher.update(str, 'utf8', 'base64');
    crypt += cipher.final("base64");
    console.log(crypt);

  对应的NodeJS解密代码,反过来即可,相当简单,唯一需要处理的就是在结尾要替换\0导致的空白符,这种解密方式也就导致字符串末尾出现\0就没法正常解密了,但无所谓了。


var crypto = require('crypto');
var key = '12345678901234567890123456789012';
var iv = '1234567890123456';
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv).setAutoPadding(false);
var decode = decipher.update("wQji/D1x7Le7n/UAetNW8w==", 'base64', 'utf8');
    decode += decipher.final('utf8');
    console.log(decode.replace(/[\0]+$/, ''));

一条评论在“PHP NODEJS AES 加密兼容问题”

写下你最简单的想法