C++ 通过CryptoPP计算Hash值
Crypto++ (CryptoPP) 是一个用于密码学和加密的 C++ 库。它是一个开源项目,提供了大量的密码学算法和功能,包括对称加密、非对称加密、哈希函数、消息认证码 (MAC)、数字签名等。Crypto++ 的目标是提供高性能和可靠的密码学工具,以满足软件开发中对安全性的需求。
该库包含了许多常见的密码学算法,如AES、DES、RSA、DSA、SHA等,使开发者能够轻松地在他们的应用程序中实现安全性和加密功能。Crypto++ 是以面向对象的方式设计的,因此它的使用通常涉及使用类和对象来表示不同的密码学概念和算法。
Crypto++ 提供了许多特性,包括多平台支持(Windows、Linux、macOS等)、容易使用的 API、高性能的实现、丰富的文档和社区支持。在使用 Crypto++ 之前,你需要确保正确地配置和链接 Crypto++ 库到你的项目中。
编译Crypto库
目前Crypto库的最新版本为8.90,读者可自行下载对应的库源代码,下载好以后使用Visual Studio工具打开源文件中的cryptest.sln文件。
打开以后选中调试菜单中的属性页面,此时将运行库修改为多线程/MT模式,否则虽可以编译通过但这个库却无法被正常使用,此处是一个坑。

此时选中解决方案,并直接点击重新编译库,这个过程可能需要等待一段时间,更具设备的配置而不同读者可在最底部看到输出进度;

当编译成功以后,读者可以来到cryptopp890\Win32\Output\Release目录下,该目录下的则是编译成功后的lib库文件,可以将这3个文件全部保存在新建的lib文件夹内。

接着在cryptopp890文件夹下直接搜索所有的*.h头文件,并放入到新建的include文件夹内,此时我们就有了最新版本的开发工具包了。

使用该库也很容易,只需要包含Include与Lib库文件即可,如下图所示配置;

使用MD5算法
MD5(Message Digest Algorithm 5)是一种常见的哈希函数,用于产生128位的散列值(通常以32位的十六进制数表示)。MD5广泛用于检查数据完整性、数字签名、密码存储等领域。
以下是 MD5 算法的基本概述:
- 输入处理: MD5 接受任意长度的输入,但输出是固定长度的128位。输入被划分为512位的块,每个块包含16个32位的字。
- 填充: 如果输入的位数不是512的倍数,就需要填充数据,使其长度满足这个条件。填充是通过在消息的末尾添加一个'1'和零比特,然后添加一个表示原始消息长度的64位整数来完成的。
- 初始化: MD5 有四个32位的寄存器(A、B、C、D),初始化为特定的常数。这些寄存器将在处理每个消息块时进行更新。
- 处理块: 对于每个512位块,MD5 执行64个操作轮次。每个轮次都使用一个非线性函数,一个常量和一个消息块的子集。这些轮次通过循环结构连接起来。
- 输出: MD5 的输出是四个32位字的级联,通常以32位的十六进制数表示。这四个字的顺序是 A、B、C、D。
MD5 算法的设计目标是产生一个唯一的(或极其难以相同)散列值,以便在密码存储、数字签名和数据完整性检查等场景中使用。然而,由于MD5存在一些安全性问题,特别是其易受碰撞攻击的漏洞,现在不再被推荐用于安全性要求较高的场景。对于安全性要求较高的应用,推荐使用更强大和安全的哈希函数,如SHA-256或SHA-3。
如下这段代码中涉及到一些特殊的类,这里将分别介绍功能;
FileSource: 用于从文件中读取数据。
StringSource: 用于从字符串或二进制数据中读取数据。
HashFilter: 表示一个用于计算哈希的过滤器。它接受一个哈希函数作为参数,这里是
md5。md5: 用于计算输入数据的 MD5 哈希值。
HexEncoder: 用于将二进制数据编码为十六进制表示。
StringSink(dst 或 digest): 用于将数据写入字符串。在这里,它将最终的哈希值以十六进制字符串的形式写入到
dst或digest中。
#include <Windows.h>
#include <iostream>
#include <md5.h>
#include <files.h>
#include <hex.h>
using namespace std;
using namespace CryptoPP;
#pragma comment(lib,"cryptlib.lib")
int main(int argc, char* argv[])
{
// 定义MD5类
MD5 md5;
// 计算字符串MD5
string src = "Hello World";
string dst;
StringSource(src, true, new HashFilter(md5, new HexEncoder(new StringSink(dst))));
std::cout << "字符串hash = " << dst << std::endl;
// 计算字节数组MD5
string digest;
BYTE pData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
DWORD dwDataSize = sizeof(pData);
StringSource(pData, dwDataSize, true, new HashFilter(md5, new HexEncoder(new StringSink(digest))));
std::cout << "数组长度 = " << dwDataSize << std::endl;
std::cout << "数组hash = " << digest << std::endl;
system("pause");
return 0;
}
运行后则可分别输出字符串与数组的MD5值,如下图所示;

