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();
        } 

 

posted @ 2018-08-16 17:11  僵尸吃过跳跳糖  阅读(198)  评论(0编辑  收藏  举报