我的HMAC-SHA-512实现与Python官方库结果不符,求排查
我在密码学作业中实现了RFC2104定义的HMAC函数,但输出结果和Python官方hmac库、在线工具的结果均不匹配。官方库文档明确说明其实现符合RFC2104规范,我也按照H(K XOR opad, H(K XOR ipad, text))的算法步骤编写代码,但结果仍不一致,求排查实现中的错误。
我的实现代码
from hashlib import sha512 b = 512 # 此处错误:SHA-512的块大小是1024位,不是输出长度512位 def HMAC(key, message, h, b): # 按照RFC2104定义的7步实现HMAC # 初始化opad和ipad B = b//8 # 注释错误:B是哈希函数块大小对应的字节数,不是输出字节数 ipad = 0x36 opad = 0x5c # 步骤1:将key调整为B字节长度 if len(key) > b: # 错误:len(key)是字节数,b是位数,应该比较len(key) > B k = bytearray((h(k).digest())) # 变量名错误:这里用了未定义的k,应该是h(key).digest() else: k = key + bytearray(B - len(key)) # 补零到B字节 print(k, len(k)) # 计算kXORopad和kXORipad kXopad = bytearray(B) kXipad = bytearray(B) for i in range(B): kXipad[i] ^= ipad # 错误:没有和处理后的key k做异或,应该是k[i] ^ ipad kXopad[i] ^= opad # 错误:同上,应该是k[i] ^ opad sub_result = h() sub_result.update(kXipad) sub_result.update(message) print(kXipad, len(kXipad)) print(sub_result.digest()) sub_result = bytearray(sub_result.digest()) result = h() result.update(kXopad) print(kXopad, len(kXopad)) print(result.digest()) result.update(sub_result) return result.hexdigest() # 测试用例 message = "a" key = "a" my_Hmac = HMAC(key.encode(), message.encode(), sha512, b) print(my_Hmac) import hmac official_hmac = hmac.new(key.encode(), message.encode(), digestmod=sha512) print(official_hmac.name) print(official_hmac.hexdigest()) print(my_Hmac == official_hmac.hexdigest())
输出结果对比
自定义实现的hex digest输出:
4f330db94c708a6c6b54b009e48c54a1fa1e2fad77728c2d6c28fcdb7f702fa572ea928da2154537b7cef8f4f0389cfa9c7b67ebad21b445e58c228ead9e8c15Python官方hmac库的hex digest输出:
b37ee7821bf96e293def120539070a3010f69c37a247bb4bc1344d3491b89844525519df30d89a49d2537905b30cfa7f3a6ee54324eb1c280af5a747f3baaac5在线工具计算的SHA-512 HMAC结果:
fc8c80e6b943cd07eccecf01bc6038bae68ebb6fa2e1e62b44753d7c177af7a46b089df349a19f7622a22312c76906ca9c984e1446d3ab86a98fdfa1425341c5
错误排查与修正
你的实现存在三个核心错误:
混淆了哈希函数的块大小与输出长度
SHA-512的块大小是1024位(对应128字节),而你把b设为了SHA-512的输出长度512位。RFC2104中定义的b是哈希函数的块大小(位),所以正确的b值应为1024,B = 1024//8 = 128字节。未将处理后的key与ipad/opad做异或
你当前的代码只是创建了全0的bytearray,然后直接与ipad/opad异或,得到的是全0x36或全0x5c的数组,完全没有用到处理后的keyk。正确的逻辑应该是:kXipad[i] = k[i] ^ ipad kXopad[i] = k[i] ^ opadkey长度判断逻辑错误
原代码中if len(key) > b的条件不成立:len(key)是字节数,b是位数,两者单位不一致。正确的判断应该是比较字节数:if len(key) > B(因为B是块大小的字节数)。同时,当key过长时,哈希的对象应该是原始key,而不是未定义的k,即h(key).digest()而非h(k).digest()。
修正后的代码示例
from hashlib import sha512 b = 1024 # SHA-512的块大小(位) def HMAC(key, message, h, b): B = b // 8 # 块大小对应的字节数(128字节) ipad = 0x36 opad = 0x5c # 步骤1:调整key到B字节长度 if len(key) > B: # key过长则哈希后取摘要,再补零到B字节 k = bytearray(h(key).digest()) k += bytearray(B - len(k)) else: # key过短则补零到B字节 k = bytearray(key) + bytearray(B - len(key)) # 计算k XOR ipad 和 k XOR opad kXipad = bytearray([byte ^ ipad for byte in k]) kXopad = bytearray([byte ^ opad for byte in k]) # 计算H(k XOR ipad, message) sub_result = h() sub_result.update(kXipad) sub_result.update(message) sub_digest = sub_result.digest() # 计算H(k XOR opad, sub_digest) result = h() result.update(kXopad) result.update(sub_digest) return result.hexdigest() # 测试 message = "a" key = "a" my_Hmac = HMAC(key.encode(), message.encode(), sha512, b) print(my_Hmac) import hmac official_hmac = hmac.new(key.encode(), message.encode(), digestmod=sha512) print(official_hmac.hexdigest()) print(my_Hmac == official_hmac.hexdigest()) # 现在会输出True
内容的提问来源于stack exchange,提问作者El-Icarus