如果需要从文件中读取则需要使用FileSource类,在计算MD5之前先将文件读入内存在进行计算,如下所示;
#include <Windows.h>
#include <iostream>
#include <md5.h>
#include <files.h>
#include <hex.h>
using namespace std;
using namespace CryptoPP;
#pragma comment(lib,"cryptlib.lib")
// 计算文件MD5
string CalMD5ByFile(char *pszFileName)
{
string value;
MD5 md5;
FileSource(pszFileName, true, new HashFilter(md5, new HexEncoder(new StringSink(value))));
return value;
}
// 计算数据MD5
string CalMD5ByMemory(PBYTE pData, DWORD dwDataSize)
{
string value;
MD5 md5;
StringSource(pData, dwDataSize, true, new HashFilter(md5, new HexEncoder(new StringSink(value))));
return value;
}
int main(int argc, char* argv[])
{
// 定义MD5类
MD5 md5;
// 计算文件的MD5
string md51 = CalMD5ByFile("d://lyshark.exe");
printf("md5 = %s\n", md51.c_str());
// 计算文件内存的MD5
HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
// 取文件长度
DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
BYTE *pData2 = new BYTE[dwFileSize2];
// 读文件到内存
ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);
// 计算MD5
string md52 = CalMD5ByMemory(pData2, dwFileSize2);
printf("md5 = %s\n", md52.c_str());
system("pause");
return 0;
}
如下图所示,是计算后得到的文件的MD5值;

使用CRC32算法
CRC32(Cyclic Redundancy Check,循环冗余校验)是一种广泛用于数据校验的错误检测算法。它基于多项式除法,在计算机领域中常用于检测数据传输或存储过程中的错误。
以下是CRC32算法的基本概述:
- 多项式选择: CRC32使用一个32位的二进制多项式,通常表示为一个32位的二进制数。这个多项式在CRC计算中充当除数。
- 数据处理: 要计算CRC32,首先需要将数据按位划分成块,每个块的长度等于多项式的次数。通常,CRC32使用字节为单位进行处理。
- 初始值: CRC32计算开始前,需要初始化一个32位的寄存器为一个特定的初始值,通常为全1或全0。
- 除法运算: 对于每个数据块,将它与32位的寄存器中的值进行异或操作。然后,将寄存器中的值右移一位,再与多项式进行异或操作。这个过程重复进行,直到所有数据块都被处理完。
- 最终值: 在处理完所有数据块后,寄存器中的值就是CRC32的最终校验值。
- 校验值附加: 通常,CRC32的结果会附加在原始数据的末尾,形成一个带有校验值的完整数据块。
CRC32广泛应用于文件传输、存储系统、以太网通信等领域,用于检测数据传输中的错误。由于其简单性和高效性,CRC32在实际应用中被广泛采用。然而,需要注意的是,CRC32主要用于错误检测而非安全性,不适用于对恶意操作的防范。在一些对安全性要求较高的场景中,其他更强大的校验算法可能更为合适。
crc32算法的使用只需要包含<crc.h>头文件,并将程序内的MD5类改为CRC32即可,其他的无任何差异,代码如下所示;
#include <Windows.h>
#include <iostream>
#include <crc.h>
#include <files.h>
#include <hex.h>
using namespace std;
using namespace CryptoPP;
#pragma comment(lib,"cryptlib.lib")
// 计算文件CRC32
string CalCRCByFile(char *pszFileName)
{
string value;
CRC32 crc;
FileSource(pszFileName, true, new HashFilter(crc, new HexEncoder(new StringSink(value))));
return value;
}
// 计算数据CRC32
string CalCRCByMemory(PBYTE pData, DWORD dwDataSize)
{
string value;
CRC32 crc;
StringSource(pData, dwDataSize, true, new HashFilter(crc, new HexEncoder(new StringSink(value))));
return value;
}
int main(int argc, char* argv[])
{
// 定义CRC32类
CRC32 crc32;
// 计算文件的MD5
string crc = CalCRCByFile("d://lyshark.exe");
printf("crc32 = %s\n", crc.c_str());
// 计算文件内存的crc
HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
// 取文件长度
DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
BYTE *pData2 = new BYTE[dwFileSize2];
// 读文件到内存
ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);
// 计算crc
string crc2 = CalCRCByMemory(pData2, dwFileSize2);
printf("crc32 = %s\n", crc2.c_str());
system("pause");
return 0;
}
程序运行后将会计算文件的CRC32值,如下图所示;

