【问题标题】:How to portably compute a sha1 hash in C++?如何在 C++ 中可移植地计算 sha1 哈希?
【发布时间】:2015-04-13 21:04:48
【问题描述】:

目标是计算一个或多个缓冲区的 SHA1 哈希值作为 C++ 程序的一部分。

【问题讨论】:

  • 您需要询问您的具体问题。陈述你的任务并不是一个真正的问题。这就是为什么你的票数越来越少。
  • @DavidSchwartz 可能会出现更具体的问题,以请求 SO 之外的第 3 方资源。为此已标记为关闭。
  • @DavidSchwartz,嗯,非常具体的问题在标题中,不是吗?你的意思是我应该删除需求块吗?我只是添加它以使其更加具体。我真的没有看到我的问题与 - 说 - stackoverflow.com/questions/34490/… ...
  • 这根本不是一个具体的问题。那是一个任务。甚至不清楚您是在寻找编写代码的帮助、编写代码的人,还是向您指出现有代码的人。如果不是后者,这不是问题。如果是后者,则可以通过搜索引擎轻松回答。
  • 我也有同样的问题。我用了一个搜索引擎。它把我带到了这里。我得到了我的答案。我不明白这有什么问题。

标签: c++ hash sha1


【解决方案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,它允许更简洁的代码:stackoverflow.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 用于直接写入字符串对象。

            附加的对象是引用计数的,因此它们会在范围退出时自动销毁。

            【讨论】:

              猜你喜欢
              • 2012-05-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-12-08
              • 2014-02-07
              • 2012-12-02
              相关资源
              最近更新 更多