消息摘要(散列函数)

简介

消息摘要(Message Digest)又称为数字摘要(Digital Digest)。

消息摘要是密码学中一种用于对消息或数据进行摘要和验证的技术。在消息摘要算法中,输入一段数据,经过算法处理后得到一个固定长度的摘要(通常为固定位数的字符串),此摘要就是对原始数据的一种"指纹",不同的数据将会产生不同的摘要。消息摘要的主要作用是确保数据的完整性和验证数据的真实性,防止数据在传输过程中被篡改。

只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。

消息摘要是单向、不可逆的。

常用算法

常用的消息摘要算法包括MD5(Message Digest Algorithm 5)、SHA系列(Secure Hash Algorithm,包括SHA-1、SHA-256、SHA-512等)、CRC系列(Cyclic Redundancy Check,包括CRC-32)等。其中,MD5是一种多用途的消息摘要算法,SHA-1是较为安全的消息摘要算法,SHA-256和SHA-512具有更高的安全性。

MD5算法 : 摘要结果16个字节, 转16进制后32个字节

SHA1算法 : 摘要结果20个字节, 转16进制后40个字节

SHA256算法 : 摘要结果32个字节, 转16进制后64个字节

SHA512算法 : 摘要结果64个字节, 转16进制后128个字节

消息摘要算法在网络通信、数字签名、数据校验等领域广泛应用,能够有效地验证数据的完整性和真实性,保障信息安全。通过比对接收到的消息摘要和计算得到的消息摘要,可以确认数据的完整性和可靠性,防止恶意篡改而导致数据的不一致性。因此,消息摘要在信息安全领域中具有重要的意义。

MD5

MD5(Message Digest Algorithm 5)是一种广泛使用的消息摘要算法,用于产生数据的哈希值。MD5算法的原理是将输入的任意长度的数据经过一系列复杂的运算转换成一个128位的二进制数字,然后再将这个128位的二进制数字转换成32位的16进制数字,得到最终的MD5摘要。

Python中可以使用 hashlib 模块来调用MD5算法进行摘要计算。以下是一个简单的Python代码示例,将文本”Heil hiter“使用MD5算法进行加密:

import hashlib

# 要加密的文本
text = "Heil hiter"

# 创建一个MD5对象
md5 = hashlib.md5()

# 更新MD5对象的摘要数据
md5.update(text.encode('utf-8'))

# 获取摘要结果
result = md5.hexdigest()

print("MD5加密结果为:", result)

以上代码首先导入 hashlib 模块,然后定义了要加密的文本为"Hello, World!",创建了一个 MD5 对象,更新了文本数据并计算了摘要结果,最后打印出了MD5加密结果。

需要注意的是,MD5算法由于存在已知的安全漏洞,不再被推荐用于安全性要求较高的场景,建议使用更安全的哈希算法如SHA-256等。

SHA系列

SHA算法系列(Secure Hash Algorithm)包括了多个版本,比如SHA-1、SHA-256、SHA-512等,它们都是用于产生数据的哈希值的安全哈希函数。这些算法的原理是将输入的数据进行一系列复杂的转换和运算,最终生成一个固定长度的哈希值。

  1. SHA-1:SHA-1算法产生一个160位的哈希值,通常被认为比MD5更安全。不过,由于存在碰撞攻击等问题,SHA-1算法现在也逐渐被废弃。

  2. SHA-256:SHA-256算法生成一个256位的哈希值,安全性更高,常用于安全领域。

  3. SHA-512:SHA-512算法生成一个512位的哈希值,比SHA-256更安全,适用于对安全性要求更高的场景。

下面是一个使用Python调用SHA-1、SHA-256和SHA-512算法进行加密的示例:

import hashlib

# 要加密的文本
text = "Heil hiter"

# 使用SHA-1算法加密
sha1 = hashlib.sha1()
sha1.update(text.encode('utf-8'))
sha1_result = sha1.hexdigest()
print("SHA-1加密结果为:", sha1_result)

# 使用SHA-256算法加密
sha256 = hashlib.sha256()
sha256.update(text.encode('utf-8'))
sha256_result = sha256.hexdigest()
print("SHA-256加密结果为:", sha256_result)

# 使用SHA-512算法加密
sha512 = hashlib.sha512()
sha512.update(text.encode('utf-8'))
sha512_result = sha512.hexdigest()
print("SHA-512加密结果为:", sha512_result)

以上代码将输入文本"Heil hiter"使用SHA-1、SHA-256和SHA-512算法进行加密,分别得到对应的加密结果并打印出来。这样就可以通过Python调用这些哈希算法来对数据进行加密了。

CRC系列

CRC(Cyclic Redundancy Check)是一种循环冗余校验算法,用于检测数据在传输中是否发生错误或被篡改。CRC算法使用的是多项式除法的方法,通过对数据进行位操作和异或运算生成校验值。

常见的CRC算法包括CRC-32、CRC-16等,其中CRC-32是较为常用的一种,通常用于数据传输校验。

下面是一个使用Python调用CRC-32算法进行校验的示例:

import binascii

# 要加密的文本
text = "Heil hiter"

# 计算CRC-32校验值
crc32 = binascii.crc32(text.encode('utf-8')) & 0xffffffff
print("CRC-32校验结果为:", hex(crc32))

