How to portably compute a sha1 hash in C++?

不打扰是莪最后的温柔 提交于 2021-01-28 04:50:54

问题


The objective is to compute the SHA1 hash of a buffer or multiple buffers as part of a C++ program.


回答1:


I'm not sure whether the one using boost's UUID will do leading zeros in hash values correctly (your string should always have the same length afaik), so here's a simplified version of the example above which will do that:

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



回答2:


The Qt library contains since version 4.3 the class QCryptographicHash that supports various hashing algorithms, including SHA1. Although Qt is arguably less portable than - say - OpenSSL, at least for projects that already depend on Qt QCryptographicHash is the obvious way to compute a SHA1 hash.

Example program that computes the SHA1 hash of a file:

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

The used Qt classes are all part of Qt core library. An example cmake build file:

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 provides a SHA1 hashing class as part of the Boost Uuid Library. Although it is part of the detail namespace, meaning that it is kind of library-private, it is there for years and stable.

A small example that computes the SHA1 hash of a file and prints it to stdout:

Prelude:

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

The main function:

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

The used parts of Boost don't create dependencies on any boost shared library. Since Boost is quite portable and available for various architectures, using Boost for computing SHA1 hashes is quite portable as well.




回答4:


OpenSSL library is portable, efficient, implements SHA1 support among other useful features. Available on most platforms...

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




回答5:


The OpenSSL library contains an API to different hashing methods and is very portable and readily available on many systems.

An C++ example that uses the recommended EVP API of OpenSSL to compute the SHA1 hash of a file:

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

The prelude of the example:

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

For the EVP API, the program must be linked against libcrypto, e.g.:

g++ -g -std=c++11 sha1_example.cc -lcrypto



回答6:


The Botan library implements 'a kitchen sink' of cryptographic algorithms, including SHA1 (of course). It is portable between various systems that provide a recent C++ compiler.

Computing a SHA1 hash value and obtaining it as hexadecimal string is straight forward using Botan's high-level stream-like C++ API for constructing pipes.

Example for computing the SHA1 hash of a file:

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

When the hash value should be processed as string, an ostringstream (instead of cout) can be used as data sink stream.

Depending on the target system/distribution, the headerfiles might be placed at a slightly unusual location and the library might contain a slightly unexpected suffix (e.g. on Fedora 21). Following cmake snippet accounts for that:

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:


The Crypto++ library is a portable C++ library that includes several cryptographic algorithms, including several hashing algorithms like SHA1.

The API provides various source and sink classes, where a bunch of transformations can be attached in between.

An example for computing the SHA1 hash of a file:

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

It can be compiled via e.g.: g++ -Wall -g -std=c++11 hash_cryptopp.cc -lcryptopp

Crypto++ 'pumps' the content from the source through several attached transformations into the the sink. Instead of the FileSink other sinks are available, e.g. StringSinkTemplate for writing directly into a string object.

The attached objects are reference counted, such that they are automatically destructed on scope exit.



来源:https://stackoverflow.com/questions/28489153/how-to-portably-compute-a-sha1-hash-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!