使用SHA1算法
SHA-1(Secure Hash Algorithm 1)是一种常见的哈希函数,用于生成160位的散列值。与MD5类似,SHA-1也被广泛用于数字签名、数据完整性验证等领域。然而,由于SHA-1存在一些安全性漏洞,特别是对碰撞攻击的脆弱性,因此在对安全性要求较高的应用中,不再推荐使用SHA-1,而是转向使用更安全的哈希算法,如SHA-256或SHA-3。
以下是SHA-1算法的基本概述:
- 输入处理: SHA-1同样接受任意长度的输入,但输出为160位。输入被划分为512位的块,每个块包含16个32位字。
- 填充: 与MD5类似,如果输入长度不是512的倍数,需要对输入进行填充,使其满足条件。填充的方式是在消息的末尾添加一个'1'和零比特,然后添加一个64位整数,表示原始消息长度。
- 初始化: SHA-1有五个32位的寄存器(A、B、C、D、E),初始化为特定的常数。这些寄存器将在处理每个消息块时进行更新。
- 处理块: SHA-1的处理方式类似于MD5,但使用了不同的非线性函数和常量。每个消息块经过80个操作轮次,其中包括迭代、位运算和条件操作。
- 输出: SHA-1的输出是五个32位字的级联,通常以40位的十六进制数表示。这五个字的顺序是A、B、C、D、E。
由于SHA-1存在安全性问题,特别是在2017年被证明对碰撞攻击不再是安全的,因此已经不再被推荐用于安全性要求较高的应用。取而代之的是,SHA-256和SHA-3等更安全的哈希算法,它们提供更长的输出长度和更强的抗碰撞能力。
与MD5的计算方法一致,SHA系列计算方式只需引入<sha.h>系列头文件,并使用SHA1 sha1;类进行计算即可,如下代码所示;
#include <Windows.h>
#include <iostream>
#include <sha.h>
#include <files.h>
#include <hex.h>
using namespace std;
using namespace CryptoPP;
#pragma comment(lib,"cryptlib.lib")
// 计算文件SHA1
string CalSHA1ByFile(char *pszFileName)
{
string value;
SHA1 sha1;
FileSource(pszFileName, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));
return value;
}
// 计算数据SHA1
string CalSHA1ByMemory(PBYTE pData, DWORD dwDataSize)
{
string value;
SHA1 sha1;
StringSource(pData, dwDataSize, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));
return value;
}
int main(int argc, char* argv[])
{
// 定义SHA类
SHA1 sha1;
// 计算文件的sha1
string sha11 = CalSHA1ByFile("d://lyshark.exe");
printf("sha1 = %s\n", sha11.c_str());
// 计算文件内存的sha1
HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
// 取文件长度
DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
BYTE *pData2 = new BYTE[dwFileSize2];
// 读文件到内存
ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);
// 计算sha1
string sha12 = CalSHA1ByMemory(pData2, dwFileSize2);
printf("sha1 = %s\n", sha12.c_str());
system("pause");
return 0;
}
sha1计算结果如下图所示;

