UTF8編碼字節流錯誤小析
對于對稱加密,首先生成字節流形式的key與iv,之后對指定字符串進行加密,然后將key與iv轉換為字符串傳輸到另一端,另一端再將字符串轉換回字節流完成解密工作.在key與iv的字節流與字符串之間的轉換中如果選用Utf8編碼,則可能會讓轉換回的二進制流與之前的字節流不一致.代碼如下:
byte[] bytes = new byte[]{146, 174, 27, 74, 223, 159, 52, 180}; string s = Encoding.UTF8.GetString(bytes); Console.WriteLine(s); byte[] bytes2 = Encoding.UTF8.GetBytes(s); Console.WriteLine(bytes2);
你會發現,bytes2的內容變為了:{239,191,189,239,191,189,27,74,223,159,52,239,191,189}.
從表面看,其將146, 174, 180三個數擴展成了 239,191,189.但毫無規律可尋,223比它們都大,保持了下來,27之類的比他們小,也保持了下來,甚至是159,在它們中間,也保存了下來.說雙數變吧,74是雙數,卻也保持了下來.后來經過不懈努力,終于大致弄懂原因了.
1.什么是字符編碼.
字符編碼,就是將計算機里記錄的0與1以一定的規則轉換成人類的字符,如英文或中文,是計算機表達字符的方式,其本質就是一個翻譯,.目前計算機有多種表式方式,比如ASCII之類的.不同的方式對字節基數要求不一樣,有的固定字節/字符的,如Ascii是一字節一字符,Gb2312是兩字節一字符,有的是不固定字節/字符的,如Utft8,就從一字節到四字節表示一個字符.不同的方式翻譯方式也不一樣,比如對于字節{84,128,26,144},如果使用Gb2312翻譯,就是"聯通"兩字,使用Utf8翻譯,就是亂碼"T?□?"
2.Utf8的特殊性
字節流的組合有無數種,但能被字符集識別的只是其中的一部分.就像人的嗓子可以發出很多種聲音,但只有26個聲母與5個韻母的組合才能發音成人類語言.Utf8也不例外.Utf8的編碼規則如下:
| Unicode編碼(16進制) | UTF-8 字節流(二進制) |
| 000000 – 00007F | 0xxxxxxx |
| 000080 – 0007FF | 110xxxxx 10xxxxxx |
| 000800 – 00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
| 010000 – 10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
可以看到
1.以0,110,1110,11110開頭的字節,可以被識別
2.如果以10開頭,則要求前一個字節必須以10,110,1110,11110開頭
3.如果以10開頭且前一個也以10開頭,則再前一個必須以10,1110,11110開頭
4.如果以10開頭且前一個以10開頭且再前一個也以10開頭,則再前一個必須以11110開頭
5.不能被識別的字節,每一個會轉換成三個字節{11101111,10111111,10111101},也就是239,191,189
3.解析問題
上面所提到的字節流所對應的二進制流如下:
| 146 | 10010010 |
| 174 | 10101110 |
| 27 | 00011011 |
| 74 | 01001010 |
| 223 | 11011111 |
| 159 | 10011111 |
| 52 | 00110100 |
| 180 | 10110100 |
可以看到,27,74,223,52滿足規則1,159滿足規則2,而146,174,180一個也不滿足,各個被轉換成了239,191,189.所以,最終的結果就是如上所示了.
使用Ascii,Utf7, Utf32編碼都會有不同程度的變樣,理由同Utf8.
4.正確的做法
雖然經過我的實驗,使用Unicode編碼可以完全還原,但將字節流轉換成字符串的正確做法是進行Base64編碼.不同與其它的語言編碼,這是專門用于字節流與節符串轉換的編碼.使用Convert類的ToBase64String方法與FromBase64String方法即可完成全部功能.Base64的相關知識請自行谷哥.
參考的文章:

浙公網安備 33010602011771號