Apache Commons Codec 库中的 org.apache.commons.codec.digest.Crypt 类是 GNU libc crypt(3) 兼容哈希方法,注意:这个类是不可变的和线程安全的。有关更多细节,请参见 crypt(String, String) 方法。该方法定义如下:
以与 crypt(3) 兼容的方式加密密码。确切的算法取决于盐字符串的格式:
SHA-512 盐以 $6$ 开始,且盐的长度为 16 个字符
SHA-256 盐以 $5$ 开始,且盐的长度为 16 个字符
MD5 盐以 $1$ 开始,且盐的长度为 8 个字符
DES,传统的 unixCrypt 算法只使用 2 个字符
在DES算法中只使用密码的前8个字符!
这个方法不能识别神奇的字符串“$apr1$”和“$2a$”,因为它的输出应该与 libc 实现的输出相同。
盐字符串的其余部分是从集合 [a-Za-Z0-9.] 中提取的,如果遇到“$”符号,则以最大长度切割。因此,输入一个完整的散列值作为盐是有效的,例如用以下方法验证密码:
storedPwd.equals(crypt(enteredPwd, storedPwd))
结果字符串以标记字符串 ($n$) 开头,其中 n 与输入盐类相同。然后,盐被附加,然后是“$”标志。后面跟着实际的散列值。对于DES,字符串只包含盐和实际哈希。总长度取决于所使用的算法:
SHA-512: 106 chars
SHA-256: 63 chars
MD5: 34 chars
DES: 13 chars
实例:
crypt("secret", "$1$xxxx") => "$1$xxxx$aMkevjfEIpa35Bh3G4bAc." crypt("secret", "xx") => "xxWAum7tHdIUw"
这个方法是一个变体,它接受一个 byte[] 数组来支持不是用 UTF-8 编码的输入字符串,例如在 ISO-8859-1 中,相同的字符会产生不同的字节值。
参数:
key - 使用的明文密码
salt - 真正的盐值,没有前缀或“rounds=”。盐可能为 NULL,在这种情况下,将使用 ThreadLocalRandomy 为您生成一个盐;要获得更安全的盐,请考虑使用 SecureRandy 类来生成您自己的盐。
返回:
哈希值,即加密密码,包括盐字符串。
下面实例将使用带有 $6$、$5$、$1$ 和 null 的盐去调用 crypt 方法,代码如下:
package com.huangx.codec.crypt; import org.apache.commons.codec.digest.Crypt; public class CryptDemo { public static void main(String[] args) { String key = "aaaaaa"; // 使用 SHA-512 加密 System.out.println(Crypt.crypt(key, "$6$mysalt")); // 使用 SHA-256 加密 System.out.println(Crypt.crypt(key, "$5$mysalt")); // 使用 MD5 加密 System.out.println(Crypt.crypt(key, "$1$mysalt")); // 如果盐为 null,默认使用 SHA-512 加密 System.out.println(Crypt.crypt(key, null)); } }
运行上面代码,输出结果如下:
$6$mysalt$nloQP7cq./IX62jEhS0j43soq/UAy5b4ikVEVMen8gxdg3tI7dRgI4MgCVjddo0H3nS6xwad6FZgi7aEf6oMP0 $5$mysalt$IemuP40gJs0aWIl4..v3xcr4sAG5.VGP7t4AD95TZgC $1$mysalt$nU3yTejn7ZUl1MWxOtB2T/ $6$a1r/F8LF$tpNOvl8apJU0PVL3/RcDOSFgTyN7y8qccgbduYVBPnA4FJnhoq.Kwtrk8UfEhujopQnTt6ApiyM2qxTRxOm2x.
上面代码演示了 crypt 方法的用法,剩下的三个 crypt 方法用法和上面 crypt 方法一致,只是参数不一样,它们的定义如下:
static String crypt(byte[] keyBytes)
以 crypt(3) 兼容的方式加密密码。
static String crypt(byte[] keyBytes, String salt)
以 crypt(3) 兼容的方式加密密码。
static String crypt(String key)
使用最强的 crypt(3) 算法计算消息摘要。
实例:使用上面三个方法生成消息摘要,验证多次调用同一个方法得到的消息摘要是否一致。代码如下:
package com.huangx.codec.crypt; import org.apache.commons.codec.digest.Crypt; public class CryptDemo3 { public static void main(String[] args) { String key = "aaaaaa"; // 多次加密不一致,因为使用 ThreadLocalRandomy 随机生成盐 String a1 = Crypt.crypt(key.getBytes()); String a2 = Crypt.crypt(key.getBytes()); System.out.println(a1); System.out.println(a2); System.out.println(a1.equals(a2)); // 多次加密不一致,因为使用 ThreadLocalRandomy 随机生成盐 String b1 = Crypt.crypt(key); String b2 = Crypt.crypt(key); System.out.println(b1); System.out.println(b2); System.out.println(b1.equals(b2)); // 多次加密均是一致的,因为盐是固定的 String c1 = Crypt.crypt(key.getBytes(), "$6$mysalt"); String c2 = Crypt.crypt(key.getBytes(), "$6$mysalt"); System.out.println(c1); System.out.println(c2); System.out.println(c1.equals(c2)); } }
运行上面程序,输出结果如下:
$6$VoCdj.WQ$BRIn9neDrXL7v4Pi4C2jLzPiBKNhDi6wE1CrzeLh83YLg.bwKpvmshGCl0/dPn0epqp0QSA/ex8bUcJeWS.PX1 $6$EFNq3oLR$8VrtJ2ubt2KRplnF.Rf3RkpU.5DlzGlmeV8iRNs6bXvEOySGCKZLLSY4oVIxLwYYqrgJHuhvVwmdHpUfR042v/ false $6$F5FPJMkw$ypAe0eXVmNHySOSf649STp9mbY7FF7wcLOvqFtoCZCPZtTn3yvdwrOnhrvi.2whtxyR1MfqMTQjOdl8skxshX/ $6$bUY6NskF$nsF20ThHgXIs8OAxWIt6smWMJUREyL4oqZhhHnb1wyPv.JdVoUYpvwcqwm06rK1Gsnmb1KNIhy8Cp0x0UpG09. false $6$mysalt$nloQP7cq./IX62jEhS0j43soq/UAy5b4ikVEVMen8gxdg3tI7dRgI4MgCVjddo0H3nS6xwad6FZgi7aEf6oMP0 $6$mysalt$nloQP7cq./IX62jEhS0j43soq/UAy5b4ikVEVMen8gxdg3tI7dRgI4MgCVjddo0H3nS6xwad6FZgi7aEf6oMP0 true