微信團隊提供了多種語言的示例代碼,但不包含Nodejs實現版本。通過大量查證和嘗試,我已完成並測試經過,下面說說實現要點。javascript
0.12.1
版或0.12.2
版,當前最新穩定版。key
應當經過Buffer
轉換爲binary字符串。String.fromCharCode
得到補位所用的字符,經過charCodeAt
判斷須要刪除的補位字符長度。Buf.writeUInt32BE
寫入,並轉換爲binary字符串;讀取時,使用Buf.readUInt32BE
。Buffer
轉換爲binary字符串。crypto.createCipheriv
,解密使用crypto.Decipheriv
;須設置cipher.setAutoPadding(auto_padding=false)
,不然不能正確加解密。binary
,輸出編碼爲base64
。base64
,輸出編碼爲utf8
。from_appid
時,需便宜行事:P密匙key
經過以下方式轉換:html
this.key = new Buffer(sEncodingAESKey + '=', 'base64').toString('binary');
加密部分代碼片斷:java
// 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'); }
對須要加密的明文進行填充補位算法:app
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版進行比對閱讀。dom