【问题标题】:How to implement HMAC in python without using the hmac library?如何在不使用 hmac 库的情况下在 python 中实现 HMAC?
【发布时间】:2025-12-27 16:25:15
【问题描述】:

我想根据 RFC 2104 的定义使用 SHA-1 实现 hmac 算法。代码正在运行,但结果与 test-vectors from RFC 不同。我不确定我是否正确加载了值(字符串到十六进制,还是字符串到字节?)。

作为模板,我使用了来自wikipedia的伪代码

我不确定“块大小”和“输出大小”这两个术语。在来自*的代码中,输出大小是输入值之一,但从未使用过。

到目前为止,这是我的代码: 首先,我设置了一个哈希函数,然后我将输入字符串(键和消息)转换为十六进制值。下一步是查看密钥是否被散列或填充为零。接下来,我用这些值对键中的单个字符进行异或运算(我不知道它们来自哪里,但它们在每个示例中都没有任何评论)。最后但并非最不重要的一点是,我正在组合一个内部字符串(I_key_pad + 消息)并对其进行哈希处理,从而产生一个外部字符串,该外部字符串与外部垫组合并再次对其进行哈希处理。

    import hashlib
    from functools import reduce
    
    
    def hmac(key, message, hashfunc):
        hasher = hashlib.sha1
        blocksize = 40
        message = toHex(message) #is this right?
        key = toHex(key)
        #alternative: loading values as bytes
        #message = bytes(message, 'utf-8')
        #key = bytes(key, 'utf-8')
        if len(key) > blocksize:
            key = hasher(key)
        else:
            #key = key.ljust(blocksize, '0') #filling from right to left
            #key = key.ljust(blocksize, b'\0') #same as above but for bytes
            key = pad(key, blocksize)  #filling from left to right
    
        val1 = 0x5c 
        val2 = 0x36 
    
        i = 0
        o_key_pad = ""
        i_key_pad = ""
        while i < blocksize:
            o_key_pad += str(ord(key[i]) ^ val1)
            i_key_pad += str(ord(key[i]) ^ val2)
    
            i += 1
    
        tmp_string = str(i_key_pad) + str(message)
        tmp_string = tmp_string.encode()
        inner_hash = hasher(tmp_string).hexdigest()
        fullstring = str(o_key_pad) + inner_hash
        fullstring = fullstring.encode()
        fullstring = hasher(fullstring).hexdigest()
        print(fullstring)
    
    
    def pad(key, blocksize):
        key = str(key)
        while len(key) < blocksize:
            key = '0' + key
        key = key
        return key
    
    
    def toHex(s):
        lst = []
        for ch in s:
            hv = hex(ord(ch)).replace('0x', '')
            if len(hv) == 1:
                hv = '0' + hv
            lst.append(hv)
    
        return reduce(lambda x, y: x + y, lst)
    
    
    
    def main():
        while (1):
            key = input("key = ")
            message = input("message = ")
            hash = input("hash (0: SHA-256, 1: SHA-1) = ")
            hmac(key, message, hash)
    
    
    if __name__ == "__main__":
        main()

【问题讨论】:

  • 您能否提供一个我们可以运行的完整、可验证的示例?期望人们自己开始工作并从头开始调试的代码很多。
  • 嘿@MisterMiyagi 感谢您的评论。我添加了一些东西。这都是关于 hmac() 函数的。我的主要问题不是我的代码不起作用。这更像是一种实践的理论。
  • 我的意思是“为预期输入和预期/实际输出提供样本”,这样人们就不必通读整个 RFC。
  • 我不明白您为什么要将数据转换为十六进制编码值,这在我看来是错误的。始终以字节为单位。这是python3,对吗?
  • @JamesKPolk 是的,这是 python3。我想我这样做是因为绝望。在我完成了代码的基础后,我尝试了大约 8 个小时来得到正确的结果

标签: python hash hmac


【解决方案1】:

我不了解您代码中的所有步骤,但这里有一个简短的示例,显示仅使用 hashlib.sha1 的 HMAC-SHA1,以及一个辅助函数 xor

import hashlib

def xor(x, y):
    return bytes(x[i] ^ y[i] for i in range(min(len(x), len(y))))

def hmac_sha1(key_K, data):
    if len(key_K) > 64:
        raise ValueError('The key must be <= 64 bytes in length')
    padded_K = key_K + b'\x00' * (64 - len(key_K))
    ipad = b'\x36' * 64
    opad = b'\x5c' * 64
    h_inner = hashlib.sha1(xor(padded_K, ipad))
    h_inner.update(data)
    h_outer = hashlib.sha1(xor(padded_K, opad))
    h_outer.update(h_inner.digest())
    return h_outer.digest()


def do_tests():
    # test 1
    k = b'\x0b' * 20
    data = b"Hi There"
    result = hmac_sha1(k, data)
    print(result.hex())
    # add tests as desired

【讨论】:

  • 谢谢!这正是我想要的!