Md5Crypt 类

Libc crypt() “$1$” 和 Apache “$apr1$” 是基于 MD5 的哈希算法。

该类基于 Poul-Henning Kamp 的公共领域(“beer-ware”)C 实现,该实现位于:crypt-md5.c@freebsd.org

资源:

$ FreeBSD:src/lib/libcrypt/crypt-md5.c,v 1.1 1999/01/21 13:50:09 brandon Exp $

在 2012 年转换为 Kotlin,然后从 Kotlin 转换到 Java。

注意:此类是不可变的并且是线程安全的。

apr1Crypt

static String apr1Crypt(String keyBytes, String salt)

生成基于 Apache htpasswd 的兼容 “$apr1$” MD5 的哈希值。该算法与 crypt(3) “$1$” 相同,但是由于盐前缀不同而产生的输出也不同。

参数说明:

  • keyBytes 要散列的纯文本字符串。

  • salt 盐字符串,你不需要在盐字符串前面添加类似 “$1$” 字符串的前缀,Md5Crypt 会帮你添加。

异常:

  • IllegalArgumentException 如果盐与允许的模式不匹配

  • IllegalArgumentException 当捕获到 NoSuchAlgorithmException 时

实例

演示 Md5Crypt 类的 apr1Crypt() 方法用法,代码如下:

import org.apache.commons.codec.digest.Md5Crypt;
import java.security.SecureRandom;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Md5Crypt 演示
 * @author Administrator
 * @date 2021/1/25 13:08
 */
public class Md5CryptDemo1 {

    public static void main(String[] args) {
        String str = "hello world";
        byte[] bytes = str.getBytes();

        // 默认使用 ThreadLocalRandom 生成随机盐,因此,每次计算的摘要信息是不同
        System.out.println(Md5Crypt.apr1Crypt(str));
        System.out.println(Md5Crypt.apr1Crypt(bytes));

        // 指定固定的盐,apr1Crypt 每次只获取 [0-9a-zA-Z.] 范围内,最大长度为 8 个盐字符串
        System.out.println(Md5Crypt.apr1Crypt(str, "0123456789"));
        // 这里的盐只截取了 “012Ab.” 
        System.out.println(Md5Crypt.apr1Crypt(str, "012Ab.!Bb"));
        System.out.println(Md5Crypt.apr1Crypt(bytes, "0123456789"));

        // 手动指定 SecureRandom,每次调用将返回不同的盐(推荐做法)
        System.out.println(Md5Crypt.apr1Crypt(bytes, new SecureRandom()));

        // 手动指定一个 ThreadLocalRandom,每次调用将返回不同的盐
        System.out.println(Md5Crypt.apr1Crypt(bytes, ThreadLocalRandom.current()));
    }

}

输出结果:

$apr1$o5WfMCfP$XDu47oVgJh/tOHblFrKic1
$apr1$.FMB9eOd$cU7WEufQ13PO9jig7g.Qh.
$apr1$01234567$UQphyJiVOJNx1xgKtOKGp.
$apr1$012Ab.$UZv8LTS625aTKxdQfysKj.
$apr1$01234567$g8ZoSZ3YuhfUUy4ot/7tK1
$apr1$ut.6JkwN$O3/aDZhS593ENLALpS58z/
$apr1$BEPec2bv$1Xk6hEgEtCnceUrpnHPeF0

注意,上面实例中的 Md5Crypt.apr1Crypt(byte[])、Md5Crypt.apr1Crypt(String) 将提供默认随机盐,源码如下:

public static String apr1Crypt(final byte[] keyBytes) {
    return apr1Crypt(keyBytes, APR1_PREFIX + B64.getRandomSalt(8));
}

其中,APR1_PREFIX 为 “$arp1$”,而 B64.getRandomSalt() 方法代码如下:

static String getRandomSalt(final int num) {
    return getRandomSalt(num, new SecureRandom());
}

md5Crypt

static String md5Crypt(byte[] keyBytes) 和 static String md5Crypt(byte[] keyBytes, Random random)

