Md5加密中为什么要 & 0xff
转载于:带着梦想飞翔
这里首先要搞明白一个道理(自己设想的)
* byte有8位,假如全部都是 11111111 那么对应的int(10进制)类型就是 255
* 虽然255(十进制)和11111111(二进制)都表示同一个数,但作为String类型存储255作为String类型存储长度为3,而11111111作为string类型存储长度为8
* 【关键点一】MessageDigest.getInstance(“MD5”).digest(“需要加密的字符”.getBytes()) 计算出来的结果返回的byte[]长度始终为16位。
* 所以说,真正的密文 是由java别人大神写的算法计算出来的。已经保证16位的固定长度的byte
* 【关键点二】要记住我们计算出来的md5(32位)最后都都是固定长度32的字符串
* 那么问题出现了:byte[16]这个数组要是直接转化为int类型在存储为字符。而byte的取值范围为-128 ~ 127.那么作为0-99转化为总长度为16的字符串没有问题,
* 但,100 ~ 128和-10 ~ -128这些数字转换为总长度为16的字符串就不一定了。
* 因为一个byte数可以是-10(转换为String长度为3)那么这样的16个byte总长度就变成了316=48,
\ 注意:-128 ~ 127一共有256个数字。
* 即:{1 ~ 127}有127个
* {-1 ~ -128}有128个
* 0 有 1个
* 得:127+128+1=256
* 那么就需要单独用一个字符位表示256个数字就需要用到256进制。
* 【常规一】十进制 一个字符位可以表示10个字符{0,1,2,3,4,5,6,7,8,9}
* 如果不想将md5生成的密文(byte & 0xff)二直接保存为16个定长度的字符。那么就需要256进制的数字来形容
* 【注意】应为负数转为字符串会占用2个长度,应为符号占用一个长度。所以最精简的方法就是用256进制表示,刚好可以满足byte的取值范围将其一一对应。
* 【总结一】直接将byte[16]转换为一个长度为16的字符需要【256】进制的数来做直接替换。
* 那么现在问题来了,字符串中单个字符就能代表到255的符号需要怎么来表示?
* 当然这是数学家的问题,我们只是苦逼的程序员。即使你想出来了,也没有权威,别人也不会用。
* 【常规二】java虚拟机有16精制的数。并且能用1个字符表示{0 ~ 15}即{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}。
* 对应的JAVA方法为Integer.toHexString(k),k为int类型的数。
* 那么一个16精制的正数最大为 f—->(十进制)15
* 两个16进制的正数最大为 ff—->(十进制)1516+15=255
\ 那么对于从0 到ff一个有多少个数为:
* 0—》1个
* 1 ~ ff–》256个
* 刚好和byte的取值范围{-128 ~ 127}共256个数字可以一一对应。
* 【总结二】如果用16进制的2位数来表示一个byte字节的值,在保证不出现负数的情况下刚好可以完全替换。
* 只不过对于0-f(15)这样的一个长度的字符串转换为字符串时。为了保证和两位数的16进制保持一致都占用两个长度的字符串,
* 需要将不足长度的在前面用0补齐。
* 【总结三】将byte[16]全部转换为正数的16进制字符串刚好是32位。
* 即Md5生成的byte[16]固定长度的规律可以得到固定转换为16精制的数表示为固定长度为32的字符串。
* 当然,你如果觉得用负数替换,保证固定长度。不足的位数用你自己定义的符号去替换当然可以使用.
* (解密也需要用你自己定义的符号去计算解密,挨个找出原来的byte[16]个字节)
* 只不过,在md5算出来的密文中,大家都约定束城了用什么样的字符去替换。
* 【总结四】那么在byte[16]转换为16进制的固定长度32的字符串。并不是为了什么,就是为了保证我们可以直观看到的密文都是固定长度的。
* 延续了在byte[16]这个固定长度的特点。提高了md5的加密的高大上。那么对于每一个byte[16]中的单个byte转换到对应16进制的的二位数
* 只需要保证都是唯一,一一对应的就行了(保证可逆)(md5算出来的密文byte[16]是不可逆的)。
* 那么 对于需要加密的密文 “xxxx”—–(不可逆)–>Md5(byte[16])<—–可逆—->16进制的32个长度的字符串。
* 就可以简化为 “xxxx”——(不可逆)—->16进制的32个长度的字符串。
* 想想上面这段推理大家都看得懂。
* 那么对于通用Md5加密后生产32个长度固定的字符串java逻辑为:
1 | /** |
* 在了解这么多后我们可以回归到之前的问题了。
* 【问题】为什么md5加密中需要将 byte[16] 的每一个数先 & 0xff后再计算呢?
* 当然,上面已经解释了,这样并不是为了保证两个数据相等,而是为了保正生成的Md5字符串为固定长度32,
* 并且可以将固定长度32的字符串每两个可逆为原有的byte[16]中的相应字节。至于转换后的相对应的实际值是否相当这个没有必要。
* 下面我们来看一组数据 你看了下面的一组数据就 明白了。
* 【发现规律】
* 这里我们可以发现 无论byte转int后再转16进制,还是byte & 0xff后再转16进制。在输出的数据中最后两位的结果都是保持一致的
* 而我们在用md5算法加密时,md5加密后的结果有32位和16位的 上面这种就是转为32位固定长度。
* 所有在md5(32)位 加密中需要将不足2位的前面补0
* 【规律一】其实如果将byte直接转16进制后将不足2位前面补0,比2位多的前面全部去掉。结果和byte & 0xff生成的32位密文是一样的。
* 其实还有一个规律:
* 对于byte类型 强制转换为int10(进制)类型后(无论正负)存储在int(38位)类型里面的最后8位和byte的8位是一样的
* 对于byte类型 强制转换为16进制后(无论正负)存储在最后的8位也是一样的
* 【规律二】byte转int10进制或者转16精制 最后的byte(8位)数字都没有变化
* 【原理推理证明】
* 对于计算机而言:一个byte在内存存储占用8个字节 即 00000000—->11111111
* 对于计算机而言可以用 {11111111—->01111111} 的二进制去完整的表示byte的取值范围 {-128 ~ 127}【具体计算规则请百度参考原码,反码,补码。去转10精制,这里不多介绍】
* 这也就是byte的取值范围为什么是{-128 ~ 127}的原因。
* 【发现规律】对于byte8位二进制的每一位用0和1的不同组合可以完全对应{-128 ~ 127}
* 而对于int类型来说(去除负数,完全用正数匹配)不考虑二精制转换后数字实际值是否一致的问题,让其一一对应,达到可以逆的目的。
* 那么int最小的数为:
* 计算机内部补码:
* 最小: 00000000 00000000 00000000 00000000 —》0
* 最大: 00000000 00000000 00000000 11111111 —》255(十进制)
* 而再java中进行 byte[i] & 0xff时有一个默认规律
* 先将byte[i]转换为int类型存储
* 在将0xff转换为int类型存储
* 再运算 (byte[i] & 0xff)
* 【知识点】0xff表示的是一个数,一个16进制的数,转换为十进制后为255
* 其实(byte[i] & 0xff)就是运算 (byte[i] & 255)两个得到的结果都是一样的
* 至于为什么要把0xff来替代255个人想法大概就是当初第一次用这个替换法的人觉得这样高大上把。。其实没什么不同。结果都一样。
* 那么假如 byte[i]内部存储的补码为:11010111
* 转为int存储后的补码为: 11111111 11111110 11111110 11010111
* 而0xff转为int存储的补码为: 00000000 00000000 00000000 11111111 —-》即255
* 那么做&运算结果为: 00000000 00000000 00000000 11010111
* 由于第一位是0 所有计算后的所有结果都为一个int类型的整数,不会出现负数。
* 那么这样就把byte[16]完全的用int类型的整数一一对应了。(不考虑前后的实际值,只需要一一对应就行。至于为什么具体原因见【总结四】)
* 即:{-128 ~ 127} 可以由{0-255}去一一对应。
* 由于需要将byte[16]转换为固定长度为32的string类型。我们直接用得到的int是不可能的。原因请参考【总结一】的推理
* 在文章前已经说明了,要想转换为固定长度32的字符串,可以用16进制的数做替换。这样就可以保证从{0-255} 一一对应 {0x00-0xff}(0x表示这个数为16进制)
* 那么这样就就出现了如下结果:
* {-128 ~ 127}<—-可逆—->{0 ~ 255}<— 可逆—>{0x00 ~ 0xff}<—可逆—>长度为32的16进制字符串
* 即:{-125 ~ 127}<—–可逆—->{0x00 ~ 0xff}<—可逆—>长度为32的16进制字符串
* 而md5生成的byte[16]每个可以 & 0xff结算结果得到唯一的一个16进制数,并且不会重复。
* 那么md5生成的byte[16]就可以客观的用String长度为32的十六进制字符串替代。并且都是唯一对应。
* 这样就显示可见,方便我们操作。
【重点】至于Md5(32)生成的密文为什么要有上面的java代码的逻辑生成字符串,其实就是一种将不可逆的byte[16]转换为唯一对应的字符串吧了。
而那种方法已经成为了公认的方法。只不过大家都习惯了用这种转换。如果你能保证将每一个byte[16]转换为唯一的一个字符串,并且可逆,
那么这个算法,也可用。并不一定需要按照上面的方式转换。这样就可以设计自己的密文了。并且这种密文是很难破解的。因为对应关系只有你一个知道。