基於Nodejs的微信消息加密與解密實現概要

微信團隊提供了多種語言的示例代碼,但不包含Nodejs實現版本。通過大量查證和嘗試,我已完成並測試經過,下面說說實現要點。javascript

準備

  • 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經過以下方式轉換: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

轉載請註明出處:http://my.oschina.net/u/2324376/blog/397296測試

相關文章
相關標籤/搜索