MD5码以及相关衍生


@MD5码以及相关衍生

在写易卖网项目的时候,要求我把用户密码加密,在网上查阅资料,再次进行笔记记录,代码皆非本人敲写,只是统合自己所需要的记录翻阅学习,满怀感激


**

1.JAVA中包java.security是java提供的加密API——MessageDigest

**

  1. 作用:提供MD5,SHA-1,SHA-256等的加密算法。可接受任意长度的输入,并产生固定长度的输出。输出一般可称为摘要或散列
  2. 我们一般可以使用MessageDigest进行一些常规的加密操作,具体的使用如下:
//实例化一个MessageDigest对象,通过提供的静态的getInstance方法。方法中参数指的是加密的算法,大小写无所谓。
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
//输入待加密的字符串
messageDigest.update("待加密的字符串");
//加密之后生成的密文的字节数组
byte[] value = messageDigest.digest();
//一般不会直接使用生成的字节数组,而是转化成16进行字符串,长度一般可以设定


//下来将提供字节数组转化为16进制字符串的方法``
/**
 * 字符串数组解析成16进制字符串
 *  md : 待转化的字节数组
 * needLen: 需要转化的16进制字符串的长度,一般都是偶数
 * 说明:此算法可以设定生成的16进制字符串的长度,是拿原字节数组的前needLen/2长度的字节数组转化而来的
 *       如果不需要特定长度,直接全部转,可以设置needLen的长度为md.length*2,获取去掉needLen,设定buf的长度为j*2,for循环的      
 *       终止条件为i<j*2 即可
 * */
private static String tranform16Str(byte[] md, int needLen){
    
    
    char[] hexDigits = {
    
    '0','1','2','3','4','5','6','7','8','9',
            'a','b','c','d','e','f'};
    try {
    
    
        int j = md.length;
        char buf[] = new char[needLen];
        int k = 0;
        for (int i = 0; i < needLen/2; i++) {
    
    
            byte byte0 = md[i];
            buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
            buf[k++] = hexDigits[byte0 & 0xf];
        }
        return new String(buf);
    } catch (Exception e) {
    
    
        log.error("加密后的密文转化为16进制字符串过程中出现异常,",e);
    }
    return null;
}

简单示例:

/*MySQL*/
//为了避免用户密码直接裸露存储在数据库中,应对用户密码进行加密,有些数据库就有加密函数,比如oracle,有个示例如下,进行对密码字段加密: 
CREATE   OR   REPLACE   function   md5(input_string   VARCHAR2)   return   varchar2   
  IS   
  raw_input   RAW(128)   :=   UTL_RAW.CAST_TO_RAW(input_string);   
  decrypted_raw   RAW(2048);   
  error_in_input_buffer_length   EXCEPTION;   
  BEGIN   
  --dbms_output.put_line(sysdate   ||   '>   加密前的数据:'   ||   input_string);   
  sys.dbms_obfuscation_toolkit.MD5(input   =>   raw_input,   
  checksum   =>   decrypted_raw);   
  --dbms_output.put_line(sysdate   ||   '>   加密后的数据:'   ||   rawtohex(decrypted_raw));   
  return   lower(rawtohex(decrypted_raw));   
  END; 

而这个方法也存在缺陷性,只适合某一特定的数据库,一旦数据库换了(不是所有数据库都有加密解密存储函数),这个加密方法就不能用了。
这里要讨论的是用java方法对数据进行MD5码的加密,相对也是比较简单,因为java有现成的类java.security,MessageDigest帮我们生成MD5码。
思路是把密码字符窜进行MD5码的转换存储在数据库中,用户登入的时候把登入输入的密码字符窜进行MD5码的转换,再与数据库中存储的密码MD5码进行比较;如用户修改密码就拿新的密码的MD5码进行替换。
生成MD5码的函数如下:

  /** 
     * 把字符窜转化成MD5码,主要针对密码 
     * @param str 待转码的字符窜 
     * @return MD5码字符窜 
     * @author Tony Lin Added on 2008-9-27 
     */ 
    public String getMD5String(String str){
    
     
     try{
    
     
      byte psw[] = str.getBytes(); 
      MessageDigest md = MessageDigest.getInstance("MD5"); 
      md.update(psw); 
      return this.toHex(md.digest()); 
       
     } catch (IllegalStateException e) {
    
     
            return null; 
        } catch (NoSuchAlgorithmException e) {
    
     
            return null; 
        } 
    } 
     
    /** 
     * 把byte型数组类容拼错成字符窜 
     * @param buffer The byte array to be converted 
     * @return String 
     * @author Tony Lin Added on 2008-9-27 
     */ 
    public String toHex(byte buffer[]) {
    
     
        StringBuffer sb = new StringBuffer(); 
        String s = null; 
        for (int i = 0; i < buffer.length; i++) {
    
     
            s = Integer.toHexString((int) buffer[i] & 0xff); 
            if (s.length() < 2) {
    
     
                sb.append('0'); 
            } 
            sb.append(s); 
        } 
        return sb.toString(); 
    }

