小程序encryptedData

準備知識:javascript

  1. Base64編解碼
  2. AES算法、填充模式、偏移向量
  3. session_key會話密鑰,以及怎麼存儲和獲取

以上3點對於理解解密流程很是重要。java

根據官方文檔,我梳理了大體的解密流程,以下:redis

  1. 小程序客戶端調用wx.login,回調裏面包含js_code。
  2. 而後將js_code發送到服務器A(開發者服務器),服務器A向微信服務器發起請求附帶js_code、appId、secretkey和grant_type參數,以換取用戶的openid和session_key(會話密鑰)。
  3. 服務器A拿到session_key後,生成一個隨機數咱們叫3rd_session,以3rdSessionId爲key,以session_key + openid爲value緩存到redis或memcached中;由於微信團隊不建議直接將session_key在網絡上傳輸,由開發者自行生成惟一鍵與session_key關聯。其做用是:
    1. 將3rdSessionId返回給客戶端,維護小程序登陸態。
    2. 經過3rdSessionId找到用戶session_key和openid。
  4. 客戶端拿到3rdSessionId後緩存到storage,
  5. 經過wx.getUserIinfo能夠獲取到用戶敏感數據encryptedData 。
  6. 客戶端將encryptedData、3rdSessionId和偏移量一塊兒發送到服務器A
  7. 服務器A根據3rdSessionId從緩存中獲取session_key
  8. 在服務器A使用AES解密encryptedData,從而實現用戶敏感數據解密

重點在六、七、8三個環節。
AES解密三個參數:算法

  • 密文 encryptedData
  • 密鑰 aesKey
  • 偏移向量 iv

服務端解密流程:json

  1. 密文和偏移向量由客戶端發送給服務端,對這兩個參數在服務端進行Base64_decode解編碼操做。
  2. 根據3rdSessionId從緩存中獲取session_key,對session_key進行Base64_decode能夠獲得aesKey,aes密鑰。
  3. 調用aes解密方法,算法爲 AES-128-CBC,數據採用PKCS#7填充。

下面結合小程序實例說明解密流程:小程序

  1. 微信登陸,獲取用戶信息api

    var that = this; wx.login({ success: function (res) { //微信js_code that.setData({wxcode: res.code}); //獲取用戶信息 wx.getUserInfo({ success: function (res) { //獲取用戶敏感數據密文和偏移向量 that.setData({encryptedData: res.encryptedData}) that.setData({iv: res.iv}) } }) } })
  2. 使用code換取3rdSessionIdxcode

    var httpclient = require('../../utils/httpclient.js') VAR that = this //httpclient.req(url, data, method, success, fail) httpclient.req( 'http://localhost:8090/wxappservice/api/v1/wx/getSession', { apiName: 'WX_CODE', code: this.data.wxcode }, 'GET', function(result){ var thirdSessionId = result.data.data.sessionId; that.setData({thirdSessionId: thirdSessionId}) //將thirdSessionId放入小程序緩存 wx.setStorageSync('thirdSessionId', thirdSessionId) }, function(result){ console.log(result) } );
  3. 發起解密請求緩存

    //httpclient.req(url, data, method, success, fail) httpclient.req( 'http://localhost:8090/wxappservice/api/v1/wx/decodeUserInfo', { apiName: 'WX_DECODE_USERINFO', encryptedData: this.data.encryptedData, iv: this.data.iv, sessionId: wx.getStorageSync('thirdSessionId') }, 'GET', function(result){ //解密後的數據 console.log(result.data) }, function(result){ console.log(result) } );
  4. 服務端解密實現(java)服務器

    /**  * 解密用戶敏感數據  * @param encryptedData 明文  * @param iv  加密算法的初始向量  * @param sessionId  會話ID  * @return  */ @Api(name = ApiConstant.WX_DECODE_USERINFO) @RequestMapping(value = "/api/v1/wx/decodeUserInfo", method = RequestMethod.GET, produces = "application/json") public Map<String,Object> decodeUserInfo(@RequestParam(required = true,value = "encryptedData")String encryptedData, @RequestParam(required = true,value = "iv")String iv, @RequestParam(required = true,value = "sessionId")String sessionId){ //從緩存中獲取session_key Object wxSessionObj = redisUtil.get(sessionId); if(null == wxSessionObj){ return rtnParam(40008, null); } String wxSessionStr = (String)wxSessionObj; String sessionKey = wxSessionStr.split("#")[0]; try { AES aes = new AES(); byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv)); if(null != resultByte && resultByte.length > 0){ String userInfo = new String(resultByte, "UTF-8"); return rtnParam(0, userInfo); } } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return rtnParam(50021, null); }
  5. AES解密核心代碼

    public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException { initialize(); try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); Key sKeySpec = new SecretKeySpec(keyByte, "AES"); cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化 byte[] result = cipher.doFinal(content); return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
相關文章
相關標籤/搜索