网络安全-消息摘要

173 阅读7分钟
  1. 什么是消息摘要算法

消息摘要算法是密码学算法中非常重要的一个分支,它通过对所有数据提取指纹信息以实现数据签名、数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密。消息摘要算法也被称为哈希(Hash)算法或散列算法。

任何消息经过散列函数处理后,都会获得唯一的散列值,这一过程称为 “消息摘要”,其散列值称为 “数字指纹”,其算法自然就是 “消息摘要算法”了。换句话说,如果其数字指纹一致,就说明其消息是一致的。

  1. 消息摘要的特点
  • 无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。
  • 消息摘要看起来是 “随机的”。
  • 消息摘要函数是单向函数,即只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息。
  • 好的摘要算法,没有人能从中找到 “碰撞” 或者说极度难找到,虽然 “碰撞” 是肯定存在的(碰撞即不同的内容产生相同的摘要)。
  1. 常见的消息摘要算法:
  • MD (Message Digest,消息摘要算法),包括 MD2、MD4 和 MD5 共 3 种算法。
  • SHA (Secure Hash Algorithm,安全散列算法),主要包括其代表算法 SHA-1 和 SHA-1 算法的变种 SHA-2 系列算法(包含 SHA-224、SHA-256、SHA-384 和 SHA-512)
  • MAC (Message Authentication Code,消息认证码算法),主要包括 HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384 和 HmacSHA512 算法
  1. MD算法

MD是应用非常广泛的一个算法家族,尤其是 MD5(Message-Digest Algorithm 5,消息摘要算法版本5),它由MD2、MD3、MD4发展而来,由Ron Rivest(RSA公司)在1992年提出,目前被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。MD2、MD4、MD5 都产生16字节(128位)的校验值,一般用32位十六进制数表示。MD2的算法较慢但相对安全,MD4速度很快,但安全性下降,MD5比MD4更安全、速度更快。

目前在互联网上进行大文件传输时,都要得用MD5算法产生一个与文件匹配的、存储MD5值的文本文件(后缀名为 .md5或.md5sum),这样接收者在接收到文件后,就可以利用与 SFV 类似的方法来检查文件完整性,目前绝大多数大型软件公司或开源组织都是以这种方式来校验数据完整性,而且部分操作系统也使用此算法来对用户密码进行加密,另外,它也是目前计算机犯罪中数据取证的最常用算法。与MD5 相关的工具有很多,如 WinMD5等。

Java示例:

public class MD5Util {
    public static String md5(String data){
        try{
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(data.getBytes());
            return bytesToHex(digest);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    // 使用盐值可以进一步提升 MD5 加密算法安全性,降低破解风险。
    public static String md5withSalt(String data, String salt){
        try{
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest((data+salt).getBytes());
            return bytesToHex(digest);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    private static String bytesToHex(byte[] bytes){
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes){
            sb.append(String.format("%02x",b));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String data = "this is md5 test";
        String md5 = md5(data);
        System.out.println("data:"+data);
        System.out.println("md5:"+md5);
        String salt = new SecureRandom().nextInt(1000)+"";
        String md5withSalt = md5withSalt(data, salt);
        System.out.println("salt:"+salt);
        System.out.println("md5withSalt:"+md5withSalt);
    }
}

输出:

data:this is md5 test

md5:a39a5bd2c42a66f19dc57ee07a01c1eb

salt:666

md5withSalt:30c730f669e09df9e2a5cbc1b4ed5b09

  1. SHA算法

SHA(Secure Hash Algorithm)是由美国专门制定密码算法的标准机构——美国国家标准技术研究院(NIST)制定的,SHA系列算法的摘要长度分别为:SHA-1为20字节(160位)、SHA-224为32字节(224位)、SHA-256为32字节(256位)、SHA-384为48字节(384位)、SHA-512为64字节(512位),由于它产生的数据摘要的长度更长,因此更难以发生碰撞,因此较之MD5更为安全,它是未来数据摘要算法的发展方向。由于SHA系列算法的数据摘要长度较长,因此其运算速度与MD5相比,也相对较慢。

目前SHA1的应用较为广泛,主要应用于CA和数字证书中,另外在目前互联网中流行的BT软件中,也是使用SHA1来进行文件校验的。

示例代码:

public class SHAUtil {
    public static String sha1(String data){
        try{
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(data.getBytes());
            return bytesToHex(digest);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    public static String sha256(String data){
        try{
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] digest = md.digest(data.getBytes());
            return bytesToHex(digest);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    private static String bytesToHex(byte[] bytes){
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes){
            sb.append(String.format("%02x",b));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String data = "this is sha test";
        String s = sha1(data);
        String s1 = sha256(data);
        System.out.println("data:"+data);
        System.out.println("sha1:"+s);
        System.out.println("sha256:"+s1);
    }
}

输出:

data:this is sha test

sha1:ee57fb52bcfc39fd7bbab4b16990e7a1bb631d9d

sha256:621586c6ee507e548b417cbbca76c2d60ebfc896725bb62181a18804081fca5b

  1. MAC算法

MAC算法 (Message Authentication Codes消息认证码算法) 含有密钥的散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加上了密钥。因此MAC算法也经常被称作HMAC算法。消息的散列值由只有通信双方知道的密钥来控制。此时Hash值称作MAC。

经过MAC算法得到的摘要值也可以使用十六进制编码表示,其摘要值的长度与实现算法的摘要值长度相同。例如 HmacSHA算法得到的摘要长度就是SHA1算法得到的摘要长度,都是160位二进制数,换算成十六进制的编码为40位。

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法,并于1997年作为RFC2104被公布,并在IPSec和其他网络协议(如SSL)中得以广泛应用,现在已经成为事实上的Internet安全标准。它可以与任何迭代散列函数捆绑使用。

示例代码:

public class HMacUtil {
    static {
        if(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    public static String hmacMD5(String plainText, String key){
        try {
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
            Mac mac = Mac.getInstance("HmacMD5");
            mac.init(keySpec);
            byte[] bytes = mac.doFinal(plainText.getBytes());
            return bytesToHex(bytes);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    public static String hmacSHA256(String plainText, String key){
        try{
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(keySpec);
            byte[] bytes = mac.doFinal(plainText.getBytes());
            return bytesToHex(bytes);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    public static String hmacSM3(String plainText, String key){
        try{
            KeyParameter keyParameter = new KeyParameter(key.getBytes());
            HMac hmac = new HMac(new SM3Digest());
            hmac.init(keyParameter);
            hmac.update(plainText.getBytes(),0,plainText.getBytes().length);
            byte[] bytes = new byte[hmac.getMacSize()];
            hmac.doFinal(bytes,0);
            return bytesToHex(bytes);
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    private static String bytesToHex(byte[] bytes){
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes){
            sb.append(String.format("%02x",b));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String plainText = "hello world";
        String key = "12345678";
        String s = hmacMD5(plainText, key);
        String s1 = hmacSHA256(plainText, key);
        String s2 = hmacSM3(plainText, key);
        System.out.println("hmacMD5:"+s);
        System.out.println("hmacSHA256:"+s1);
        System.out.println("hmacSM3:"+s2);
    }
}

输出:

hmacMD5:34cb3abd51ee10e1783d46a8f6fe5eee

hmacSHA256:11bb59374143d3968722d49c854505c8cf3c4848713cb197fb756664342f747a

hmacSM3:25fc5e75754438f9936f33f831192ce2e72f3fca2cb28da042759227322ec987