上述原文
上述原文

衍生

  • 续接(1)第一点

给一个文件进行加密怎么办。文件那么长又该如何加密?

//上面有这么一句
messageDigest.update("待加密的字符串");
/*从上面的注释上看,这个是添加待加密的明文的。
那么如果需要给一个文件进行加密怎么办。文件那么长又该如何加密?*/
//1.首先先把文件读取到一个字节数组里面
File file = new File(filePath);
InputStream in = new FileInputStream(file);
byte[] allData = readInputStream(in);//获取到文件的内容

/*接下来需要给这些内容进行加密,就需要使用到上面的MessageDigest加密的Api了。
有两种方式:*/
//方式1:一段一段往里面塞

int len = allData.length;
int i = 0;
while(true){
    
    
    try{
    
    
        int arrLen = (len - i * 4096) > 4096 ? 4096 : (len - i * 4096);
        byte[] content = new byte[arrLen];
        System.arraycopy(getData, i * 4096, content, 0, arrLen);
        messageDigest.update(content);
        i++;
    }catch (Exception e){
    
    
        log.info("字节数组拷贝出现异常,表示完成 i ={}", i);
        break;
    }
}
byte[] transform = messageDigest.digest();
//说明,MessageDigest调用digest()方法之后  输入的摘要将被重置,意思就是之后需要再加密的话  可以直接使用之前已有的对象
String miwen = tranform16Str(transform, transform.length);

//方式2:一次性全部往里面塞
messageDigest.update(allData);
byte[] second = messageDigest.digest();
//之后再进行16进制的转换操作。

#####################################################################
/*上述两种方法的结果拿到的是一样的。
那么就说明多次的update操作  只是单纯的摘要内容的追加操作。*/
/**
* 获取输入流中的内容到字节数组里面
**/
public static byte[] readInputStream(InputStream inputStream) throws IOException {
    
    
    byte[] buffer = new byte[1024];
    int len = 0;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    while ((len = inputStream.read(buffer)) != -1) {
    
    
        bos.write(buffer, 0, len);
    }
    bos.close();
    return bos.toByteArray();
}

buffer[i] & 0xff

代码中加密的时候 用到bytes[i] & 0xFF

MessageDigest md5 = MessageDigest.getInstance(“MD5”);

        bytes = md5.digest(basestring.toString().getBytes("UTF-8"));

String hex = Integer.toHexString(bytes[i] & 0xFF);

首先你得清楚几个个概念

1.byte的取值范围

    byte java 中一个字节 8位 即 -2^7—2^7-1  范围是 -128——127 (*对这个有疑问吗?在2 中解答)

 2.计算机中负数的存储的值是它的补码

   补码计算方法  负数取值是它的绝对值,的二进制数,取反,加1,那么对于          -128——-1的存储 就知道了吧

            举个例子 -12   绝对值------> 12  就是   0000  1100  取反------> 1111 0011 加 1  1111 0100 (晓得不)

    那么-128——127  就可以理解了

      -128  是    绝对值 128  二进制-------->  1000 0000   取反  0111 1111(127)  加1  ,1000 0000 (128)

      -1 的绝对值 是  1   0000 0001  取反  1111 1110 加1   1111 1111 (255) 

     计算机 中   -128——-1 对应存储的是   128-255  

      再看 2^7-1  也就是127 刚好 是0111 1111  为啥减1  因为 128 已经被占了 所以  

   -128——127  在数据库中真实存的是   128——255  外加  0——127   也就是 byte真正在计算机中存储范围是 

          0——255 只不过我们说的是它的取值范围  是 -128——127

 3.byte 转 int 16进制 (int  -2^31——2^31-1)

  bytes[i] & 0xFF

          byte和int运算,计算机会自动将 byte转化为32位,然后再运算,也就是8位转32位,那么计算机会自动将高位补1

          所以-12 在计算机种就是    1111 0011--------> 1111 1111 1111 1111 1111 1111 1111 0100

