前情回顧
通過wx.getUserInfo()的success回調得到的加密數據( encryptedData )
對加密數據( encryptedData )解密后可得到openId和unionId。
如何解密,官方文檔是這樣解釋的!
首次看到如上解密說明時,我只知道encryptedData和session_key獲得方式。
session_key在上篇有介紹,如下:
獲得session_key和openId(加解密、簽名系列)
產生的疑問:
1:AES是什么?
2:128是什么?
3:CBC是什么?
4:初始向量iv是做什么用的?
5:數據采用PKCS#7填充什么意思?
6:1,2,3,4條分別說明的算法,密文,密鑰,初始向量iv如何組合使用?
7:Base64_Decode又是什么?
1. AES是什么?
美國國家標准技術研究所在2001年發布了高級加密標准(AES)Advanced Encryption Standard。
AES是基於數據塊的加密方式,
即,每次處理的數據是一塊(16字節),當數據不是16字節的倍數時填充,
這就是所謂的分組密碼(區別於基於比特位的流密碼),16字節是分組長度。
AES是一個對稱分組密碼算法。
- 1
在這里簡單介紹下對稱加密算法與非對稱加密算法的區別。
對稱加密算法
加密和解密用到的密鑰是相同的,這種加密方式加密速度非常快,適合經常發送數據的場合。缺點是密鑰的傳輸比較麻煩。
非對稱加密算法
加密和解密用的密鑰是不同的,這種加密方式是用數學上的難解問題構造的,通常加密解密的速度比較慢,適合偶爾發送數據的場合。優點是密鑰傳輸方便。常見的非對稱加密算法為RSA、ECC和EIGamal。
實際中,一般是通過RSA加密AES的密鑰,傳輸到接收方,接收方解密得到AES密鑰,然后發送方和接收方用AES密鑰來通信。
AES加密函數
設AES加密函數為E,則 C = E(K, P),其中P為明文,K為密鑰,C為密文。也就是說,把明文P和密鑰K作為加密函數的參數輸入,則加密函數E會輸出密文C。
AES解密函數
設AES解密函數為D,則 P = D(K, C),其中C為密文,K為密鑰,P為明文。也就是說,把密文C和密鑰K作為解密函數的參數輸入,則解密函數會輸出明文P。
參數
-
明文P:沒有經過加密的數據。
-
密鑰K:用來加密明文的密碼,在對稱加密算法中,加密與解密的密鑰是相同的。密鑰為接收方與發送方協商產生,但不可以直接在網絡上傳輸,否則會導致密鑰泄漏,通常是通過非對稱加密算法加密密鑰,然后再通過網絡傳輸給對方,或者直接面對面商量密鑰。密鑰是絕對不可以泄漏的,否則會被攻擊者還原密文,竊取機密數據。
-
密文C:經加密函數處理后的數據
2. 128是什么?
AES為分組密碼,分組密碼也就是把明文分成一組一組的,每組長度相等,每次加密一組數據,直到加密完整個明文。在AES標准規范中,分組長度只能是128位,也就是說,每個分組為16個字節(每個字節8位)。密鑰的長度可以使用128位、192位或256位。密鑰的長度不同,推薦加密輪數也不同,如下表所示:
AES | 密鑰長度(32位比特字) | 分組長度(32位比特字) | 加密輪數 |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
微信小程序使用的是AES-128。
3. CBC是什么?
AES是基於數據塊的加密方式,也就是說,每次處理的數據是一塊(16字節),
當數據不是16字節的倍數時填充,這就是所謂的分組密碼(區別於基於比特位的流密碼),16字節是分組長度。
即,AES把看的見的信息(明文),分成很多相同組(明文塊),一般為128位(16字節)。
對每組進行單獨加密,然后再把各加密塊拼接成一條密文。
分組加密的幾種方式
-
ECB:是一種基礎的加密方式,密文被分割成分組長度相等的塊(不足補齊),然后單獨一個個加密,一個個輸出組成密文。
-
CBC:是一種循環模式,前一個分組的密文和當前分組的明文異或操作后再加密,這樣做的目的是增強破解難度。
-
CFB/OFB實際上是一種反饋模式,目的也是增強破解的難度。
ECB模式(基礎加密)
處理方式:
密文快[0…n] = 加密算法(明文塊[0…n],密鑰)
特點:
1:相同的輸入產生相同的輸出。
2:不能隱藏明文的模式,可能對明文進行主動攻擊;
AES默認的,最簡單,但安全性不夠,所以微信用了改良版CBC。
CBC模式(小程序采用)
處理方式:
密文快[0] = 加密算法(初始向量IV,明文塊0,密鑰)
其他密文塊[1…n]=加密算法(之前的密文塊,明文快,密鑰)
這個模式是鏈式的,后一塊需要前一塊做基礎,第一塊需要一個需要初始化向量IV做基礎。
相同的輸入產生不同的輸出。
能看到的數據是“明文+IV”或“明文+前一個密文”的亂碼,所以能隱藏明文。
總結:
安全性比第一種好,所以微信小程序用AES-CBC模式,所以需要IV向量。 密文 =AES(明文、密鑰、初始向量參數) 明文=AES(密文、密鑰、初始向量參數)
- 1
- 2
- 3
- 4
4. PKCS#7填充是什么
因為AES的算法是把明文分組再處理的,他要求每個分組(16字節)是“滿”的,即明文長度必須被16字節整除。
所以明文最后不足的16字節的要先進行數據填充,把不足16字節的最后一組補成16字節。
所以可知:明文先填充,再AES加密。
例如:明文171字節,最后一節為11個字節,需要填充5個字節(16-11)
上邊是填充的原理,具體來說,填充方式有很多,PKCS#7是其中一種。
PKCS #7 字符串由一個字節序列組成,每個字節填充該字節序列的長度。
下面的示例演示這些模式的工作原理。
假定塊長度為 8,數據長度為 9,則填充用八位字節數等於 7,
數據:
FF FF FF FF FF FF FF FF FF
PKCS7 填充:
FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07
5. Base64_Decode
Base64是網絡上最常見的用於字節代碼的編碼方式之一(一個字母就是一字節byte)
采用Base64編碼具有不可讀性,即所編碼的數據不會被人用肉眼所直接看到。
Base64編碼非常適合HTTP環境下傳遞較長的標識信息(傳輸8Bit字節代碼)
其他應用程序中,也常常需要把二進制數據編碼為適合放在URL中的形式
其實迅雷的“專用地址”也是用Base64”加密”的,其過程如下:
1、在http://地址的前后分別添加AA和ZZ
2、對新的字符串進行Base64編碼
把迅雷地址還原為http地址,只需要用Base64解碼,然后去掉頭尾的AA和ZZ即可。
迅雷地址Base編碼案例,詳見此文:
http://www.wxappclub.com/topic/711
微信服務器的操作是:
Base64_Encode(目標密文)=encryptedData(wx.getUserInfo得到的)
Base64_Encode(AES密鑰)=session_key
Base64_Encode(初始向量)=iv
所以:
目標密文和密鑰aeskey要用Base64解密:
即目標密文=Base64_Decode(encryptedData)
即密鑰aeskey=Base64_Decode(session_key)
即初始向量=Base64_Decode(iv)
注意:通過如下官方提供的代碼demo可知,iv也進行了Base64的解碼。
文檔上並未說明
6. 理解官方文檔
通過上邊的分析,我們知道:
微信小程序用的AES加密算法、AES-128的方案、CBC的分組加密模式(此模式需要IV初始化向量)
AES加密敏感數據之前,先用 PKCS#7 填充“用戶敏感數據”最后不足16字節的部分。
AES對密文解密后,需用 PKCS#7 去除填充才能得到真正“用戶敏感數據”
知道:
openId,union等敏感數據 = AES-128-CBC(密文,密鑰,初始向量iv)
第1條:描述的是加密算法和數據填充方式
第2條:描述的是如何得到密文(目標密文=Base64_Decode(encryptedData))
第3條:描述的是如何得到密鑰(密鑰aeskey=Base64_Decode(session_key))
第4條:描述的是如何得到初始向量iv
上述涉及的數據:
encryptedData(來自第2條)
通過wx.getUserInfo()的success回調得到的
iv(來自第4條)
通過wx.getUserInfo()的success回調得到的
session_key (來自第3條)
1:通過wx.login()的success回調得到的js_code
2:通過js_code、appid、secret得到session_key
7. 加密解密的全過程
微信服務器:加密
1:對敏感用戶信息“目標明文”用psck#7號填充得到“填充文”
2:AES-128-CBC(填充文,密鑰,初始向量)=>目標密文
3:Base64_Encode(目標密文)=>encryptedData
4:Base64_Encode(初始向量)=>iv
5:Base64_Encode(密鑰)=session_key
后台服務器:解密
1:通過wx.getUserInfo()獲得密文crypteddata,iv
2:通過wx.login()得到的js_code和http接口得到session_key(詳情請看)
3:Base64_Decode(encryptedData)=>目標密文
4:Base64_Decode(session_key)=>即密鑰aeskey
5:Base64_Decode(iv)=>初始向量
6:AES-128-CBC(目標密文,密鑰,初始向量)=>填充文
7:用psck#7對填充文去除填充得到敏感的用戶信息“目標明文”