Md5加密中为什么要 & 0xff

《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总长度就变成了3*16=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---->(十进制)15*16+15=255
 *   那么对于从0 到ff一个有多少个数为:
 *   0---》1个
 *   1~ff--》256个
 *   刚好和byte的取值范围{-128~127}共256个数字可以一一对应。

 *      对应的JAVA方法为Integer.toHexString(k),k为int类型的数。
 *   那么一个16精制的正数最大为 f---->(十进制)15
 *   两个16进制的正数最大为 ff---->(十进制)15*16+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逻辑为:
 *  

	/**
	 * 获取md5密文
	 * @param pwd 未加密的密码
	 * @return
	 */
	private static String getmd5(String pwd) {
		StringBuffer str=new StringBuffer();
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] digest = md.digest(pwd.getBytes());
			for(int i=0;i



 *  在了解这么多后我们可以回归到之前的问题了。
 *   【问题:】为什么md5加密中需要将 byte[16] 的每一个数先 & 0xff后再计算呢?
 *   当然,上面已经解释了,这样并不是为了保证两个数据相等,而是为了保正生成的Md5字符串为固定长度32,
 *      并且可以将固定长度32的字符串每两个可逆为原有的byte[16]中的相应字节。至于转换后的相对应的实际值是否相当这个没有必要。
 * 下面我们来看一组数据 你看了下面的一组数据就 明白了。
Md5加密中为什么要 & 0xff_第1张图片
 *  
 *  【发现规律】
 *  这里我们可以发现 无论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]转换为唯一的一个字符串,并且可逆,
 *  那么这个算法,也可用。并不一定需要按照上面的方式转换。这样就可以设计自己的密文了。并且这种密文是很难破解的。因为对应关系只有你一个知道。







你可能感兴趣的:(java)