生成与 libc6 crypt() 兼容的 “$1$” 哈希值。实例

import org.apache.commons.codec.digest.Md5Crypt;

public class Md5CryptDemo2 {

    public static void main(String[] args) {
        String str = "hello world";
        System.out.println(Md5Crypt.md5Crypt(str.getBytes()));
        System.out.println(Md5Crypt.md5Crypt(str.getBytes()));

        // 指定随机盐
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), ThreadLocalRandom.current()));
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), new SecureRandom()));
    }

}

输出结果:

$1$k10MhM7H$1jnNZq6j8mCcMZtE/.LBh/
$1$oLExMIb8$N4FIfQqBFTcehL3ZwDRi61
$1$Br0K6Kxd$s1/K8ydTb6X.uvFJFKUHT.
$1$KxkU1GsV$fqW.yZRhiF6F40HSBT2V41

根据结果得知,同一个字符串两次哈希值并不同,这是因为 Commons Codec 将默认获取一个随机盐。部分源码如下:

public static String md5Crypt(final byte[] keyBytes) {
    return md5Crypt(keyBytes, MD5_PREFIX + B64.getRandomSalt(8));
}

其中,MD5_PREFIX 等于 “$1$”,B64.getRandomSalt() 方法代码如下:

static String getRandomSalt(final int num) {
    return getRandomSalt(num, new SecureRandom());
}

static String md5Crypt(byte[] keyBytes, String salt)

生成与 Libc crypt()兼容的“$1$”基于 MD5 的哈希值。实例如下:

import org.apache.commons.codec.digest.Md5Crypt;

public class Md5CryptDemo3 {

    public static void main(String[] args) {
        String str = "hello world";
        // 盐的格式为 “$**$****”
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), "$1$0123456789"));
        // 下面将抛出如下错误:
        // java.lang.IllegalArgumentException: Invalid salt value: 0123456789
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), "0123456789"));
    }

}

输出结果:

$1$01234567$hrZxh5BCBsbRPc89Bq0By1
Exception in thread "main" java.lang.IllegalArgumentException: Invalid salt value: 0123456789
	at org.apache.commons.codec.digest.Md5Crypt.md5Crypt(Md5Crypt.java:293)
	at org.apache.commons.codec.digest.Md5Crypt.md5Crypt(Md5Crypt.java:255)
	at org.apache.commons.codec.digest.Md5Crypt.md5Crypt(Md5Crypt.java:230)
	at com.huangx.codec.md5crypt.Md5CryptDemo3.main(Md5CryptDemo3.java:15)

static String md5Crypt(byte[] keyBytes, String salt, String prefix)

生成 Libc6 crypt()“$1$”或 Apache htpasswd“$apr1$”哈希值。实例:

import org.apache.commons.codec.digest.Md5Crypt;

public class Md5CryptDemo4 {

    public static void main(String[] args) {
        String str = "hello world";
        // 盐的格式为 “$**$****”
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), "$1$0123456789", "$1$"));
        // 下面将抛出如下错误:
        // java.lang.IllegalArgumentException: Invalid salt value: 0123456789
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), "0123456789", ""));
    }

}

输出结果:

$1$01234567$hrZxh5BCBsbRPc89Bq0By1
01234567$jDoD1axfdogcQgMnJ09Qv.

static String md5Crypt(byte[] keyBytes, String salt, String prefix, Random random)

生成 Libc6 crypt()“$1$”或 Apache htpasswd“$apr1$”哈希值。实例:

import org.apache.commons.codec.digest.Md5Crypt;
import java.security.SecureRandom;
import java.util.concurrent.ThreadLocalRandom;

public class Md5CryptDemo5 {

    public static void main(String[] args) {
        String str = "hello world";
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), null,
                "", new SecureRandom()));
        System.out.println(Md5Crypt.md5Crypt(str.getBytes(), null,
                "", ThreadLocalRandom.current()));
    }

}

输出结果:

wdrGDxki$PF0EcgoTQVViDrS5xmLlF0
WhnFkCAk$jzPUDFwMuLwCJq691syEi0
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号