python模块--hmac

1.什么叫hmac

它的全称叫做Hash-based Message Authentication Code: 哈希消息认证码,从名字中就可以看出来这个hmac基于哈希函数的,并且还得提供一个秘钥key,它的作用就是用来保证消息的完整性,不可篡改。基本思想就是,将消息用一个哈希函数加上一个秘钥key生成一个摘要,比如现在很流行的JWT就是基于hmac实现的。

摘要算法,也叫哈希算法,散列算法,其作用就是将任意长度的输入值,转换为一个固定长度的输出值,相同的输入必定输出也相同,并且不可逆,也就是说很难根据输出值,计算出输入值。所以摘要算法一般用来隐藏一些重要数据,但也可以验证原始数据的真实性,比如应用中用户密码,不可能以明文的方式存储在数据库把,这样的话一旦泄露,后果不堪设想,所以一般都是将用户的密码,进行hash后存储起来的,这样的话,泄露出去,也很难逆推出原始密码。

常见的摘要算法:MD5 SHA1 SHA256 SHA52等等。
MD5 返回的数据固定长度为128bit
SHA1 返回的数据固定长度为160bit
SHA256 返回的数据固定长度为256bit
SHA512 返回的数据固定长度为512bit

2.hmac中的一个类和一个方法

看源码可知python的hmac模块提供了一个HMAC类new函数

应用中一般都是通过new函数来使用hmac模块。

def new(key, msg = None, digestmod = None):
    """Create a new hashing object and return it.

    key: The starting key for the hash.
    msg: if available, will immediately be hashed into the object's starting
    state.

    You can now feed arbitrary strings into the object using its update()
    method, and can ask for the hash value at any time by calling its digest()
    method.
    """
    return HMAC(key, msg, digestmod)

其实new函数就是一个快捷api,返回一个HMAC对象,然后通过HMAC对象的digest()和hexdigest()方法来获得消息签名。
另外还可以根据update()方法来增量更新摘要,这和完整传入消息的结果是一样的

key: 就是秘钥,并且必须是bytes类型或者bytearray类型
msg:需要加密的消息,并且必须是bytes类型或者bytearray类型
digestmod: 摘要算法,可以是一个hashlib模块中的哈希算法类,也可以是字符串的类名,还可以不传,不传的情况下,默认使用hashlib.md5这个摘要算法

再来看看HMAC类的__init__()方法

import hashlib as _hashlib


class HMAC:
    """RFC 2104 HMAC class.  Also complies with RFC 4231.

    This supports the API for Cryptographic Hash Functions (PEP 247).
    """
    blocksize = 64  # 512-bit HMAC; can be changed in subclasses.

    def __init__(self, key, msg = None, digestmod = None):
        """Create a new HMAC object.

        key:       key for the keyed hash object.
        msg:       Initial input for the hash, if provided.
        digestmod: A module supporting PEP 247.  *OR*
                   A hashlib constructor returning a new hash object. *OR*
                   A hash name suitable for hashlib.new().
                   Defaults to hashlib.md5.
                   Implicit default to hashlib.md5 is deprecated and will be
                   removed in Python 3.6.

        Note: key and msg must be a bytes or bytearray objects.
        """

        if not isinstance(key, (bytes, bytearray)):
            raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)

        if digestmod is None:
            _warnings.warn("HMAC() without an explicit digestmod argument "
                           "is deprecated.", PendingDeprecationWarning, 2)
            digestmod = _hashlib.md5

        if callable(digestmod):
            self.digest_cons = digestmod
        elif isinstance(digestmod, str):
            self.digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
        else:
            self.digest_cons = lambda d=b'': digestmod.new(d)

        self.outer = self.digest_cons()
        self.inner = self.digest_cons()
        self.digest_size = self.inner.digest_size

        if hasattr(self.inner, 'block_size'):
            blocksize = self.inner.block_size
            if blocksize < 16:
                _warnings.warn('block_size of %d seems too small; using our '
                               'default of %d.' % (blocksize, self.blocksize),
                               RuntimeWarning, 2)
                blocksize = self.blocksize
        else:
            _warnings.warn('No block_size attribute on given digest object; '
                           'Assuming %d.' % (self.blocksize),
                           RuntimeWarning, 2)
            blocksize = self.blocksize

        # self.blocksize is the default blocksize. self.block_size is
        # effective block size as well as the public API attribute.
        self.block_size = blocksize

        if len(key) > blocksize:
            key = self.digest_cons(key).digest()

        key = key.ljust(blocksize, b'\0')
        self.outer.update(key.translate(trans_5C))
        self.inner.update(key.translate(trans_36))
        if msg is not None:
            self.update(msg)

可以看出来hmac模块是依赖hashlib这个模块的,因为如果digestmod以字符串的方式指定摘要算法,比如'sha256',需要使用hashlib.new('sha256')导入这个类。

另外如果key的长度不满足指定摘要算法的要求,会以0填充满

3. 使用

digest()方法和hexdigest()方法

>>>import hmac
>>>key = b'\&s23@3fsd'
>>>msg = b'hello world'
>>>md5 = hmac.new(key, msg)
>>>dg = md5.hexdigest()
>>>dg
c0ecec9b969d2d32ba4583170fc22651
>>>len(dg)
32
>>>bg = md5.digest()
>>>bg
b'\xc0\xec\xec\x9b\x96\x9d-2\xbaE\x83\x17\x0f\xc2&Q'
>>>len(bg)
16

md5返回数据长度的是128bit
hexdigest()方法是返回十六进制的字符串,所以长度为32
digest()方式使返回二进制的字节串,所以长度为16

其他的sha256等等都是一样的道理

一般应用中都会使用返回二进制类型数据的digest()方法,比如JWT的最后签名阶段就是根据二进制的数据进行base64编码,最终得到JWT

猜你喜欢

转载自blog.csdn.net/weixin_42237702/article/details/103646894