对于对称加密,首先生成字节流形式的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的相关知识请自行谷哥.
参考的文章: