C++中实现HMAC单向散列类

版权声明:本文为博主原创文章,禁止转载。 https://blog.csdn.net/FlushHip/article/details/84833688

HMAC的维基百科解释是:hash-based message authentication code,其实就是加了盐的单向散列算法。而HMAC的重点就是如何给要散列的数据加盐

加盐公式如下:

在这里插入图片描述

解释一下上面的符号:

  • \oplus 表示异或运算;
  • m m 表示要散列的数据;
  • a b a || b 表示把 a a 加入 b b ,其实就是用散列算法把 a a 算一下,紧接着把 b b 算一下;
  • i p a d ipad 是一个常量,值为0x36
  • o p a d opad 是一个常量,值为0x5C
  • H H 表示散列算法,比如MD5,SHA256;
  • K K 表示你要加入的盐, K K^{'} 表示转换过的盐,如果盐的长度大于块长(一般是64个字节,MD5,SHA1,SHA256它们处理的数据块都是这个长度)就用散列算法算一下,结果再作为盐;否者盐就是自己。

下面就是这个算法的具体步骤图解(以散列算法SHA1为例子)了:

在这里插入图片描述

这个图已经很清晰地表达了加盐散列的步骤,不是很清楚的可以待会看代码。

那么在C++中如何实现呢,先给出代码:

// hmac.h

#define HMAC_SALT_UINT8 64

class HMACDigest : public BaseDigest
{
public:
    enum HMACDigestType
    {
        MD5,
        SHA1,
        SHA256,
    };
public:
    HMACDigest(const void *saltByte, size_t saltLength, HMACDigestType type = SHA256);
    HMACDigest(const void *input, size_t length, const void *saltByte, size_t saltLength, HMACDigestType type = SHA256);
    ~HMACDigest();

    virtual const byte *Digest() override;
    virtual void Reset() override;
    virtual size_t GetSize() override;

protected:
    virtual void Update(const void *input, size_t length) override;

private:
    HMACDigest(const HMACDigest &) = delete;
    HMACDigest & operator = (const HMACDigest &) = delete;

private:
    void InitSalt(const void *saltByte, size_t saltLength);
    void Final();

private:
    HMACDigestType _type;
    std::unique_ptr<BaseDigest> _digestAlgorithm;

    byte _salt[HMAC_SALT_UINT8];

    static uint8_t sIpad;
    static uint8_t sOpad;

    static uint32_t sDigestByteLength[3];
};

// hmac.cpp

uint8_t HMACDigest::sIpad = 0x36;
uint8_t HMACDigest::sOpad = 0x5C;
uint32_t HMACDigest::sDigestByteLength[3] =
{
    16, 20, 32,
};

HMACDigest::HMACDigest(const void * saltByte, size_t saltLength, HMACDigestType type)
    : _type(type)
{
    if (saltByte == nullptr || !saltLength)
    {
        throw std::exception("salt is invalid");
    }
    _digestAlgorithm.reset(type == MD5 ? static_cast<BaseDigest *>(new Md5Digest)
                           : type == SHA1 ? static_cast<BaseDigest *>(new SHA1Digest)
                           : static_cast<BaseDigest *>(new SHA256Digest));
    InitSalt(saltByte, saltLength);
}
HMACDigest::HMACDigest(const void * input, size_t length, const void * saltByte, size_t saltLength, HMACDigestType type)
    : HMACDigest::HMACDigest(saltByte, saltLength, type)
{
    Update(input, length);
}

HMACDigest::~HMACDigest()
{
    Reset();
    std::fill(std::begin(_salt), std::end(_salt), 0);
}

const HMACDigest::byte * HMACDigest::Digest()
{
    Final();
    return _digestAlgorithm->Digest();
}

void HMACDigest::Reset()
{
    byte key[HMAC_SALT_UINT8];
    _digestAlgorithm->Reset();
    for (byte *ptr = key, *it = std::begin(_salt); it != std::end(_salt); *ptr++ = *it++ ^ sIpad)
    {
    }
    _digestAlgorithm->Update(key, HMAC_SALT_UINT8);
}

size_t HMACDigest::GetSize()
{
    return _digestAlgorithm->GetSize();
}

void HMACDigest::Update(const void * input, size_t length)
{
    _digestAlgorithm->Update(input, length);
}

void HMACDigest::InitSalt(const void * saltByte, size_t saltLength)
{
    std::memset(_salt, 0, sizeof(_salt));
    if (saltLength < HMAC_SALT_UINT8)
    {
        std::memcpy(_salt, saltByte, saltLength);
    }
    else
    {
        _digestAlgorithm->Update(saltByte, saltLength);
        size_t byteCount = _digestAlgorithm->GetSize() >> 1;
        const byte *ptr = _digestAlgorithm->Digest();
        std::memcpy(_salt, ptr, byteCount);
        _digestAlgorithm->Reset();
    }

    Reset();
}

void HMACDigest::Final()
{
    std::unique_ptr<byte[]> buff(new byte[sDigestByteLength[_type]]);
    std::memcpy(buff.get(), _digestAlgorithm->Digest(), sDigestByteLength[_type]);
    _digestAlgorithm->Reset();

    byte key[HMAC_SALT_UINT8];
    for (byte *ptr = key, *it = std::begin(_salt); it != std::end(_salt); *ptr++ = *it++ ^ sOpad)
    {
    }
    _digestAlgorithm->Update(key, sizeof(key));
    _digestAlgorithm->Update(buff.get(), sDigestByteLength[_type]);
}
// BaseDigest Definition

class BaseDigest
{
public:
    typedef uint8_t byte;
    BaseDigest();
    virtual ~BaseDigest();
    virtual const byte* Digest() = 0;
    virtual void Reset() = 0;
    virtual void Update(const void *input, size_t length);
    virtual std::string ToString(bool upCase = false)
    {
        const byte *pstr = Digest();
        size_t length = GetSize();
        return BytesToHexString(pstr, length, upCase);
    }

    virtual size_t GetSize() = 0;
protected:
    virtual void UpdateImpl(const void *input, size_t length) = 0;

    static std::string BytesToHexString(const byte *input, size_t length, bool upCase);
    static void ByteReverse(uint32_t * buffer, int byteCount);
};

总结一下这段代码:

  • 由于之前编写过SHA1和SHA256,发现这些单向散列算法都有一些共同的特征,于是可以抽象出一个基类BaseDigest,定义了单向散列算法所共有的东西;
  • 由于HMAC只是加盐,而散列算法有很多,于是这里要支持扩展,这也是抽象BaseDigest的意义,用多态来支持可变;
  • HMACDigest的默认加密算法是SHA256;
  • 代码和步骤可以相互印证,如果看懂了步骤图没看懂代码,可以对着步骤图来看代码,反之有效;
  • 以上代码给出了核心部分,有一些函数可能需要自己补充,这里就不再赘述。

参考:

最后,以上的两幅图片均来自于维基百科。

猜你喜欢

转载自blog.csdn.net/FlushHip/article/details/84833688
今日推荐