您的位置:

MD5加密解密

MD5是一种广泛使用的散列函数,它将输入数据不可逆地映射成固定长度的哈希值,通常是128位的二进制数。在计算机科学中,哈希函数(也叫散列函数)是将任意数量的数据映射到固定数量的数据的函数,而MD5就是其中一种。本文主要从以下几个方面进行详细阐述。

一、MD5加密算法原理

MD5加密算法是由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)在1991年提出,它基于消息摘要算法,能够快速将任意长度的字节序列映射为一个128位的哈希值。MD5加密算法的基本原理如下:

1、填充数据:首先在数据末位填充一位1,然后在末尾填充足够的0,使得填充前的长度之与512同余448,这里的512是MD5加密算法的一个固定参数。

    public class MD5 {

        private static final String SPLIT_STR = "'5A827999' + F(a,b,c,d) + X[k] + T[i]";
        private static final Integer[] R = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
                5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
                4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
                6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
        // T是用正弦函数生成的一个表,其中k表示第k个元素的值
        private static final Integer[] T = new Integer[64];

        static {
            for (int i = 0; i < 64; i++) {
                double d = Math.abs(Math.sin(i + 1));
                T[i] = (int) (d * (1L << 32));
            }
        }

        // ...
    }

2、划分数据:将填充后的数据划分为多个512位的块,然后对每个块进行加密操作。

3、初始化变量:对每个块进行加密操作之前,需要先对MD5加密算法用到的四个变量进行初始化,这四个变量的初始值为固定值。

4、加密:将每个块进行加密操作,每个块的加密都是基于上一个块的加密结果。最终的加密结果即为MD5值。

二、MD5加密算法应用场景

MD5加密算法在计算机领域中有着广泛的应用场景,下面介绍三个典型的应用场景。

1、密码存储

MD5加密算法可以用于密码的存储,通过将用户输入的明文密码进行MD5加密,将加密后的结果存储到数据库中。当用户下一次登录时,再将输入的密码进行MD5加密,并与数据库中的密文密码进行比对,从而达到验证用户身份的目的。

    public class PasswordUtils {

        /**
         * 对字符串进行MD5加密
         *
         * @param src 待加密的字符串
         * @return 加密后的字符串
         */
        public static String encrypt(String src) {
            MessageDigest md5;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
            byte[] byteArray = src.getBytes(StandardCharsets.UTF_8);
            byte[] md5Bytes = md5.digest(byteArray);
            StringBuilder hexValue = new StringBuilder();
            for (byte md5Byte : md5Bytes) {
                int val = ((int) md5Byte) & 0xff;
                if (val < 16) {
                    hexValue.append("0");
                }
                hexValue.append(Integer.toHexString(val));
            }
            return hexValue.toString();
        }

        // ...
    }

2、数字签名

MD5加密算法可以用于数字签名,数字签名是指用某个私钥对信息进行签名,再将签名与信息一起发送给收件人,收件人使用对应的公钥来验证签名的合法性。

    public class DigitalSignatureUtils {

        /**
         * 对数据进行数字签名
         *
         * @param data 待签名的数据
         * @param privateKey 签名私钥
         * @return 数字签名
         * @throws Exception
         */
        public static byte[] sign(byte[] data, PrivateKey privateKey) throws Exception {
            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initSign(privateKey);
            signature.update(data);
            return signature.sign();
        }

        /**
         * 验证数字签名
         *
         * @param data 原始数据
         * @param signature 签名
         * @param publicKey 验证公钥
         * @return 验证结果
         * @throws Exception
         */
        public static boolean verify(byte[] data, byte[] signature, PublicKey publicKey) throws Exception {
            Signature sig = Signature.getInstance("MD5withRSA");
            sig.initVerify(publicKey);
            sig.update(data);
            return sig.verify(signature);
        }

    }

3、文件校验

MD5加密算法可以用于文件校验,因为文件的MD5值是唯一的,这意味着当用户想要下载一个文件时,可以先对文件的MD5值进行校验,如果MD5值一致,则表明该文件是原来的文件,否则文件就被篡改了。

    public class FileCheckUtils {

        /**
         * 计算文件MD5值
         *
         * @param file 待计算MD5值的文件
         * @return MD5值
         * @throws Exception
         */
        public static String getFileMD5(File file) throws Exception {
            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[1024];
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            int numRead;
            do {
                numRead = fileInputStream.read(buffer);
                if (numRead > 0) {
                    md5.update(buffer, 0, numRead);
                }
            } while (numRead != -1);
            fileInputStream.close();
            byte[] md5Bytes = md5.digest();
            StringBuilder hexValue = new StringBuilder();
            for (byte md5Byte : md5Bytes) {
                int val = ((int) md5Byte) & 0xff;
                if (val < 16) {
                    hexValue.append("0");
                }
                hexValue.append(Integer.toHexString(val));
            }
            return hexValue.toString();
        }

    }

三、MD5加密算法存在的问题

虽然MD5加密算法在很多应用场景中都得到了广泛的应用,但由于一些历史原因和技术问题,MD5加密算法在某些场景下可能存在一些问题,下面介绍两个MD5加密算法存在的问题。

1、碰撞攻击

在密码存储、数字签名等场景中,MD5加密算法被用于验证数据的完整性、安全性,但实际上,MD5加密算法可以被破解,因为它并不是完全无法反推。碰撞攻击是指在输入的任意两个不同的数据中找到相同的MD5值的过程。目前针对MD5的碰撞攻击已经非常成熟,因此在密码存储、数字签名等场景中建议使用更为安全的加密算法。

2、MD5彩虹表攻击

彩虹表攻击是指对密文进行预先计算,存储在数据库中的彩虹表中,然后进行账号破解的攻击方式。虹表都是提前计算好算法,存放在一个表格里,由于在运算速度上,计算要比破解慢得多,因此,指定长度的几乎所有明文可能,都可以构造一张包含其MD5值的彩虹表。因此,为了增强密码的安全性,导入“盐值”(即随机数)后再进行MD5加密。

    public class SaltMd5 {

        /**
         * 加盐MD5加密
         *
         * @param src 待加密的字符串
         * @param salt 盐值
         * @return 盐值加密后的字符串
         */
        public static String encrypt(String src, String salt) {
            return DigestUtils.md5Hex(salt + DigestUtils.md5Hex(src));
        }

    }

总之,MD5加密算法在现实生活中有着广泛的应用场景,在使用时候,我们需要根据具体的业务场景来选择加密方式,建议在密码存储、数字签名等安全性要求比较高的场景下,不要使用MD5加密算法。