65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 CryptoAPI 的 C++ SHA1 和 MD5 实现

starIconstarIconstarIconstarIconstarIcon

5.00/5 (22投票s)

2012年9月20日

CPOL

4分钟阅读

viewsIcon

97069

downloadIcon

8461

本文介绍了一种使用 Microsoft CryptoAPI 库在 Windows 上计算哈希值(SHA1、MD5、MD4 和 MD2)的 C++ 实现。

引言

加密哈希函数广泛用于保护通信安全、在数据库中存储密码的哈希值、验证消息或文件在两个实体之间是否正确传输等。最广泛使用的算法之一是 MD5(Message Digest Five),它生成一个 128 位哈希值,通常表示为 32 个十六进制数字。它是 MD4MD2 的后继者,所有这些算法都由 Ronald Rivest 开发。另一种基于 MD4 的哈希算法是 SHA-1,这是一种加密安全的单向哈希算法,可生成 160 位消息摘要(通常表示为 40 个十六进制数字)。本文介绍了一种使用 Microsoft CryptoAPI 库计算哈希值(SHA1、MD5、MD4 和 MD2)的 C++ 实现。CryptoAPI 最低要求 Windows XP 或 Windows Server 2003。

注意:MD5 和 SHA1 都已被证明存在弱点。MD5 不具备抗碰撞性,并存在其他弱点,这使其不适用于至少一些安全应用,包括 SSL 证书。US-CERT 已得出结论,MD5“应被认为在加密上已失效,不再适用于进一步使用”。SHA1 的发布者美国国家标准与技术研究院目前也在寻找 SHA1 的替代品。

一些 CryptoAPI 考虑因素

为了生成哈希值,必须遵循以下步骤:

  • 初始化加密操作的上下文
    • CryptAcquireContext:获取对特定加密服务提供商中密钥容器的句柄;该句柄用于后续的加密 API 调用。
  • 创建哈希对象
    • CryptCreateHash:为哈希数据流创建哈希对象;返回一个用于后续哈希数据或会话密钥的句柄。
  • 哈希数据
    • CryptHashData:将数据添加到先前创建的哈希对象中。可以多次调用以向哈希对象添加更多数据。
  • 检索哈希值
    • CryptGetHashValue:从哈希对象中检索数据。要检索哈希值,必须首先调用此方法并传入 HP_HASHSIZE 以确定哈希值的大小,分配哈希值内存,然后调用它并传入 HP_HASHVAL 来获取哈希值。
  • 通过销毁 hash 对象和释放加密上下文来清理
    • CryptDestroyHash:销毁先前使用 CryptCreateHash 创建的哈希对象。
    • CryptReleaseContext:释放先前通过调用 CryptAcquireContext 获取的加密服务提供商和密钥容器的句柄。

消息摘要实现

我的实现包含几个模板 C++ 类,它们提供:cryptohash_t 是为数据流提供哈希功能的类,而 cryptohash_helper_t 是一个辅助类,为哈希测试和文件提供简化的 API。这两个类都以哈希算法为参数。

   template <ALG_ID algorithm>

   class cryptohash_t;
   
   template <ALG_ID algorithm>
   class cryptohash_helper_t;

提供了几个 typedefs 以简化这些模板类的使用。

   typedef cryptohash_t<CALG_MD2> md2_t;
   typedef cryptohash_t<CALG_MD4> md4_t;
   typedef cryptohash_t<CALG_MD5> md5_t;
   typedef cryptohash_t<CALG_SHA1> sha1_t;

   typedef cryptohash_helper_t<CALG_MD2> md2_helper_t;
   typedef cryptohash_helper_t<CALG_MD4> md4_helper_t;
   typedef cryptohash_helper_t<CALG_MD5> md5_helper_t;
   typedef cryptohash_helper_t<CALG_SHA1> sha1_helper_t;

cryptohash_t 具有以下 public 接口:

  • bool begin()
    初始化哈希例程,创建用于计算数据流哈希值的哈希对象。如果函数失败,请检查最后一个错误。
  • bool update(unsigned char* const buffer, size_t size)
    向当前哈希添加更多数据。如果函数失败,请检查最后一个错误。
  • bool finalize()
    计算当前哈希对象的哈希值并销毁哈希对象。如果函数失败,请检查最后一个错误。
  • hash_t digest() const
    以二进制格式检索计算出的哈希值。
  • std::string hexdigest(bool uppercase = false) const
    以十六进制数字字符串的形式检索计算出的哈希值。
  • errorinfo_t lasterror() const
    返回最后一个错误。

cryptohash_helper_t 具有以下 public 接口:

  • hash_t digesttext(std::string const& text)
    计算并以二进制格式返回文本的哈希值。
  • std::string hexdigesttext(std::string const& text, bool uppercase = false)
    计算并以十六进制数字字符串形式返回文本的哈希值。
  • hash_t digestfile(std::string const& filename)
    计算并以二进制格式返回文件的哈希值。
  • std::string hexdigestfile(std::string const& filename, bool uppercase = false)
    计算并以十六进制数字字符串形式返回文件的哈希值。
  • errorinfo_t lasterror() const
    返回以上任何函数的最后一个错误。

示例

注意:在以下所有示例中,您可以直接使用任何提供的哈希类型(md5_tmd4_tmd2_tsha1_t)或辅助类型(md5_helper_tmd4_helper_tmd2_helper_tsha1_helper_t),而无需对示例进行任何其他更改,即可获得相应的结果。

计算字符串的 SHA-1

std::string text = "mariusbancila";
std::string digest;

sha1_t hasher;
if(hasher.begin())
{
  if(hasher.update((unsigned char*)(text.c_str()), text.length()))
  {
    if(hasher.finalize())
    {
      digest = hasher.hexdigest();
    }
  }
}

if(digest.empty())
  std::cout << "error code = " << hasher.lasterror().errorCode 
            << ", error message = " << hasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

计算多个字符串的 SHA-1

std::string text1 = "marius";
std::string text2 = "bancila";
std::string digest;

sha1_t hasher;
if(hasher.begin())
{
  if(hasher.update((unsigned char*)(text1.c_str()), text1.length()))
  {
    if(hasher.update((unsigned char*)(text2.c_str()), text2.length()))
    {
      if(hasher.finalize())
      {
        digest = hasher.hexdigest();
      }
    }
  }
}

if(digest.empty())
  std::cout << "error code = " << hasher.lasterror().errorCode 
            << ", error message = " << hasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

使用辅助类计算字符串的 MD5

md5_helper_t hhasher;
std::string digest = hhasher.hexdigesttext("mariusbancila");

if(digest.empty())
  std::cout << "error code = " << hhasher.lasterror().errorCode 
            << ", error message = " << hhasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

使用辅助类计算文件的 MD5

md5_helper_t hhasher;
std::string digest = hhasher.hexdigestfile("c:\\temp\\sample.png");

if(digest.empty())
  std::cout << "error code = " << hhasher.lasterror().errorCode 
            << ", error message = " << hhasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

演示应用程序

在项目中,您可以找到一个演示应用程序(使用 Visual Studio 2008 SP1 和 MFC 编写)的源代码和二进制文件,该应用程序使用这些哈希模板类来生成文本或选定文件的哈希值(SHA1、MD5、MD4 和 MD2)。使用该应用程序非常简单,您可以在下面看到一个屏幕截图。

附加阅读材料

历史

  • 2012 年 9 月 20 日:初始版本
© . All rights reserved.