算法还原之哈希算法总结
一 常见哈希算法介绍
哈希算法对比
算法 | 返回值长度(hex) | 初始化链接向量(hex) | 常量表(k表)【个】 |
---|---|---|---|
MD5 | 32byte | 32byte(4个) | 64 |
SHA1 | 40byte | 40byte(5个) | 4 |
SHA256 | 64byte | 64byte(8个) | 64 |
SHA512 | 128byte | 128byte(8个) | 160(32byte) |
初始化链接向量
MD5:4个=32byte
context->state[0] = 0x67452301; |
SHA1:5个=40byte
#define INIT_DATA_h0 0x67452301UL |
SHA256:8个=64byte
ctx->hash[0] = 0x6a09e667; |
SHA512:8个=128byte(这块可以看到SHA512的前8个十六进制位就是SHA256的初始化链接变量)
context->state[0] = 0x6a09e667f3bcc908ULL; |
常量表(k表)[个]
MD5:64个
/* Round 1 */ |
SHA1:4个
#define K_00_19 0x5a827999UL |
SHA256:
static const uint32_t K[64] = { |
SHA512:
static const unsigned long long K[80] = { |
MD5-salt(加盐)
顾名思义,就是在MD5的值后面加上了一个常量。
举个例子:明文字符串为 “123456”,盐为 “hello”。
- 正常md5(123456) = e10adc3949ba59abbe56e057f20f883e
- 加盐md5(123456) = 04522abf42bb8ad979fe66948402bc0d
正常md5(123456hello) = “04522abf42bb8ad979fe66948402bc0d”
由此可以看出,加盐md5(123456,hello) = md5(123456hello)。所谓的加盐,就是在字符串拼接后再求其md5。
二 哈希算法特征寻找&比对
正常的针对这些算法的特征,就是找他的初始化链接变量和K值。这块以sha512为例
1、首先看它的链接变量:
因为32位IDA,只能表示8个十六进制位。所以,这块有16个链接变量(4-19)
2、再看一下SHA512的K表
三 HMAC算法介绍
在前面讲到哈希算法时,我们说,存储用户的哈希口令时,要加盐存储,目的就在于抵御彩虹表攻击。
我们回顾一下哈希算法:
digest = hash(input) |
正是因为相同的输入会产生相同的输出,我们加盐的目的就在于,使得输入有所变化:
digest = hash(salt + input) |
这个salt可以看作是一个额外的“认证码”,同样的输入,不同的认证码,会产生不同的输出。因此,要验证输出的哈希,必须同时提供“认证码”。
Hmac算法就是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。
Hmac算法总是和某种哈希算法配合起来用的。例如,我们使用MD5算法,对应的就是HmacMD5算法,它相当于“加盐”的MD5:
HmacMD5 ≈ md5(secure_random_key, input) |
此,HmacMD5可以看作带有一个安全的key的MD5。使用HmacMD5而不是用MD5加salt,有如下好处:
- HmacMD5使用的key长度是64字节,更安全;
- Hmac是标准算法,同样适用于SHA-1等其他哈希算法;
- Hmac输出和原有的哈希算法长度一致。
可见,Hmac本质上就是把key混入摘要的算法。验证此哈希时,除了原始的输入数据,还要提供key。
为了保证安全,我们不会自己指定key,而是通过Java标准库的KeyGenerator生成一个安全的随机的key。下面是使用HmacMD5的代码:
import javax.crypto.*; |
和MD5相比,使用HmacMD5的步骤是:
- 通过名称HmacMD5获取KeyGenerator实例;
- 通过KeyGenerator创建一个SecretKey实例;
- 通过名称HmacMD5获取Mac实例;
- 用SecretKey初始化Mac实例;
- 对Mac实例反复调用update(byte[])输入数据;
- 调用Mac实例的doFinal()获取最终的哈希值。
我们可以用Hmac算法取代原有的自定义的加盐算法,因此,存储用户名和口令的数据库结构如下:
有了Hmac计算的哈希和SecretKey,我们想要验证怎么办?这时,SecretKey不能从KeyGenerator生成,而是从一个byte[]数组恢复:
import javax.crypto.*; |
恢复SecretKey的语句就是new SecretKeySpec(hkey, “HmacMD5”)。