微信团队提供了多种语言的示例代码,但不包含Nodejs实现版本。经过大量查证和尝试,我已完成并测试通过,下面说说实现要点。
准备
- Nodejs为
0.12.1
版或0.12.2
版,当前最新稳定版。 - 平台支持Windows和Linux。
- 基于Python版本改写,通过Python的加解密验证及实际部署验证。
关键点
- 密匙
key
应当通过Buffer
转换为binary字符串。 - 通过
String.fromCharCode
获得补位所用的字符,通过charCodeAt
判断需要删除的补位字符长度。 - 设置明文长度时,应通过
Buf.writeUInt32BE
写入,并转换为binary字符串;读取时,使用Buf.readUInt32BE
。 - 加密时,XML原文需通过
Buffer
转换为binary字符串。 - 加密使用
crypto.createCipheriv
,解密使用crypto.Decipheriv
;须设置cipher.setAutoPadding(auto_padding=false)
,否则不能正确加解密。 - 加密时,输入编码为
binary
,输出编码为base64
。 - 解密时,输入编码为
base64
,输出编码为utf8
。 - 每个中文字符通过Buffer转换后,实际计算长度为3,因此最后分离
from_appid
时,需便宜行事:P
密匙key
通过如下方式转换:
this.key = new Buffer(sEncodingAESKey + '=', 'base64').toString('binary');
加密部分代码片段:
// 16位随机字符串添加到明文开头
// 使用自定义的填充方式对明文进行补位填充
var text = new Buffer(xml), // 一个中文长度为3
pad = this.enclen(text.length),
pack = PKCS7.encode(20 + text.length + appid.length),
random = crypto.randomBytes(8).toString('hex'),
content = random + pad + text.toString('binary') + appid + pack;
try {
var cipher = crypto.createCipheriv(this.mode, this.key, this.key.slice(0, 16));
cipher.setAutoPadding(auto_padding=false);
var crypted = cipher.update(content, 'binary', 'base64') + cipher.final('base64');
return [ierror.OK, crypted];
} catch (e) {
console.log(e.stack);
return [ierror.EncryptAES_Error, null];
}
解密部分代码片段:
var decipher, plain_text;
try {
decipher = crypto.Decipheriv(this.mode, this.key, this.key.slice(0, 16));
// 使用BASE64对密文进行解码,然后AES-CBC解密
decipher.setAutoPadding(auto_padding=false);
plain_text = decipher.update(text, 'base64', 'utf8') + decipher.final('utf8');
} catch (e) {
console.log(e.stack);
return [ierror.DecryptAES_Error, null];
}
var pad = plain_text.charCodeAt(plain_text.length - 1);
plain_text = plain_text.slice(20, -pad);
Pad计算方法enclen:
this.enclen = function (len) {
var buf = new Buffer(4);
buf.writeUInt32BE(len);
//console.log('enclen:', len, buf.toString('binary'));
return buf.toString('binary');
}
对需要加密的明文进行填充补位算法:
PKCS7.encode = function (text_length) {
// 计算需要填充的位数
var amount_to_pad = PKCS7.block_size - (text_length % PKCS7.block_size);
if (amount_to_pad === 0) {
amount_to_pad = PKCS7.block_size;
}
// 获得补位所用的字符
var pad = String.fromCharCode(amount_to_pad), s = [];
//console.log('pad:', amount_to_pad, pad);
for (var i=0; i<amount_to_pad; i++) s.push(pad);
return s.join('');
}
关键思路及代码如上,建议参考Python版进行比对阅读。
转载请注明出处:http://my.oschina.net/u/2324376/blog/397296
来源:oschina
链接:https://my.oschina.net/u/2324376/blog/397296