java 针对MD5一种加密的小研究记录
try { MessageDigest md = MessageDigest.getInstance("MD5"); String str ="a"; byte[] strByte =str.getBytes(); byte[] bCode = md.digest(strByte); final char[] strpwd="0123456789ABCDEF".toCharArray(); StringBuilder pCode = new StringBuilder(bCode.length *2); for (int i = 0; i < bCode.length; i++) { pCode.append( strpwd[ (bCode[i] >> 4) & 0x0f ] ); pCode.append(strpwd[ (bCode[i]) & 0x0f ]); } System.out.println("原始字符串:"+str); System.out.println("简单MD5加密后字符串:"+bCode.toString()); System.out.println("秘钥MD5加密后字符串:"+pCode.toString()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }
1.byte[] strByte =str.getBytes();
将字符串变为字节数组
String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组。这表示在不同的操作系统下,返回的东西不一样!
不同编码集解析出来的结果也不一样
字符对应的ASCII码
比如 “我” 这个字,在utf-8和gbk下得到的字符数组是不一样的
//utf-8
/*
strByte[0]:-26
strByte[1]:-120
strByte[2]:-111
*/
/*gbk
strByte[0]:-50
strByte[1]:-46
* */
因此,建议在得到字符数组时,最好指定编码集,以防出错。
2.byte[] bCode = md.digest(strByte);
经过MD5加密 bCode为16位的字节数组 内容对应的也是ASCII码
bCode存放的是哈希值
这里为什么会解析为16位长度的字节数组呢??? 这个问题还未解决,有待研究查询。
3.final char[] strpwd="0123456789ABCDEF".toCharArray();
秘钥 长度为16位的,少于16位报数组下标越界,多于16位没影响
为什么是16位呢?因为任何数 & 0x0f 之后不可能大于16 即取值范围是0到15所以对应的数组长度为16
0x0f 表示 0x 是一个16进制的数字 0f对应10进制中的15
0x0f 即10进制的15 对应二进制的 1111 在java中补全32 0000 0000 0000 0000 0000 0000 0000 1111
4.运算 原码反码补码
- 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。
- 反码的表示方法是:正数的反码是其本身;负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
- 补码的表示方法是:正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。 (即在反码的基础上+1)
原码转换为反码:符号位不变,数值位分别“按位取反”
反码转换为原码:符号位不变,数值位分别“按位取反”
原码转换为补码:符号位不变,数值位按位取反,末位再加1
补码转换为原码:符号位不变,数值位按位取反,末位再加1。即补码的补码等于原码
总结来说
正整数的原码、反码和补码都一样;
负数部分:
原码和反码的相互转换:符号位不变,数值位按位取反
原码和补码的相互转换:符号位不变,数值位按位取反,末位再加1
已知补码,求原码的负数的补码:符号位和数值位都取反,末位再加1
&运算规则
0&0=0; 0&1=0; 1&0=0; 1&1=1;
bCode[1]:-63 右移4:-4 右移4并&0x0f:12 &0x0f:1
bCode[i] & 0x0f
bCode[i]:1111 1111 1111 1111 1111 1111 1100 0001
0x0f: 0000 0000 0000 0000 0000 0000 0000 1111
结果: 0000 0000 0000 0000 0000 0000 0000 0001 ==> 1
bCode[i] >> 4
bCode[i]:1111 1111 1111 1111 1111 1111 1100 0001
>>4 :1111 1111 1111 1111 1111 1111 1111 1100
转源码 :1000 0000 0000 0000 0000 0000 0000 0011
+1
原码: 1000 0000 0000 0000 0000 0000 0000 0100 ==> 4 带上符号位: -4
(bCode[i] >> 4) & 0x0f
>>4 :1111 1111 1111 1111 1111 1111 1111 1100
0x0f :0000 0000 0000 0000 0000 0000 0000 1111
运算& :0000 0000 0000 0000 0000 0000 0000 1100 ==> 12
5.实践,将原代码改造,打印为更清晰的版本
try { MessageDigest md = MessageDigest.getInstance("MD5"); String str ="a"; //String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组。这表示在不同的操作系统下,返回的东西不一样! //不同编码集解析出来的结果也不一样 //字符对应的ASCII码 byte[] strByte =str.getBytes(); for (int i = 0; i < strByte.length; i++) { System.out.println("strByte["+i+"]:"+strByte[i]); } System.out.println(); //经过MD5加密 bCode为16位的字节数组 内容对应的也是ASCII码 //bCode存放的是哈希值 //这里为什么会解析为16位长度的字节数组呢??? byte[] bCode = md.digest(strByte); //秘钥 长度为16位的,少于16位报数组下标越界,多于16位没影响 //为什么是16位呢?因为任何数 & 0x0f 之后不可能大于16 即取值范围是0到15所以对应的数组长度为16 final char[] strpwd="ABCDEFGHIJKLMNOP".toCharArray(); StringBuilder pCode = new StringBuilder(bCode.length *2); for (int i = 0; i < bCode.length; i++) { //0x0f 表示 0x 是一个16进制的数字 0f对应10进制中的15 //0x0f 即10进制的15 对应二进制的 1111 在java中补全32 0000 0000 0000 0000 0000 0000 0000 1111 System.out.println("10进制结果:"); System.out.println("bCode["+i+"]:"+bCode[i]+ "\t\t"+"右移4:"+(bCode[i] >> 4)+ "\t\t"+"右移4并&0x0f:"+((bCode[i] >> 4) & 0x0f)+ "\t\t"+"&0x0f:"+(bCode[i] & 0x0f)); //int 在java和c中是4个字节表示的,因此 int是 32个二进制位 最高位表示正负 0为正1为负 int a = bCode[i]; int b = bCode[i] >> 4; int c = (bCode[i] >> 4) & 0x0f; int d = bCode[i] & 0x0f; System.out.println("二进制结果:"); System.out.println("bCode["+i+"]:"+Integer.toBinaryString(a)+ "\t\t"+"右移4:"+Integer.toBinaryString(b)+ "\t\t"+"右移4并&0x0f:"+Integer.toBinaryString(c)+ "\t\t"+"&0x0f:"+Integer.toBinaryString(d)); System.out.println(); pCode.append( strpwd[ (bCode[i] >> 4) & 0x0f ] ); pCode.append(strpwd[ (bCode[i]) & 0x0f ]); } System.out.println("原始字符串:"+str); System.out.println("简单MD5加密后字符串:"+bCode.toString()); System.out.println("秘钥MD5加密后字符串:"+pCode.toString()); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); }