如何在C ++中可移植地计算sha1哈希?

时间:2015-02-12 22:33:25

标签: c++ hash sha1

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

7 个答案:

答案 0 :(得分:6)

我不确定使用boost的UUID的人是否会正确地在哈希值中执行前导零(你的字符串应始终具有相同的长度),所以这里是一个简化版本的上面的例子可以做到这一点:

#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);
}

答案 1 :(得分:0)

Boost提供SHA1哈希类作为Boost Uuid Library的一部分。虽然它是详细命名空间的一部分,意味着它是一种库私有的,它存在多年并且稳定。

计算文件SHA1哈希并将其打印到stdout的小例子:

前奏:

#include <boost/uuid/sha1.hpp>
#include <boost/detail/endian.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;

主要功能:

int main(int argc, char **argv)
{
  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);
  }
  static_assert(sizeof(unsigned) == 4, "we are assuming 4 bytes here");
  unsigned hash[5] = {0};
  sha1.get_digest(hash);
#ifdef  BOOST_LITTLE_ENDIAN
  for (unsigned i = 0; i < 5; ++i)
    hash[i] = __builtin_bswap32(hash[i]); // GCC builtin
#endif
  boost::algorithm::hex(boost::make_iterator_range(
        reinterpret_cast<const char*>(hash),
        reinterpret_cast<const char*>(hash+5)),
        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哈希也非常便携。

答案 2 :(得分:0)

OpenSSL库是可移植的,高效的,在其他有用的功能中实现SHA1支持。可在大多数平台上使用......

https://www.openssl.org/docs/crypto/sha.html

答案 3 :(得分:0)

OpenSSL库包含一个用于不同散列方法的API,非常便于携带,并且可以在许多系统上使用。

使用推荐的EVP API OpenSSL来计算文件的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

答案 4 :(得分:0)

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)

答案 5 :(得分:0)

Botan库实现了加密算法的“厨房接收器”,包括SHA1(当然)。它可以在提供最新C ++编译器的各种系统之间移植。

计算SHA1哈希值并将其作为十六进制字符串获取是直接使用Botan的高级流类C ++ API来构建管道。

计算文件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代码段:

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})

答案 6 :(得分:0)

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用于直接写入字符串对象。

附加对象被引用计数,以便它们在范围出口处自动销毁。