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

加盐公式如下:

解释一下上面的符号:

  • ⊕\oplus⊕表示异或运算;
  • mmm表示要散列的数据;
  • a∣∣ba || ba∣∣b表示把aaa加入bbb,其实就是用散列算法把aaa算一下,紧接着把bbb算一下;
  • ipadipadipad是一个常量,值为0x36
  • opadopadopad是一个常量,值为0x5C
  • HHH表示散列算法,比如MD5,SHA256;
  • KKK表示你要加入的盐,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框架

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

C++中实现HMAC单向散列类相关推荐

  1. MD5(单向散列算法)原理分析

    注:本文章转载于网络. MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2.MD3和MD4发展而来.MD5算法的使用不需要支付任何版权费用. ...

  2. MD5单向散列算法详解

    历史: MD5 叫信息-摘要算法,是一种密码的算法,它可以对任何文件产生一个唯一的MD5验证码,每个文件的MD5码就如同每个人的指纹一样,都是不同的,这样,一旦这个文件在传输过程中,其内容被损坏或者被 ...

  3. 密码学(一)—— 背景、常用的密码算法简介,单向散列简介、数字签名简介

    背景 密码,最初的目的就是用于对信息的加密,计算机领域的密码技术种类繁多,但随着密码学的运用,密码还用于身份认证,防止否认等功能上.最基本的,是信息加密解密分为对称加密和非对称加密,这两者的区别在于是 ...

  4. 物联网安全-单向散列算法

    单向散列函数简介 概论 ​  单项散列函数又称为安全散列函数或者哈希函数,可以将一段可变长度是输入数据转化为固定长度的一段输出值. 输入数据通常称为消息,输出数据通常称为消息摘要或者摘要,可用于检查消 ...

  5. 数字签名、数字证书、对称加密算法、非对称加密算法、单向加密(散列算法)...

    2019独角兽企业重金招聘Python工程师标准>>> 数字签名是什么? 1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥. 2. 鲍勃把公钥送给他的朋友们----帕蒂.道格.苏珊-- ...

  6. 数字签名、数字证书、对称加密算法、非对称加密算法、单向加密(散列算法)

    数字签名是什么? 1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥. 2. 鲍勃把公钥送给他的朋友们--帕蒂.道格.苏珊--每人一把. 3. 苏珊给鲍勃写信,写完后用鲍勃的公钥加密,达到保密的效果. 4 ...

  7. 数字签名、数字证书、对称加密算法、非对称加密算法、单向加密(散列算法)——Web网络系列学习笔记

    数字签名是什么? 1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥. 2. 鲍勃把公钥送给他的朋友们--帕蒂.道格.苏珊--每人一把. 3. 苏珊给鲍勃写信,写完后用鲍勃的公钥加密,达到保密的效果. 4 ...

  8. 创建分区表+分区表的分类+创建散列分区表+查看散列分区表分区中的数据+创建列表分区表+查看列表分区表分区中的数据...

    创建分区表 分区表的分类 范围分区:对数据表的某个值的范围进行分区,需要使用partition by range字句. 散列分区: 1通过hash算法均匀分布数据的一种分区类型. 2通过在I/O设备上 ...

  9. 散列算法(也叫:摘要算法)

    散列算法(也叫:摘要算法): 特点: ① 无论输入的消息有多长,计算出来的消息摘要的长度总是固定的. ② 消息摘要看起来是"随机的".这些比特看上去是胡乱的杂凑在一起的. ③ 一般 ...

最新文章

  1. linux系统调用理解之摘录(3)
  2. 运用Appium 实现添加微信好友自动化
  3. 进程控制(kill)
  4. 【详细说明】nginx反向代理wss websocket
  5. GAN —— 《Generative Adversarial Nets》
  6. JavaScript表单序列化的方法详解
  7. 正则基础之——非捕获组
  8. maven创建父项目和子项目
  9. 【springboot基础】配置日志输出级别以及输出位置
  10. nginx防火墙在哪设置_Nginx 基本使用介绍
  11. Bootstrap table列内容过长,显示固定长度,超过内容隐藏
  12. tranmac不能识别_U盘插入苹果mac系统不能识别的解决方法
  13. 响应式图像对齐中心引导3
  14. 黄聪:解决WordPress的函数mb_strimwidth截断中文产生乱码的问题
  15. 用POP3获取邮箱邮件内容(完整C#源码)
  16. 查普曼-科莫高洛夫方程Chapman–Kolmogorov equation
  17. 有源光网络VS无源光网络
  18. Yii2.0 获取当前url和域名
  19. win10如果虚拟化服务器,Win10内置虚拟化技术Virtualization在哪里 Win10查看CPU是否支持VT步骤...
  20. 学生综合素质评价系统c语言,学生综合素质评价表自我评价范文(2)

热门文章

  1. 教你如何在Linux下查找大文件
  2. 职业选择的两种路径:向外求 vs 向内求
  3. 初学者对HTML的认知
  4. 「需求广场」需求词更新明细(十四)
  5. freenom php,【云续期】github actions上执行freenom免费域名自动续期php脚本
  6. dlib 人脸识别论文 One Millisecond Face Alignment with an Ensemble of Regression Trees
  7. Java中Vector类的常用方法
  8. 大数据计算(分布式)--简单理解
  9. 如何从零开始自学前端
  10. MySQL中大于小于,IN,OR,BETWEEN性能比较分析