使用SHA256算法
SHA-256(Secure Hash Algorithm 256-bit)是SHA-2(Secure Hash Algorithm 2)家族中的一种哈希函数,用于生成256位的散列值。SHA-256是目前广泛应用于各种安全领域的强大哈希算法,包括数字签名、证书签名、数据完整性验证等。SHA-256提供了更高的安全性,相对于之前的SHA-1和MD5来说更为强大。
以下是SHA-256算法的基本概述:
- 输入处理: SHA-256同样接受任意长度的输入,但输出为256位。输入被划分为512位的块,每个块包含16个32位字。
- 填充: 与SHA-1和MD5相似,如果输入长度不是512的倍数,需要对输入进行填充,以满足条件。填充的方式包括添加一个'1'和零比特,然后添加一个64位整数,表示原始消息长度。
- 初始化: SHA-256有八个32位的寄存器(A、B、C、D、E、F、G、H),初始化为特定的常数。这些寄存器将在处理每个消息块时进行更新。
- 处理块: SHA-256的处理方式包括64个操作轮次,每个轮次使用一个非线性函数、常量和消息块的子集。这些轮次通过循环结构连接起来。
- 输出: SHA-256的输出是八个32位字的级联,通常以64位的十六进制数表示。这八个字的顺序是A、B、C、D、E、F、G、H。
SHA-256相对于SHA-1和MD5提供了更高的抗碰撞能力和更强的安全性,使其成为当前广泛使用的哈希算法之一。然而,随着计算能力的增强,一些专家逐渐倾向于使用更长的哈希算法,如SHA-3,以适应未来更高的安全性需求。
代码调用上与sha1保持一致,Sha256同样只需要少量的更改,只要掌握了这个规律,那么则可以完成其他算法的调用,代码如下所示;
#include <Windows.h>
#include <iostream>
#include <sha.h>
#include <files.h>
#include <hex.h>
using namespace std;
using namespace CryptoPP;
#pragma comment(lib,"cryptlib.lib")
// 计算文件SHA1
string CalSHA1ByFile(char *pszFileName)
{
string value;
SHA256 sha256;
FileSource(pszFileName, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));
return value;
}
// 计算数据SHA1
string CalSHA1ByMemory(PBYTE pData, DWORD dwDataSize)
{
string value;
SHA256 sha256;
StringSource(pData, dwDataSize, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));
return value;
}
int main(int argc, char* argv[])
{
// 定义SHA类
SHA256 sha256;
// 计算文件的sha256
string sha11 = CalSHA1ByFile("d://lyshark.exe");
printf("sha256 = %s\n", sha11.c_str());
// 计算文件内存的sha256
HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
// 取文件长度
DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
BYTE *pData2 = new BYTE[dwFileSize2];
// 读文件到内存
ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);
// 计算sha256
string sha12 = CalSHA1ByMemory(pData2, dwFileSize2);
printf("sha256 = %s\n", sha12.c_str());
system("pause");
return 0;
}
运行后则可输出文件的sha256值,如下图所示;

