1.摘要算法

1.1 摘要算法(哈希算法/Hash/数字指纹):

  • 计算任意长度数据的摘要(固定长度)
  • 相同的输入数据始终得到相同的输出
  • 不同的输入尽量得到不同的输出

1.2 摘要算法目的:

  • 验证数据和原始数据是否一致,被篡改

1.3.java的Object.hashCode()方法就是一个摘要算法:

  • 输入:任意数据
  • 输出:固定长度数据(int, byte[4])
  • 相同的输入得到相同的输出:重写equals方法时,也要正确的复写hashCode方法。

1.4.碰撞

两个不同的输入得到了相同的输出,仅做示例

hash("abc") = 0x12345678 
    hash("xyz") = 0x12345678
    hash("abc") = 0x12345678 
    hash("xyz") = 0x12345678

原因:哈希算法是将一个无限的输入集合映射到一个有限的输入集合。碰撞是不能避免的,因为输出的字节长度是固定的,而输入的字节长度是不固定的。所以哈希算法是把一个无限的输入集合映射到一个有限的输出集合。
假设输出2个字节的摘要,1个字节8位,2个字节16位,即所有的输出在16个0到16个1之间,即2^16=65536。将无限的输入映射到输出集合中,肯定有不同的输入获得相同输出的情况,即碰撞。

1.5 Hash算法的安全性:

一个好的Hash算法:

  • 碰撞率要低
  • 不能根据输入猜测输出
    * 如hashA("java001") = "123456",hashA("java002") = "123457"。可能推出hashA("java003") = "123458"
  • 输入的任意一个bit的变化会造成输出完全不同
    * 如hashA("java001") = "123456",hashA("java002") = "580645"。可能推出hashA("java003") = ?
  • 很难从输出反推输入(只能暴力穷举)

1.6 常用的摘要算法:

算法

输出长度:位数

输出长度:字节数

MD5

128 bits

16bytes

SHA-1

160bits

20bytes

SHA-256

256bits

32bytes

PipeMd-160

160bits

20bytes

2 MD5算法

Java使用MD5非常简单

//示例代码
import java.security.MessageDigest;
...
        MessageDigest md = new MessageDigest.newInstance("MD5");
        //反复调用update输入数据
        md.update(data1);
        md.update(data2);
        ...
        byte[] result = md.digest();//获取长度为16的byte数组
//示例代码
import java.security.MessageDigest;
...
        MessageDigest md = new MessageDigest.newInstance("MD5");
        //反复调用update输入数据
        md.update(data1);
        md.update(data2);
        ...
        byte[] result = md.digest();//获取长度为16的byte数组

输入数据可以分片操作

import java.security.MessageDigest;
import java.util.Arrays;

public class SplitString {
    public static void main(String[] args) throws Exception{
        getMD5("helloworld");
        String[] ss = {"hello","world"};
        getMD5(ss);
    }
    static void getMD5(String s) throws Exception{
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(s.getBytes());
        byte[] result =  md.digest();
        System.out.println(Arrays.toString(result));
    }
    static void getMD5(String[] s) throws Exception{
        MessageDigest md = MessageDigest.getInstance("MD5");
        for(String si:s){
            md.update(si.getBytes());
        }
        byte[] result =  md.digest();
        System.out.println(Arrays.toString(result));
    }
}
import java.security.MessageDigest;
import java.util.Arrays;

public class SplitString {
    public static void main(String[] args) throws Exception{
        getMD5("helloworld");
        String[] ss = {"hello","world"};
        getMD5(ss);
    }
    static void getMD5(String s) throws Exception{
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(s.getBytes());
        byte[] result =  md.digest();
        System.out.println(Arrays.toString(result));
    }
    static void getMD5(String[] s) throws Exception{
        MessageDigest md = MessageDigest.getInstance("MD5");
        for(String si:s){
            md.update(si.getBytes());
        }
        byte[] result =  md.digest();
        System.out.println(Arrays.toString(result));
    }
}

