学而实习之 不亦乐乎

Java 实现 md5 加密的方法

2023-10-07 20:55:45

一、加密的三种方式

都是返回长度为32位的16进制字符串(小写)。

方法一:推荐使用

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>
import org.apache.commons.codec.digest.DigestUtils;

/**
 * MD5加密之方法一
 * @explain 借助apache工具类DigestUtils实现
 * @param str 待加密字符串
 * @return 16进制加密字符串
 */

public static String encryptToMD5(String str) {
    return DigestUtils.md5Hex(str);
}

方法二

/**
 * MD5加密之方法二
 * @explain java实现
 * @param str 待加密字符串
 * @return 16进制加密字符串
 */
public static String encrypt2ToMD5(String str) {

    // 加密后的16进制字符串
    String hexStr = "";
    try {
        // 此 MessageDigest 类为应用程序提供信息摘要算法的功能
        MessageDigest md5 = MessageDigest.getInstance("MD5");

        // 转换为MD5码
        byte [] digest = md5.digest(str.getBytes("utf-8"));
        hexStr = ByteUtils.toHexString(digest);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return hexStr;
}

方法三:Spring 核心包

import org.springframework.util.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;

/**
 * MD5加密
 * @explain springboot自带MD5加密
 * @param str 待加密字符串
 * @return 16进制加密字符串(小写)
 */
public static String toMD5(String str) {
    log.debug("MD5待加密字符串:\n" + str);
    String md5 = md5 = DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));
    log.debug("MD5加密结果:\n" + md5);
    return md5;
}

二、关于md5自动补零

我们知道,MD5加密结果通常返回的是32位的16进制字符串,加密结果之所以永远是32位,其实是进行了自动补零操作(也就是:当加密结果长度不够32位时,会在前面自动补零,直到满足长度=32位)

只所以讲这个问题,是因为在实际开发过程中,在对接接口的时候,我方用的是常用的MD5加密方案(自动补零),而接口提供方使用的是展示真实加密结果的MD5,在比对加密结果的时候肯定会经常出现加密结果不一致导致校验失败的问题。

所以,我在这里添加能够显示真实加密结果的MD5代码,以供有这种特殊需求的园友使用。

/*
 * MD5加密(真实长度,不补零)
 * @attention:
 * @date: 2020年12月01日 0001 14:44
 * @param: str
 * @return: java.lang.String
 */
public static String toMD5_realLength_UpperCase(String str) {
    if (StringUtils.isEmpty(str)) return "";

    try {
        log.debug("MD5待加密字符串:\n" + str);
        MessageDigest md5 = MessageDigest.getInstance("MD5");

        // 计算md5函数
        md5.update(str.getBytes(UTF8));

        //digest()最后确定返回md5 hash值,返回值为8位字符串。
        //因为md5hash值是16位的hex值,实际上就是8位的字符
        //BigInteger函数则将8位的字符串转换成16位hex值,
        //用字符串来表示;得到字符串形式的hash值
        String md5Str = new BigInteger( 1 , md5.digest()).toString( 16 ).toUpperCase();

        log.debug("MD5加密结果(不补零):\n" + md5Str);
        return md5Str;
    } catch (Exception e) {
        e.printStackTrace();
        return "" ;
    }
}

/*
 * MD5加密(真实长度,不补零)
 * @attention:
 * @date: 2020年12月01日 0001 14:44
 * @param: str
 * @return: java.lang.String
 */
public static String toMD5_realLength_LowerCase(String str) {

    if (StringUtils.isEmpty(str)) return "" ;

    try {
        log.debug(     "MD5待加密字符串:\n" + str);
        MessageDigest md5 = MessageDigest.getInstance("MD5" );

        // 计算md5函数
        md5.update(str.getBytes(UTF8));
        String md5Str = new BigInteger( 1 , md5.digest()).toString( 16 ).toLowerCase();
        log.debug("MD5加密结果(不补零):\n" + md5Str);
        return md5Str;
    }      catch (Exception e) {
        e.printStackTrace();
        return "" ;
    }
}

三、代码优化

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5加密工具类
 * @description
 * 不可逆算法(非对称算法)
 * md5加密会将数据加密成16进制字符串,其加密结果有且只有两种情况:
 * 1.默认情况
 * HexString.Length=32,当md5进行加密时,其加密后的长度不够32位时,会在前面自动补0;
 * 2.自定义
 * HexString.Length<32,我们可以手动来控制不让其在前面自动补0。
 * 3.虽然md5默认加密结果是16进制,但是,我们可以自定义加密结果的形式;
 * 但是,约定俗成的MD5加密结果是16进制。
 */
@Slf4j
public final class Md5Utils extends EncodeDecodeTemplate{

    private Md5Utils() {
    }

    private static final String ALGORITHM_NAME = "MD5";

    /*
     * md5加密(32位)
     * @description:
     * @date: 2022/3/3 9:51
     * @param: plainText 明文
     * @return: java.lang.String 密文
     * 16进制加密字符串(大写)
     * 固定长度:32位
     */
    @NotNull
    public static String encrypt32ToHexString(@NotNull String plainText) {
        log.info("MD5待加密字符串:" + plainText);
        String hexString = encodeToHexString(core32(plainText.getBytes(StandardCharsets.UTF_8)));
        log.info("MD5加密结果(Length=32):" + hexString);
        return hexString;
    }

    /*
     * md5加密核心实现(自动补零,32位)
     * @description: 当加密解过不够32位时,自动在最前面补0,直到补够32位
     * @date: 2022/3/2 17:58
     * @param: plainBytes 待加密二进制数据
     * @return: byte[] 加密结果
     */
    @Nullable
    private static byte[] core32(byte[] plainBytes) {

        try {
            return MessageDigest.getInstance(ALGORITHM_NAME).digest(plainBytes);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("MD5加密失败:{}", e.getMessage());
            return null;
        }
    }

    /*
     * md5加密(<=32位)
     * @description: 当长度不够32位时,不用补0
     * @date: 2022/3/3 10:12
     * @param: plainText 明文
     * @return: java.lang.String 密文
     */
    @NotNull
    public static String encryptToHexString(@NotNull String plainText) {
        log.info("MD5待加密字符串:" + plainText);
        try {
            MessageDigest md5 = MessageDigest.getInstance(ALGORITHM_NAME);

            // 计算md5函数
            md5.update(plainText.getBytes(StandardCharsets.UTF_8));
            //digest()最后确定返回md5 hash值,返回值为8位字符串。
            //因为md5hash值是16位的hex值,实际上就是8位的字符
            //BigInteger函数则将8位的字符串转换成16位hex值,
            //用字符串来表示;得到字符串形式的hash值
            String hexString = new BigInteger(1, md5.digest()).toString(16).toUpperCase();
            log.info("MD5加密结果(Length<=32):" + hexString);
            return hexString;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("MD5加密失败:{}", e.getMessage());
            return "";
        }
    }

    public static void main(String[] args) {
        String str = "++iNr+/=.0.瞾肇";
        encryptToHexString(str);
        str = "zhang三11111111";
        encrypt32ToHexString(str);
    }
}