C++ 通过CryptoPP计算Hash值的更多相关文章
- C#实现像Git那样计算Hash值
从Git Tip of the Week: Objects一文中得知,Git是这样计算提交内容的Hash值的: Hash算法用的是SHA1 计算前,会在内容前面添加"blob 内容长度\0& ...
- 痞子衡嵌入式:利用i.MXRT1xxx系列内部DCP引擎计算Hash值时需特别处理L1 D-Cache
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是利用i.MXRT1xxx系列内部DCP引擎计算Hash值时需特别处理L1 D-Cache. 关于i.MXRT1xxx系列内部通用数据协处 ...
- JAVA-读取文件部分内容计算HASH值
对于一些大文件,有时会需要计算部分内容的Hash,下面的函数计算了 文件头尾各1M,中间跳跃100M取10K 以及文件大小的Hash值 public static String CalHash(Str ...
- bt 介绍以及 bt 种子的hash值(特征值)计算
bt种子的hansh值计算,近期忽然对bt种子感兴趣了(原因勿问) 1. bt种子(概念) bt 是一个分布式文件分发协议,每一个文件下载者在下载的同一时候向其他下载者不断的上传已经下载的数据,这样保 ...
- 【转】Java计算文件的hash值
原文地址:http://blog.csdn.net/qq_25646191/article/details/78863110 如何知道一个文件是否改变了呢?当然是用比较文件hash值的方法,文件has ...
- 【Python】 hash值计算 hashlib & hmac
hashlib & hmac *不是很清楚能不能把这种hash值取样算法称之为加密,但是似乎好像也是这么说的哈(非科班出身的野路子就是没这种基本知识的) ■ 基本用法 hashlib支持MD5 ...
- python 通过文件路径获取文件hash值
import hashlib import os,sys def CalcSha1(filepath): with open(filepath,'rb') as f: sha1obj = hashli ...
- HashMap的底层实现以及解决hash值冲突的方式
class HashMap<K,V> extends AbstractMap<K,V> HashMap put() HashMap get() 1.put() HashMa ...
- IOS9.0中hash值的bug与解决方案
事件起因 事情是这样的:产品上线发布,突然出现了问题.运营Gg过来反应,当场给露珠演示,运营同事的手机是iphone,bug确实是存在的.奇怪的是露珠用了其他iphone手机(借别人的,露珠的是吊死安 ...
- geotrellis使用(十七)使用缓冲区分析的方式解决单瓦片计算边缘值问题
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 需求分析 实现方案 总结 一.前言 最 ...
随机推荐
- 从序号和确认号理解TCP三次握手
头部信息 TCP首部存储的数据和建立连接有关,具体每个字段的用途可以参考这一篇文章,其中序号和确认号决定了发送数据的内容. 头部中间部分"保留"和"窗口"中间是 ...
- 何时使用MongoDB而不是MySql
什么是 MySQL 和 MongoDB MySQL 和 MongoDB 是两个可用于存储和管理数据的数据库管理系统.MySQL 是一个关系数据库系统,以结构化表格格式存储数据.相比之下,MongoDB ...
- Room组件的用法
一.Android官方ORM数据库Room Android采用Sqlite作为数据库存储.但由于Sqlite代码写起来繁琐且容易出错,因此Google推出了Room,其实Room就是在Sqlite上面 ...
- Go开始:Go基本元素介绍
本文深入探讨了Go编程语言中的核心概念,包括标识符.关键字.具名函数.具名值.定义类型.类型别名.包和模块管理,以及代码块和断行.这些元素是构成Go程序的基础,也是编写高质量代码的关键. 关注Tech ...
- 【爬虫实战】用python爬豆瓣电影《热烈》短评
目录 一.爬虫对象-豆瓣电影短评 二.爬取结果 三.爬虫代码讲解 三.演示视频 四.获取完整源码 一.爬虫对象-豆瓣电影短评 您好!我是@马哥python说,一名10年程序猿. 今天分享一期爬虫案例, ...
- Oracle的差异增量备份和累积增量备份
在rman增量备份中,有差异增量和累积增量的概念. 差异增量:是备份上级及同级备份以来所有变化的数据块,差异增量是默认增量备份方式累积增量:是备份上级备份以来所有变化的块 累积增量是备份上级备份以来所 ...
- vue框架,input相同标签如何定位-label定位
一.问题提出: 后台前端框架改版,之前是angularjs,现在用vue,导致input标签定位失败,只能定位到第一个input标签,查看后台源代码发现这两个标签是一模一样,如下图: 二.问题思考过程 ...
- SOA认知和方法论
1 前言 1.1 架构分类 在软件设计领域,企业架构通常被划分为如下五种分类: 如何理解架构分类依据及其彼此之间的关系?业务是企业赖以生存之本,因此业务架构是基础.是灵魂,其他一切均是对业务架构的支撑 ...
- 数据结构与算法 | 二叉树(Binary Tree)
二叉树(Binary Tree) 二叉树(Binary Tree)是一种树形数据结构,由节点构成,每个节点最多有两个子节点:一个左子节点和一个右子节点. public class TreeNode { ...
- 详解GuassDB数据库权限命令:GRANT和REVOKE
本文分享自华为云社区<GuassDB数据库的GRANT & REVOKE>,作者: Gauss松鼠会小助手2 . 一.GaussDB的权限概述 在数据库中,对象的创建者将成为该对象 ...