微信接入探祕(三)——加密消息的處理

本文出處:http://blog.csdn.net/chaijunkun/article/details/53435972,轉載請註明。由於本人不定期會整理相關博文,會對相應內容作出完善。因此強烈建議在原始出處查看此文

在上一篇博文中,介紹了被動回調接口的抽象數據層次,並實現了一個高效的實體與XML互轉的基礎功能。細心的開發者在初次修改公衆號的基本配置時會發現一個名爲“消息加解密方式”的選項。展開後可供選擇的值爲:“明文模式”(默認)、“兼容模式”和“安全模式(推薦)”。那麼這個安全模式又是什麼呢?如何適配安全模式呢?帶着這些問題,來進入微信接入的第三次探祕之旅。

本專欄代碼可在本人的CSDN代碼庫中下載,項目地址爲:https://code.csdn.net/chaijunkun/wechat-common

可惡的流量劫持

在文章的一開始,先給大家看兩幅處於正常使用中的手機屏幕截圖:

2016年9月北京聯通的流量劫持

在屏幕的右下角你會看到都有個綠色的水球顯示,這與要顯示的畫面格格不入,很明顯不是設計者爲之。點開水球,顯示的是當前通訊套餐中剩餘的流量詳情。

經過多方查證,它來自於北京聯通。運營商強制將自己的代碼注入到了當前顯示的頁面當中。也就是說,ISP在沒經過用戶同意的前提下,攔截了我們訪問的內容,經過篡改後再返回給我們。例子中僅僅是流量提醒,有一些小規模的ISP甚至過分到直接私自插入廣告。面對運營商級別的不法侵害,只有技術升級,採取一定的加密手段才能防止數據被非法篡改,其中HTTPS就是很好的方式。這也是爲什麼淘寶、京東等公司在全站實現HTTPS,

中間人監聽

從家庭局域網絡到Internet再到IDC機房內網,我們的數據鏈路充滿了轉發的過程。我們的數據在經過任何一臺設備的轉發時都有可能被其持久化保存起來。你的辦公內網一定安全嗎?潛伏在你身邊的黑客,通過設置局域網內某個網卡爲混雜模式(Promiscuous Mode)可以將局域網中的所有數據都接收一份。可能你並不能發現什麼,然而你的私有數據已經被別人竊取。

使用安全模式保護用戶隱私

微信的被動回調接口提供了一套基於消息加密的防範措施,來抵禦這些我們面臨的衆多安全威脅。這裏請注意,如果你提供的回調地址是https協議的,那麼開啓明文模式即可,因爲整個傳輸鏈路層對你來說已經是安全的了。

安全模式的回調數據格式

當啓用安全模式之後,被動回調接口接收到的消息就產生了很大的變化,形式如下所示:

<xml>
    <ToUserName><![CDATA[gh_38a2de904e09]]></ToUserName>
    <Encrypt><![CDATA[i7b8ccNA9OWDhau/F26aUWKFJ6Jd0imsDQIFPSdSfAg8mHT7rL0kIWSVpcqf6/dVSoOQOQK4T/CS3w96j4k3qcg89M6xn2RGZBs+9JkrsdRig5yhcia1B59akWb1t9QdutXqnl4edAqtXEh8SIs+N2HkOTTVldtOUHpdwLqRYuC4F6ejUoXui4xKuc3oyODR9edfL+xzZ7JfMJ1KUNF/YBJMj/Ba9y/CLLYmdFYOtCMH7tMUz8h+S0XKkHKN6r0ELLCIZJ9+PPlHZcfSGhwMLUeRF1nMIjXGEKHkI0uMcruh7wD96lMU/RFgJDjAk26xbmUYfa3l+34p+txw4R8iD3Q58S8Yekiy3lUsbk+C6eDeefGs1ck23BQ8xWU3AReWH2dEsY6SYIkb3ANeyJmcoIKZfpc/31njp0KcHAxL1Lk=]]></Encrypt>
</xml>

可以看出,明文部分保留了該消息的公衆號原始ID:gh_38a2de904e09,其餘部分均爲加密文本(這種結構可以使得同一個後臺系統,接入多個公衆號,再利用每個公衆號的設置進行分別解密)。由於加密結構在官方文檔(http://qydev.weixin.qq.com/wiki/index.php?title=加解密庫下載與返回碼)中並沒有明確的闡述,這裏就給大家詳細地說明一下。

Created with Raphaël 2.1.0文本密文文本密文字節密文字節密文字節數據字節數據16字節隨機數16字節隨機數4字節內容長度標識4字節內容長度標識明文數據明文數據appIdappIdBase64解碼AES解密提取提取得到明文長度n從長度標識後讀取n字節,UTF-8編碼記錄明文結束點m從明文結束點m讀到末尾,UTF-8編碼

如果讀者以前開發過網絡協議應該很容易理解這個結構,典型的不定長消息。一定會有一個標識來指明不定長區域的長度,讀取時以該值爲準,在定長數據之後讀取若干字節。在結構的開頭,有16字節隨機數,該部分數據無實際意義,可用於後續返回加密消息時作爲隨機數的填充數據。

安全模式的簽名驗證方法

微信的被動回調接口實際上是一個HTTP POST請求,我們都知道HTTP請求分爲請求頭和請求體。微信巧妙地將加密的XML數據放在了請求體,將驗證簽名時使用的參數放入了請求頭。

POST /cgi-bin/wxpush? msg_signature=477715d11cdb4164915debcba66cb864d751f3e6&timestamp=1409659813&nonce=1372623149 HTTP/1.1
Host: qy.weixin.qq.com
Content-Length: 613
<xml>
    <ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName>
    <Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt>
</xml>

簽名生成的過程

Created with Raphaël 2.1.0接入配置接入配置請求請求時間戳時間戳隨機字符隨機字符回調加密XML回調加密XMLTokenToken參數收集容器參數收集容器拼接字符串拼接字符串簽名簽名從頭部獲取從頭部獲取從請求體獲取讀取裝入裝入裝入裝入按字典序排序取SHA1摘要

通過上述計算,現在得出了當前加密消息的簽名calculated_sign,然後從請求頭中獲取參數msg_signature,如果calculated_sign和msg_signature相同,則說明消息沒有被篡改過。這種簽名策略利用的數學難題有:
1. SHA1摘要算法對全部數據進行了計算,有任何改動最終的摘要都會發生變化;
2. Token是事先設置好的,在傳輸過程中並不包含,無從破解;
3. 時間戳和隨機字符的引入,使得即便明文相同,每一次加密密文都有不同,給破解AES加密增加難度

簽名驗證成功之後就可以放心地使用解密後的明文了。明文的格式和之前文章中介紹的格式一致,因此解密後的業務處理邏輯可以複用。

作爲安全模式的應答,返回的XML格式也應該是加密的,整個加密過程與解密過程方向相反,這裏就不再贅述了。

2017年5月26日補充:微信的加密消息核心使用AES方式加密。在生產環境中,由於解密消息超過了JDK默認的安全限制值,解密時會拋出異常java.security.InvalidKeyException:illegal Key Size。解決該問題可以在官方網站下載JCE無限制權限策略文件:
a.如果使用的是JDK7:下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
b.如果使用的是JDK8:下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
下載後解壓,可以看到local_policy.jar和US_export_policy.jar以及readme.txt。
如果安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件,如果安裝了JDK,將兩個jar文件放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章