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

C++中实现HMAC单向散列类HMAC的维基百科解释是:hash-basedmessageauthenticationcode,其实就是加了盐的单向散列算法。而HMAC的重点就是如何给要散列的数据加盐。加盐公式如下:解释一下上面的符号:⊕\oplus⊕表示异或运算;mmm表示要散列的数据;a∣∣ba||ba∣∣b表示把aaa加入bbb,其实就是用散列算法把aaa算一下,紧接着把bbb算一下;ipadi…

大家好,欢迎来到IT知识分享网。

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

加盐公式如下:

在这里插入图片描述

解释一下上面的符号:

  • ⊕ \oplus 表示异或运算;
  • m m m表示要散列的数据;
  • a ∣ ∣ b a || b ab表示把 a a a加入 b b b,其实就是用散列算法把 a a a算一下,紧接着把 b b b算一下;
  • i p a d ipad ipad是一个常量,值为0x36
  • o p a d opad opad是一个常量,值为0x5C
  • H H H表示散列算法,比如MD5,SHA256;
  • K K 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;
  • 代码和步骤可以相互印证,如果看懂了步骤图没看懂代码,可以对着步骤图来看代码,反之有效;
  • 以上代码给出了核心部分,有一些函数可能需要自己补充,这里就不再赘述。

参考:

  • HMAC – Wikipedia
  • SHA1散列算法及其C++实现 – FlushHip的CSDN博客
  • Zeus框架

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

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/11165.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信