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

news/2024/11/29 19:41:03/

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 64class 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.cppuint8_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 Definitionclass 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框架

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


http://www.ppmy.cn/news/676274.html

相关文章

77、基于STM32单片机学生信息管理系统指纹密码控制设计(程序+原理图+参考论文+相关资料+开题报告+任务书+元器件清单等)

单片机主芯片选择方案 方案一&#xff1a;AT89C51是美国ATMEL公司生产的低电压&#xff0c;高性能CMOS型8位单片机&#xff0c;器件采用ATMEL公司的高密度、非易失性存储技术生产&#xff0c;兼容标准MCS-51指令系统&#xff0c;片内置通用8位中央处理器(CPU)和Flash存储单元&a…

多表-DDL以及DQL

多表DDL 个表之间也可能存在关系 存在在一对多和多对多和一对多的关系 一对多&#xff08;外键&#xff09; 在子表建一哥字段&#xff08;列&#xff09;和对应父表关联 父表是一&#xff0c;对应子表的多&#xff08;一个部门对应多个员工&#xff0c;但一个员工只能归属一…

U盘被写保护?我来教你高级格式化

很多人都遇到过U盘被写保护了&#xff0c;造成原因有很多&#xff0c;一般是热插热拔&#xff0c;未弹出U盘就将U盘拔出等操作不当原因造成。 我也是经历过这样的情况。 1.首先&#xff0c;U盘被写保护但是可以把U盘里面的东西拷贝出来&#xff0c;以做备份。&#xff08;因为格…

高级格式化与低级格式化

概念&#xff1a; 格式化是指对磁盘或磁盘中的分区&#xff08;partition&#xff09;进行初始化的一种操作&#xff0c;这种操作通常会导致现有的磁盘或分区中所有的文件被清除。格式化通常分为低级格式化和高级格式化。如果没有特别指明&#xff0c;对硬盘的格式化通常是指高…

C++ - std::string字符串格式化方法总结

文章目录 1 C std::string字符串格式化1.1 C语言中的字符串格式化1.2 C使用std::stringstream进行字符串格式化1.3 开源的C单个头文件的字符串格式化工具1.3.1 format1.3.2 sformat 1.4 自定义的C字符串格式化函数1.5 C20的字符串标准库函数std::format 1 C std::string字符串格…

ultraEdit格式化代码

以C语言为例&#xff1a; 高级->配置工具&#xff1a; 命令行为&#xff1a; "D:\Program Files\UltraEdit\GNU\astyle.exe" --styleansi "%f" java代码的话把ansi改为java linux文件改为linux 其中前面为你的ultraEdit的安装目录中的GNU\astyle.ex…

Python格式化输出

Python格式化输出主要有三种方式&#xff0c;1、%-formatting&#xff1b;2、str.format();3、f-Strings %-formatting 字符串对象具有实用%运算符的内置操作&#xff0c;您可以使用它来格式化字符串。 以下是部分字符串格式符号&#xff1a; 符号说明%s字符串%c字符%i整数…

linux bios格式化磁盘,BIOS设置与磁盘分区、格式化.doc

项目2 BIOS设置与磁盘分区、格式化 教学目标 终极目标:能熟悉各种BIOS设置方法和步骤;能独立完成装机常用的BIOS设置;能熟练使用多种工具进行磁盘分区;能熟练使用工具进行磁盘的低级和高级格式化。 促成教学目标: 1. 能熟悉各种BIOS设置的方法和操作步骤 2. 能独立完成装机…