然后 &  0xFF     

         0x 代表16进制   每一位取值范围是  0 —— 15 

          但是 10——15之间是两位数字了,会和10进制分部开 所以 10——15就对应了 A——F

         那么0xFF 就是   0000 0000 0000 0000 0000 0000 1111 1111 

         &运算  得到 0000 0000 0000 0000  0000 0000  1111 0100

有没有发现什么?低八位 1111 0011 没有改变 ,那么为什么 & 0xFF而不是别的呢?

扫描二维码关注公众号,回复: 11830652 查看本文章

原因就是 byte 转int 前24位都被计算机强制 变成了1, 1111 1111 1111 1111 1111 1111 1111 0100 转化成int已经不是

-12在计算机中存储的值了,& 0xFF 0000 0000 0000 0000 0000 0000 1111 1111 恰好前24位都是 0,进行&运算,只会保留

低八位,也就是byte中原来在计算机中存储的值 1111 0100。

上述原文

integer.tohexstring

Integer.toHexString这个方法是把字节(转换成了int)以16进制的方式显示。
别人的相关笔记

位操作符

位操作符

  • C语言中六种位运算符:
  • “&” 按位与
  • “|” 按位或
  • “^” 按位异或
  • “~”取反
  • “<<”左移
  • “>>”右移

因为项目内容,我主要只记录了&:按位与
按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。

例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码) 00000001 (1的二进制补码)可见9&5=1。 按位与运算通常用来对某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 运算 ( 255 的二进制数为11111111)。


十进制转化为二进制

进制转换

十进制整数转换为二进制整数十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体做法是:用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为小于1时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。

十进制整数转二进制
如:
255=(11111111)B
255/2=127=余1
127/2=63
余1
63/2=31
=余1
31/2=15
=余1
15/2=7
余1
7/2=3
=余1
3/2=1
=余1
1/2=0
===余1


我发现,上面“9”的二进制00001001 (9的二进制补码)和我根据算法算出来的二进制1001不一样,那么千米那那么多“0”是什么

补码(二进制补码)

计算机中的有符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同 。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理 。

所以前面的“1001”就是数值位,因而,前面的一串“0”就是符号位


在介绍补码概念之前,先介绍一下“模”的概念:“模”是指一个计量系统的计数范围,如过去计量粮食用的斗、时钟等。计算机也可以看成一个计量机器,因为计算机的字长是定长的,即存储和处理的位数是有限的,因此它也有一个计量范围,即都存在一个“模”。如:时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是 ,模= .“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算 。
就是取反后加1。
假设当前时针指向8点,而准确时间是6点,调整时间可有以下两种拨法:一种是倒拨2小时,即8-2=6;另一种是顺拨10小时,8+10=12+6=6,即8-2=8+10=8+12-2(mod 12).在12为模的系统里,加10和减2效果是一样的,因此凡是减2运算,都可以用加10来代替。若用一般公式可表示为:a-b=a-b+mod=a+mod-b。对“模”而言,2和10互为补数。实际上,以12为模的系统中,11和1,8和4,9和3,7和5,6和6都有这个特性,共同的特点是两者相加等于模。对于计算机,其概念和方法完全一样。n位计算机,设n=8,所能表示的最大数是11111111,若再加1成100000000(9位),但因只有8位,最高位1自然丢失。又回到了 00000000,所以8位二进制系统的模为 。在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。把补数用到计算机对数的处理上,就是补码 。

例:+9的补码是00001001。(备注:这个+9的补码是用8位2进制来表示的,补码表示方式很多,还有16位二进制补码表示形式,以及32位二进制补码表示形式,64位进制补码表示形式等。每一种补码表示形式都只能表示有限的数字。)


猜你喜欢

转载自blog.csdn.net/MarKie_forJAVA/article/details/108770570