以上代码首先导入了 binascii 模块,然后将文本"Heil hiter"转换为UTF-8编码后,使用 crc32 方法计算CRC-32校验值,最后输出校验结果。需要注意的时,在计算CRC-32校验值时,需要将结果进行位运算与 0xffffffff,以确保结果为一个32位无符号整数。

通过以上示例代码,可以实现对数据进行CRC校验,并得到相应的校验值。CRC算法在数据传输校验、网络通信等领域有着广泛的应用,通过校验值可以快速检测数据在传输中是否发生错误或被篡改。

消息摘要应用(Java实现)

获取字符串消息摘要

package com.atguigu.digest;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;

public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 获取消息数字摘要的字节数组
        byte[] digest = messageDigest.digest(input.getBytes());//原文
        System.out.println(new String(digest));
    }
}

运行:乱码,所以我们可以使用Base64转一下

1.9.3 base64 编码

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;

public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
//        System.out.println(new String(digest));
        // base64编码
        System.out.println(Base64.encode(digest));
    }
}

运行:QSS8CpM1wn8IbyS6IHpJEg==

使用在线 md5 加密 ,发现我们生成的值和代码生成的值不一样,那是因为消息摘要不是使用base64进行编码的,所以我们需要把值转成16进制

数字摘要转换成 16 进制:虽然我们不能显示负数,但是我们可以显示字符串,把原来的字节码不用ASCII表示,改用16进制表示即可

// 4124bc0a9335c27f086f24ba207a4912     md5 在线校验
// QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16进制

代码转成16进制

要注意的是,要有补0的操作,原因在于Integer.toHexString(b & 0xff);这个语句,如果字节是00001111的话,只得到了F

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;

public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 4124bc0a9335c27f086f24ba207a4912     md5 在线校验
        // QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16进制
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
//        System.out.println(new String(digest));
        // base64编码
//        System.out.println(Base64.encode(digest));
        // 创建对象用来拼接
        StringBuilder sb = new StringBuilder();

        for (byte b : digest) {
            // 转成 16进制
            String s = Integer.toHexString(b & 0xff);
            //System.out.println(s);
            if (s.length() == 1){
                // 如果生成的字符只有一个,前面补0
                s = "0"+s;
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
        
    }
}

运行:4124bc0a9335c27f086f24ba207a4912

其他数字摘要算法

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 4124bc0a9335c27f086f24ba207a4912     md5 在线校验
        // QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16进制
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        String md5 = getDigest(input, "MD5");
        System.out.println(md5);

        String sha1 = getDigest(input, "SHA-1");
        System.out.println(sha1);

        String sha256 = getDigest(input, "SHA-256");
        System.out.println(sha256);

        String sha512 = getDigest(input, "SHA-512");
        System.out.println(sha512);


    }

    private static String toHex(byte[] digest) throws Exception {

//        System.out.println(new String(digest));
        // base64编码
//        System.out.println(Base64.encode(digest));
        // 创建对象用来拼接
        StringBuilder sb = new StringBuilder();

        for (byte b : digest) {
            // 转成 16进制
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1){
                // 如果生成的字符只有一个,前面补0
                s = "0"+s;
            }
            sb.append(s);
        }
        System.out.println("16进制数据的长度:" + sb.toString().getBytes().length);
        return sb.toString();
    }

    private static String getDigest(String input, String algorithm) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println("密文的字节长度:" + digest.length);

        return toHex(digest);
    }
}

获取文件消息摘要

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.MessageDigest;

public class DigestDemo {

    public static void main(String[] args) throws Exception{
        String input = "aa";
        String algorithm = "MD5";

        // sha1 可以实现秒传功能

        String sha1 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-1");
        System.out.println(sha1);

        String sha512 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-512");
        System.out.println(sha512);

        String md5 = getDigest("aa", "MD5");
        System.out.println(md5);

        String md51 = getDigest("aa ", "MD5");
        System.out.println(md51);
    }

    private static String getDigestFile(String filePath, String algorithm) throws Exception{
        FileInputStream fis = new FileInputStream(filePath);
        int len;
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ( (len =  fis.read(buffer))!=-1){
            baos.write(buffer,0,len);
        }
        // 获取消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 获取消息摘要
        byte[] digest = messageDigest.digest(baos.toByteArray());
        System.out.println("密文的字节长度:"+digest.length);
        return toHex(digest);
    }

    private static String getDigest(String input, String algorithm) throws Exception{
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println("密文的字节长度:"+digest.length);
        return toHex(digest);
    }

    private static String toHex(byte[] digest) {
        //        System.out.println(new String(digest));
        // 消息摘要进行表示的时候,是用16进制进行表示
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            // 转成16进制

            String s = Integer.toHexString(b & 0xff);
            // 保持数据的完整性,前面不够的用0补齐
            if (s.length()==1){
                s="0"+s;
            }
            sb.append(s);
        }
        System.out.println("16进制数据的长度:"+ sb.toString().getBytes().length);
        return sb.toString();
    }
}

运行程序 ,获取 sha-1 和 sha-512 的值

使用 sha-1 算法,可以实现秒传功能,不管咱们如何修改文件的名字,最后得到的值是一样的

如果原文修改了,那么sha-1值 就会不一样