一、概述
通过哈希算法,我们可以验证一段数据是否有效,方法就是对比该数据的哈希值,例如,判断用户口令是否正确,我们用保存在数据库中的password_md5
对比计算md5(password)
的结果,如果一致,用户输入的口令就是正确的。
为了防止黑客通过彩虹表根据哈希值反推原始口令,在计算哈希的时候,不能仅针对原始输入计算,需要增加一个salt来使得相同的输入也能得到不同的哈希,这样,大大增加了黑客破解的难度。
如果salt是我们自己随机生成的,通常我们计算MD5时采用md5(message + salt)
。但实际上,把salt看做一个“口令”,加salt的哈希就是:计算一段message的哈希时,根据不同口令计算出不同的哈希。要验证哈希值,必须同时提供正确的口令。
这实际上就是Hmac算法:Keyed-Hashing for Message Authentication code。它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中。
和我们自定义的加salt算法不同,Hmac算法针对所有哈希算法都通用,无论是MD5还是SHA-1。采用Hmac替代我们自己的salt算法,可以使程序算法更标准化,也更安全。
关于hmac算法的详情可以参看RFC 2104(http://www.ietf.org/rfc/rfc2104.txt)。
这里需要说明的是经过mac算法得到的摘要值也可以使用十六进制编码表示,其摘要值得长度与实现算法的摘要值长度相同。例如 HmacSHA算法得到的摘要长度就是SHA1算法得到的摘要长度,都是160位二进制数,换算成十六进制的编码为40位。
HMAC用于保护消息的完整性,他采用摘要算法对消息、填充以及秘密密钥进行混合运算。在消息传输时,用户不仅传送消息本身,还传送HMAC值。接收方数据后也进行HMAC运算,再比对MAC值是否一致。由于秘密密钥只有发送方和接收才有,其他人不可能伪造假的HMAC值,从而能够知道消息是否被串改。
Python内置的hmac模块实现了标准的Hmac算法,它利用一个key对message计算“杂凑”后的hash,使用hmac算法比标准hash算法更安全,因为针对相同的message,不同的key会产生不同的hash。
二、python应用
>>> import hmac
>>> message = b'Hello, world!'
>>> key = b'secret'
>>> h = hmac.new(key, message, digestmod='MD5')
>>> # 如果消息很长,可以多次调用h.update(msg)
>>> h.hexdigest()
'fa4ee7d173f2d97ee79022d1a7355bcf'
可见使用hmac和普通hash算法非常类似。hmac输出的长度和原始哈希算法的长度一致。需要注意传入的key和message都是bytes
类型,str
类型需要首先编码为bytes
。
三、openssl应用
unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
const unsigned char *d, size_t n, unsigned char *md,
unsigned int *md_len)
evp_md 指明HMAC使用的摘要算法
key为秘密密钥指针地址
key_len为秘密密钥的长度
d为需要做HMAC运算的数据指针地址
n为d的长度
md用于存放HMAC值
md_len为HMAC值的长度
aliyun mqtt上传信息时hmac应用:
//username和 Password 签名模式下的设置方法,参考文档 https://help.aliyun.com/document_detail/48271.html?spm=a2c4g.11186623.6.553.217831c3BSFry7
HMAC(EVP_sha1(), secretKey, strlen(secretKey), clientIdUrl, strlen(clientIdUrl), tempData, &len);
char resultData[100];
int passWordLen = EVP_EncodeBlock((unsigned char *) resultData, tempData, len);
resultData[passWordLen] = '\0';
printf("passWord is %s", resultData);
char userNameData[128];
sprintf(userNameData,"Signature|%s|%s", accessKey, instanceId);
userName = userNameData;
passWord = resultData;
参考:
1. hmac 廖雪峰Python教程
2. openssl编程——摘要与HMAC