usb指纹识别器Java 指纹识别 java_System

2.1 MD5用途

  • 验证文件完整性
  • MD5存储用户口令
  • 系统不存储用户原始口令
  • 系统存储用户原始口令的MD5

如何判断用户口令是否正确

  • 系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比
    * 相同:口令正确
    * 不相同:口令错误

使用MD5要注意防止彩虹表攻击

usb指纹识别器Java 指纹识别 java_java_02

抵御彩虹表攻击:

  • 对每个口令额外添加随机数salt
    * md5(password)
    * md5(salt+password)

3.代码示例

import java.math.BigInteger;
import java.security.MessageDigest;

public class SplitString {
    public static byte[] toMD5(byte[] input){
        MessageDigest md;
        try{
            md = MessageDigest.getInstance("MD5");
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        md.update(input);
        return md.digest();//返回MD5
    }
    public static void main(String[] args) throws Exception {
        String s = "MD5摘要算法测试";
        byte[] r = toMD5(s.getBytes("UTF-8"));//先将字符串转化为字节数组
        System.out.println(String.format("%032x",new BigInteger(1,r)));
        System.out.println(String.format("%040x",new BigInteger(1,r)));//不足40位,前面补0
        System.out.println(String.format("%40x0",new BigInteger(1,r)));//后面加0
    }
}
import java.math.BigInteger;
import java.security.MessageDigest;

public class SplitString {
    public static byte[] toMD5(byte[] input){
        MessageDigest md;
        try{
            md = MessageDigest.getInstance("MD5");
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        md.update(input);
        return md.digest();//返回MD5
    }
    public static void main(String[] args) throws Exception {
        String s = "MD5摘要算法测试";
        byte[] r = toMD5(s.getBytes("UTF-8"));//先将字符串转化为字节数组
        System.out.println(String.format("%032x",new BigInteger(1,r)));
        System.out.println(String.format("%040x",new BigInteger(1,r)));//不足40位,前面补0
        System.out.println(String.format("%40x0",new BigInteger(1,r)));//后面加0
    }
}

usb指纹识别器Java 指纹识别 java_java_03

import java.math.BigInteger;
import java.security.MessageDigest;

public class SplitString {
    public static byte[] toMD5(byte[] input){
        MessageDigest md;
        try{
            md = MessageDigest.getInstance("MD5");
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        md.update(input);
        return md.digest();//返回MD5
    }
    public static void main(String[] args) throws Exception {
        String s = "helloworld";
        String salt = "Random salt";
        byte[] r = toMD5((salt+s).getBytes("UTF-8"));//先将字符串转化为字节数组
        System.out.println(String.format("%032x",new BigInteger(1,r)));
        System.out.println(String.format("%040x",new BigInteger(1,r)));
        System.out.println(String.format("%40x0",new BigInteger(1,r)));
    }
}
import java.math.BigInteger;
import java.security.MessageDigest;

public class SplitString {
    public static byte[] toMD5(byte[] input){
        MessageDigest md;
        try{
            md = MessageDigest.getInstance("MD5");
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        md.update(input);
        return md.digest();//返回MD5
    }
    public static void main(String[] args) throws Exception {
        String s = "helloworld";
        String salt = "Random salt";
        byte[] r = toMD5((salt+s).getBytes("UTF-8"));//先将字符串转化为字节数组
        System.out.println(String.format("%032x",new BigInteger(1,r)));
        System.out.println(String.format("%040x",new BigInteger(1,r)));
        System.out.println(String.format("%40x0",new BigInteger(1,r)));
    }
}

usb指纹识别器Java 指纹识别 java_usb指纹识别器Java_04

4.总结:

  • MD5是一种常用的哈希算法,输出128bits/16bytes
  • 常用于验证数据完整性
  • 用于存储口令时要考虑彩虹表攻击