不使用hmac库实现RFC2104 SHA-1 HMAC遇测试向量不符问题
你的HMAC-SHA1代码问题梳理
咱们来一步步拆解你代码里的问题,这些都是导致和RFC测试向量对不上的核心原因:
1. 错误的Block Size定义
你把SHA-1的blocksize设成了40,但这是SHA-1输出的十六进制字符长度(SHA-1原始输出是20字节,转十六进制是40个字符)。HMAC要求的blocksize是哈希函数的内部块大小,SHA-1的内部块大小是512位,也就是64字节。这个参数错了,整个HMAC的逻辑就完全跑偏了。
2. 字符串、字节、十六进制的混乱处理
HMAC是纯字节流的算法,你现在混合了字符串转十六进制、数字转字符串拼接等操作,完全破坏了字节级的运算逻辑:
- 你的
toHex函数把输入字符串转成十六进制字符串,但这完全没必要——比如测试向量里的"Jefe"应该直接用原始字节b"Jefe",而不是它的十六进制表示"4a656665"。 - 异或操作后你把结果转成字符串拼接(比如
str(ord(key[i]) ^ val1)),这会把字节值(比如99)变成字符串"99",而不是对应的字节b'\x63',彻底打乱了后续哈希的输入结构。
3. Key填充逻辑错误
- 你写的
pad函数在key左边补'0'字符串,但HMAC要求的是在key的末尾补0字节,直到长度达到blocksize。 - 当key长度超过blocksize时,你直接赋值
key = hasher(key),但如果key是字符串的话,哈希函数会报错;正确的做法是先把key转成字节,哈希后取原始字节结果(用digest()),而不是十六进制字符串(hexdigest())。
4. 哈希函数硬编码+参数未使用
你在hmac函数里硬写了hasher = hashlib.sha1,完全忽略了传入的hashfunc参数,这样不管用户选SHA-256还是SHA-1,都只会用SHA-1,这也不符合你的功能设计。
修正后的完整代码
下面是修复所有问题后的代码,完全遵循RFC 2104的逻辑:
import hashlib def hmac(key, message, hashfunc): # 根据选择的哈希函数获取对应的实现和参数 if hashfunc == '1': # SHA-1 hasher = hashlib.sha1 blocksize = 64 # SHA-1内部块大小:512位=64字节 elif hashfunc == '0': # SHA-256 hasher = hashlib.sha256 blocksize = 64 # SHA-256内部块大小同样是512位=64字节 else: raise ValueError("Invalid hash selection - use 0 for SHA-256, 1 for SHA-1") # 统一把输入转成字节(如果是字符串的话) if isinstance(key, str): key = key.encode('utf-8') if isinstance(message, str): message = message.encode('utf-8') # 处理Key:长度超过blocksize则哈希缩短;不足则补0字节到blocksize if len(key) > blocksize: key = hasher(key).digest() elif len(key) < blocksize: key += b'\x00' * (blocksize - len(key)) # 生成o_key_pad和i_key_pad:字节级异或操作 o_key_pad = bytes(byte ^ 0x5c for byte in key) i_key_pad = bytes(byte ^ 0x36 for byte in key) # 计算内部哈希:i_key_pad拼接message,取原始字节结果 inner_hash = hasher(i_key_pad + message).digest() # 计算最终哈希:o_key_pad拼接内部哈希,转十六进制输出 final_hash = hasher(o_key_pad + inner_hash).hexdigest() print(final_hash) return final_hash def main(): while True: key = input("key = ") message = input("message = ") hash_choice = input("hash (0: SHA-256, 1: SHA-1) = ") try: hmac(key, message, hash_choice) except ValueError as e: print(e) if __name__ == "__main__": main()
验证RFC测试向量
用RFC 2104里的测试用例验证:
Test Case 1
key = "Jefe"
message = "what do ya want for nothing?"
预期HMAC-SHA1结果:effcdf6ae5eb2fa2d27416d5f184df9c259a7c79
运行代码输入上述值,选1(SHA-1),会输出正确的结果。
另外关于你提到的维基伪代码里的output size参数:那个参数确实是冗余的,因为HMAC的输出长度就是哈希函数的输出长度,所以伪代码里没用到它,不用纠结这个。
内容的提问来源于stack exchange,提问作者Kodiak
相关产品推荐
相关产品推荐

