起因
創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供融水網(wǎng)站建設(shè)、融水做網(wǎng)站、融水網(wǎng)站設(shè)計、融水網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、融水企業(yè)網(wǎng)站模板建站服務(wù),十多年融水做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
最近遇到一個問題,把某個字符串計算MD5,之后把該字符串加密與MD5一起上傳到服務(wù)端,服務(wù)端解密后重新計算md5發(fā)現(xiàn)與上傳的MD5不一致,而出問題的字符串中無一例外都有Emoji表情。但我自己弄個帶表情的字符串上傳卻沒有什么問題。
最終確認這是在Android 5.1以下 jstring -> char數(shù)組 時出的問題。下面通過一個示例來還原這個過程。
事件還原
假設(shè)有一個字符串s,String s = "\uD83D\uDC8B";
,對應(yīng)表情💋
。通過調(diào)用getBytes()
方法,會看到對應(yīng)的byte數(shù)組為[-16, -97, -110, -117]
,按16進制輸出為[f0, 9f, 92, 8b]
。
定義一個參數(shù)為String的native方法,public native String test(String str);
,在對應(yīng)的C/C++代碼中,通過env->GetStringUTFChars
獲取傳入的String對應(yīng)的char數(shù)組,把char數(shù)組的每一個元素按16進制輸出。
在Android 7.1.2的測試機上,native層輸出的結(jié)果為[f0, 9f, 92, 8b]
,與Java的byte數(shù)組是一樣的,但是在Android 4.4.4的測試機上,輸出結(jié)果為[ed, a0, bd, ed, b2, 8b]
。從而導(dǎo)致加密后的結(jié)果不一樣。
服務(wù)端收到舊版Android的數(shù)據(jù)解密后得到[ed, a0, bd, ed, b2, 8b]
,計算MD5自然無法與[f0, 9f, 92, 8b]
計算MD5一樣。
Unicode、UTF-8、UTF-16
可能有人不是很清楚上面那2種byte數(shù)組是怎么來的。首先我們要知道,UTF-8和UTF-16都是Unicode的實現(xiàn)。\uD83D\uDC8B
其實是UTF-16大端的表現(xiàn)形式,對于大于0xFFFF(0x10000~0x10FFFF)的Unicode,轉(zhuǎn)換為UTF-16的步驟如下:
按照這個步驟反推:
所以,表情💋對應(yīng)的Unicode為0x1F48B。
UTF-8的規(guī)則是,對于占N個字節(jié)的符號(N>1),第一個字節(jié)前N位都是1,N+1位是0,后面的字節(jié)前2位為10,然后把Unicode的二進制位填入空缺的二進制位中,空出的位置補0。因此,上面的Unicode 0x1F48B轉(zhuǎn)為UTF-8需要占4個字節(jié),為:
11110 000
10 011111
10 010010
10 001011
即0xF09F928B,這也就是[f0, 9f, 92, 8b]這個byte數(shù)組的由來。
那么[ed, a0, bd, ed, b2, 8b]這個byte數(shù)組又是怎么來的呢?這是把\uD83D\uDC8B當成2個單獨的字符處理了,按照上面Unicode轉(zhuǎn)UTF-8的邏輯,Unicode 0xD83D轉(zhuǎn)為UTF-8為1110 1101 10 100000 10 111101,即0xEDA0BD,Unicode 0xDC8B轉(zhuǎn)為UTF-8為1110 1101 10 110010 10 001011,即0xEDB28B。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對創(chuàng)新互聯(lián)的支持。