大家好,欢迎来到IT知识分享网。
HMAC的维基百科解释是:hash-based message authentication code
,其实就是加了盐的单向散列算法。而HMAC的重点就是如何给要散列的数据加盐。
加盐公式如下:
解释一下上面的符号:
- ⊕ \oplus ⊕表示异或运算;
- m m m表示要散列的数据;
- a ∣ ∣ b a || b a∣∣b表示把 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