如何在 C++ 中可移植地计算 sha1 哈希?
Posted
技术标签:
【中文标题】如何在 C++ 中可移植地计算 sha1 哈希?【英文标题】:How to portably compute a sha1 hash in C++? 【发布时间】:2015-04-13 21:04:48 【问题描述】:目标是计算一个或多个缓冲区的 SHA1 哈希值作为 C++ 程序的一部分。
【问题讨论】:
您需要询问您的具体问题。陈述你的任务并不是一个真正的问题。这就是为什么你的票数越来越少。 @DavidSchwartz 可能会出现更具体的问题,以请求 SO 之外的第 3 方资源。为此已标记为关闭。 @DavidSchwartz,嗯,非常具体的问题在标题中,不是吗?你的意思是我应该删除需求块吗?我只是添加它以使其更加具体。我真的没有看到我的问题与 - 说 - ***.com/questions/34490/… ... 这根本不是一个具体的问题。那是一个任务。甚至不清楚您是在寻找编写代码的帮助、编写代码的人,还是向您指出现有代码的人。如果不是后者,这不是问题。如果是后者,则可以通过搜索引擎轻松回答。 我也有同样的问题。我用了一个搜索引擎。它把我带到了这里。我得到了我的答案。我不明白这有什么问题。 【参考方案1】:我不确定使用 boost 的 UUID 的人是否会正确地在哈希值中使用前导零(您的字符串应该始终具有相同的长度 afaik),所以这是上面示例的简化版本,可以做到这一点:
#include <cstdio>
#include <string>
#include <boost/uuid/sha1.hpp>
std::string get_sha1(const std::string& p_arg)
boost::uuids::detail::sha1 sha1;
sha1.process_bytes(p_arg.data(), p_arg.size());
unsigned hash[5] = 0;
sha1.get_digest(hash);
// Back to string
char buf[41] = 0;
for (int i = 0; i < 5; i++)
std::sprintf(buf + (i << 3), "%08x", hash[i]);
return std::string(buf);
【讨论】:
您的意思是您不确定 boost hex 算法是否打印前导零?这个minimal example shows that it does。顺便说一句,您的 sprintf 方法在小端架构上不会产生相同的结果。这意味着如果您像这样使用 sprintf ,则不能进行字节交换。另一个版本交换字节,因为十六进制算法在 4 字节整数上逐字节迭代。 是的,你是对的,我删除了 bswap32() 调用,谢谢。现在产生的结果与 Python 的 hashlib.sha1(str).hexdigest() 相同。 事实证明,Boost 已经为 SHA1 哈希字符串获得了一个更高级的 API,它允许更简洁的代码:***.com/a/28489154/427158【参考方案2】:Qt 库从 4.3 版开始包含类 QCryptographicHash,它支持各种散列算法,包括 SHA1。尽管 Qt 的可移植性可以说不如 OpenSSL,但至少对于已经依赖 Qt 的项目来说,QCryptographicHash 是计算 SHA1 哈希的明显方法。
计算文件的 SHA1 哈希的示例程序:
#include <QCryptographicHash>
#include <QByteArray>
#include <QFile>
#include <iostream>
#include <stdexcept>
using namespace std;
int main(int argc, char **argv)
try
if (argc < 2)
throw runtime_error(string("Call: ") + *argv + string(" FILE"));
const char *filename = argv[1];
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
throw runtime_error("Could not open: " + string(filename));
QCryptographicHash hash(QCryptographicHash::Sha1);
vector<char> v(128*1024);
for (;;)
qint64 n = file.read(v.data(), v.size());
if (!n)
break;
if (n == -1)
throw runtime_error("Read error");
hash.addData(v.data(), n);
QByteArray h(hash.result().toHex());
cout << h.data() << '\n';
catch (const exception &e)
cerr << "Error: " << e.what() << '\n';
return 1;
return 0;
使用的 Qt 类都是 Qt 核心库的一部分。一个示例 cmake 构建文件:
cmake_minimum_required(VERSION 2.8.11)
project(hash_qt CXX)
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -Wall -std=c++11")
find_package(Qt5Core)
add_executable(hash_qt hash_qt.cc)
target_link_libraries(hash_qt Qt5::Core)
【讨论】:
【参考方案3】:Boost 提供了一个简单的 API 来计算字符串的 SHA1 哈希:
#include <iostream>
#include <string>
#include <boost/compute/detail/sha1.hpp>
int main(int argc, char **argv)
if (argc < 2)
std::cerr << "Call: " << *argv << " STR\n";
return 1;
boost::compute::detail::sha1 sha1 argv[1] ;
std::string s sha1 ;
std::cout << s << '\n';
return 0;
不过,该 API 对 Boost Compute library 是私有的,因为它是详细名称空间的一部分。这意味着它没有任何稳定性保证。
Boost 还提供了一个 SHA1 散列类作为 Boost Uuid Library 的一部分,其 API 更适合散列任意二进制输入,例如文件。虽然它是 detail 命名空间的一部分,这意味着它是一种库私有的,但它存在多年且稳定。
一个计算文件的 SHA1 哈希并将其打印到标准输出的小示例:
前奏:
#include <boost/uuid/detail/sha1.hpp>
#include <boost/predef/other/endian.h>
#include <boost/endian/conversion.hpp>
#include <boost/algorithm/hex.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <iostream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
using namespace std;
主要功能:
if (argc < 2) cerr << "Call: " << *argv << " FILE\n"; return 1;
const char *filename = argv[1];
int fd = open(filename, O_RDONLY);
if (fd == -1) cerr << "open: " << strerror(errno) << ")\n"; return 1;
vector<char> v(128*1024);
boost::uuids::detail::sha1 sha1;
for (;;)
ssize_t n = read(fd, v.data(), v.size());
if (n == -1)
if (errno == EINTR) continue;
cerr << "read error: " << strerror(errno) << '\n';
return 1;
if (!n) break;
sha1.process_bytes(v.data(), n);
boost::uuids::detail::sha1::digest_type hash;
sha1.get_digest(hash);
#ifdef BOOST_ENDIAN_BIG_BYTE
for (unsigned i = 0; i < sizeof hash / sizeof hash[0]; ++i)
boost::endian::endian_reverse_inplace(hash[i]);
#endif
boost::algorithm::hex(boost::make_iterator_range(
reinterpret_cast<const char*>(hash),
reinterpret_cast<const char*>(hash) + sizeof hash),
std::ostream_iterator<char>(cout)); cout << '\n';
int r = close(fd);
if (r == -1) cerr << "close error: " << strerror(errno) << '\n';
return 1;
return 0;
Boost 的使用部分不会对任何 boost 共享库创建依赖关系。由于 Boost 非常便携并且可用于各种架构,因此使用 Boost 计算 SHA1 哈希值也非常便携。
【讨论】:
【参考方案4】:OpenSSL 库是可移植的、高效的、实现 SHA1 支持以及其他有用的功能。在大多数平台上都可用...
https://www.openssl.org/docs/crypto/sha.html
【讨论】:
【参考方案5】:OpenSSL 库包含一个用于不同哈希方法的 API,并且非常便携,并且在许多系统上都可以轻松使用。
使用推荐的 OpenSSL EVP API 计算文件的 SHA1 哈希的 C++ 示例:
int main(int argc, char **argv)
try
if (argc < 2) throw runtime_error(string("Call: ") + *argv
+ string(" FILE"));
const char *filename = argv[1];
int fd = open(filename, O_RDONLY);
if (fd == -1) throw runtime_error("Could not open " + string(filename)
+ " (" + string(strerror(errno)) + ")");
BOOST_SCOPE_EXIT(&fd) close(fd); BOOST_SCOPE_EXIT_END
const EVP_MD *md = EVP_sha1();
if (!md) throw logic_error("Couldn't get SHA1 md");
unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX*)> md_ctx(EVP_MD_CTX_create(),
EVP_MD_CTX_destroy);
if (!md_ctx) throw logic_error("Couldn't create md context");
int r = EVP_DigestInit_ex(md_ctx.get(), md, 0);
if (!r) throw logic_error("Could not init digest");
vector<char> v(128*1024);
for (;;)
ssize_t n = read(fd, v.data(), v.size());
if (n == -1)
if (errno == EINTR)
continue;
throw runtime_error(string("read error: ") + strerror(errno));
if (!n)
break;
int r = EVP_DigestUpdate(md_ctx.get(), v.data(), n);
if (!r) throw logic_error("Digest update failed");
array<unsigned char, EVP_MAX_MD_SIZE> hash;
unsigned int n = 0;
r = EVP_DigestFinal_ex(md_ctx.get(), hash.data(), &n);
if (!r) throw logic_error("Could not finalize digest");
boost::algorithm::hex(boost::make_iterator_range(
reinterpret_cast<const char*>(hash.data()),
reinterpret_cast<const char*>(hash.data()+n)),
std::ostream_iterator<char>(cout));
cout << '\n';
catch (const exception &e)
cerr << "Error: " << e.what() << '\n';
return 1;
return 0;
例子的前奏:
#include <openssl/evp.h>
#include <boost/algorithm/hex.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/scope_exit.hpp>
#include <iostream>
#include <vector>
#include <array>
#include <memory>
#include <string>
#include <stdexcept>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
using namespace std;
对于 EVP API,程序必须与 libcrypto 链接,例如:
g++ -g -std=c++11 sha1_example.cc -lcrypto
【讨论】:
【参考方案6】:Botan 库实现了加密算法的“厨房水槽”,包括 SHA1(当然)。它可以在提供最新 C++ 编译器的各种系统之间移植。
使用 Botan 用于构建管道的高级流式 C++ API 直接计算 SHA1 哈希值并将其作为十六进制字符串获取。
计算文件的 SHA1 哈希的示例:
#include <botan/pipe.h>
#include <botan/basefilt.h>
#include <botan/filters.h>
#include <botan/data_snk.h>
using namespace Botan;
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
try
if (argc < 2)
throw runtime_error(string("Call: ") + *argv + string(" FILE"));
const char *filename = argv[1];
ifstream in(filename, ios::binary);
in.exceptions(ifstream::badbit);
Pipe pipe(new Chain(new Hash_Filter("SHA-1"),
new Hex_Encoder(Hex_Encoder::Lowercase)),
new DataSink_Stream(cout));
pipe.start_msg();
in >> pipe;
pipe.end_msg();
catch (const exception &e)
cerr << "Error: " << e.what() << '\n';
return 1;
return 0;
当哈希值作为字符串处理时,可以使用ostringstream
(而不是cout)作为数据接收器流。
根据目标系统/发行版,头文件可能放置在稍微不寻常的位置,并且库可能包含稍微意外的后缀(例如在 Fedora 21 上)。以下 cmake sn-p 说明了这一点:
cmake_minimum_required(VERSION 2.8.11)
project(hash CXX)
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -Wall -std=c++11")
find_library(LIB_BOTAN NAMES botan botan-1.10)
find_path(HEADER_BOTAN NAMES botan/pipe.h PATH_SUFFIXES botan-1.10)
add_executable(hash_botan hash_botan.cc)
set_property(TARGET hash_botan PROPERTY INCLUDE_DIRECTORIES $HEADER_BOTAN)
target_link_libraries(hash_botan $LIB_BOTAN)
【讨论】:
【参考方案7】:Crypto++ 库是一个可移植的 C++ 库,其中包含多种加密算法,包括 SHA1 等多种哈希算法。
API 提供各种源和接收器类,其中可以附加一堆转换。
计算文件的 SHA1 哈希的示例:
#include <cryptopp/files.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <cryptopp/sha.h>
using namespace CryptoPP;
#include <iostream>
using namespace std;
int main(int argc, char **argv)
try
if (argc < 2)
throw runtime_error(string("Call: ") + *argv + string(" FILE"));
const char *filename = argv[1];
SHA1 sha1;
FileSource source(filename, true,
new HashFilter(sha1,
new HexEncoder(new FileSink(cout), false, 0, ""),
false)
);
catch (const exception &e)
cerr << "Error: " << e.what() << '\n';
return 1;
return 0;
可以通过例如:g++ -Wall -g -std=c++11 hash_cryptopp.cc -lcryptopp
进行编译
Crypto++ 通过几个附加的转换将内容从源“泵送”到接收器中。除了FileSink
,还可以使用其他接收器,例如StringSinkTemplate
用于直接写入字符串对象。
附加的对象是引用计数的,因此它们会在范围退出时自动销毁。
【讨论】:
以上是关于如何在 C++ 中可移植地计算 sha1 哈希?的主要内容,如果未能解决你的问题,请参考以下文章