Monday, February 9, 2015

How to generate a SHA1 hash in C++

Having been put in the position of generating password hashes (again), I thought I better write it down for posterity.
You can always write your own SHA1 implementation, but let's be serious, who wants that? Unless you're doing it for academic purposes, it makes no sense to reinvent the wheel when there are so many good implementations out there.

Among all the available libraries I chose to experiment with OpenSSL and boost.



OpenSSL method 1:

It means using the raw SHA1() function to generate the digest. It is not the OpenSSL recommended way, but it works.
#include <openssl/sha.h>

void makeSHA1raw(const unsigned char text[])
{
    unsigned char hash[SHA_DIGEST_LENGTH];

    SHA1(text, sizeof(text) - 1, hash);

    // do some stuff with the hash
    printf("raw: %s \n", hash);

    char sha1string[SHA_DIGEST_LENGTH*2 +1];
    for(int i = 0; i < SHA_DIGEST_LENGTH; ++i)
    {
        sprintf(&sha1string[i*2], "%02x", (unsigned int)hash[i]);
    }
    printf("string: %s \n", sha1string);
}
OpenSSL method 2:

The recommended way of building a SHA1 digest with OpenSSL: using the EVP_* family of functions.

#include <openssl/evp.h>

void makeSHA1evp(const unsigned char text[])
{
    EVP_MD_CTX *mdctx;
    const EVP_MD *md;
    unsigned char md_value[EVP_MAX_MD_SIZE];
    unsigned int md_len;

    md = EVP_sha1();
    mdctx = EVP_MD_CTX_create();
    EVP_DigestInit_ex(mdctx, md, NULL);
    EVP_DigestUpdate(mdctx, text, sizeof(text)-1);

    EVP_DigestFinal_ex(mdctx, md_value, &md_len);
    EVP_MD_CTX_destroy(mdctx);
    printf("Digest is: ");
    for(int i = 0; i < md_len; i++){
        printf("%02x", md_value[i]);
    }
    printf("\n");
    EVP_cleanup();
}

Boost:

Everybody uses boost by now so it makes sense to make hashes with it. It avoids adding additional dependencies.

#include <iostream>
#include <boost/uuid/sha1.hpp>

void makeSHA1boostV1(const unsigned char text[])
{
    boost::uuids::detail::sha1 sha1;
    unsigned int hash[5];
    sha1.process_bytes(text, sizeof(text)-1);
    sha1.get_digest(hash);

    std::cout << "Hash: ";
    for(std::size_t i=0; i<sizeof(hash)/sizeof(hash[0]); ++i) {
        std::cout << std::hex <<hash[i];
    }
    std::cout << std::endl;
}

Bonus - adding some salt:

Salting is always recommended when hashing passwords so here's an easy way to get unique random data using boost::uuids.

#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

//...

boost::uuids::random_generator gen;
boost::uuids::uuid salt = gen();
//remove the '-' character
std::string ssalt = boost::uuids::to_string(salt);
ssalt.erase(std::remove(ssalt.begin(), ssalt.end(), '-'), ssalt.end());

std::cout << "salt: " << ssalt <<std::endl;

Now, all that you need is to add the salt to the password, or whatever you wanted to hash.

1 comment:

  1. These are quite useful and I've choosed the boost version for my project. However when using ostream, the leading zeros of numbers will be ignored, thus leads to invalid hash strings sometimes. To prevent such situation, setw and setfill should be used.

    ReplyDelete