Generate SHA hash in C++ using OpenSSL library
Asked Answered
I

6

77

How can I generate SHA1 or SHA2 hashes using the OpenSSL libarary?

I searched google and could not find any function or example code.

Inerney answered 28/5, 2009 at 0:45 Comment(0)
W
86

From the command line, it's simply:

printf "compute sha1" | openssl sha1

You can invoke the library like this:

#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

int main()
{
    unsigned char ibuf[] = "compute sha1";
    unsigned char obuf[20];

    SHA1(ibuf, strlen((char *)ibuf), obuf);

    int i;
    for (i = 0; i < 20; i++) {
        printf("%02x ", obuf[i]);
    }
    printf("\n");

    return 0;
}
Willie answered 28/5, 2009 at 5:54 Comment(15)
don't forget to link against libcrypto and libsslAbshire
Use SHA256(ibuf, strlen(ibuf), obuf);, it's more secure.Tinnitus
in c++ your code give ::: 'strlen' : cannot convert parameter 1 from 'unsigned char [13]' to 'const char *'Kirkcudbright
the command line example is incomplete. in order for echo to suppress the trailing newline, you should add '-n' as in: echo -n "compute sha1" | openssl sha1Koheleth
@Tinnitus just saying "use sha256" is bad advice. You don't know the requirements. If we're hashing passwords, for example, it's the wrong hash function, and not significantly more secure. Space or time could be issues, and security could not be an issue, for all we know.Dubose
No idea why this answer was accepted because it doesn't compile.Bein
@Sosukodo I was able to compile it on Ubuntu 12.04 (64bit) using the following command line: gcc -o sha sha.c -lcryptoMoral
@SDX2000 Thanks for following up. I neglected to -lcrypto and now I only get depreciation warning but it compiles.Bein
It doesn't compile because of the strlen, either a reinterpret_cast or a unsigned char overload is needed.Badger
@Sosukodo - The deprecation warnings are due to Apple. Don't use their version.Unlike
@Luis Machuca - OpenSSL is a C project, not a C++ project. Use GCC (not g++ or clang++).Unlike
@Unlike why? And how will one compile if he wants to use OpenSSL in a C++ project?Econometrics
May seem like a stupid question, but why is the length of obuf 20? Could it be longer or shorter, w/o consequences?Izaguirre
@FlareCat SHA1 always produces a 160-bit value (or 20 bytes). en.wikipedia.org/wiki/SHA-1Tantalus
You shouldn't hardcode the obuf size. Use the constant provided by OpenSSL: SHA_DIGEST_LENGTH.Gerkman
A
68

OpenSSL has a horrible documentation with no code examples, but here you are:

#include <openssl/sha.h>

bool simpleSHA256(void* input, unsigned long length, unsigned char* md)
{
    SHA256_CTX context;
    if(!SHA256_Init(&context))
        return false;

    if(!SHA256_Update(&context, (unsigned char*)input, length))
        return false;

    if(!SHA256_Final(md, &context))
        return false;

    return true;
}

Usage:

unsigned char md[SHA256_DIGEST_LENGTH]; // 32 bytes
if(!simpleSHA256(<data buffer>, <data length>, md))
{
    // handle error
}

Afterwards, md will contain the binary SHA-256 message digest. Similar code can be used for the other SHA family members, just replace "256" in the code.

If you have larger data, you of course should feed data chunks as they arrive (multiple SHA256_Update calls).

Anceline answered 14/2, 2010 at 19:34 Comment(3)
The code is good, but it omits return value checking. It could be improved since its high integrity code. A lot of folks will copy/paste it without thinking or checking.Unlike
There is no return value. Just look at the linked documentation.Anceline
Hmm actually my link describes the return values, but the ones I found by googling didn't. Will update the example code. (Of course people copying without thinking is not my problem, more an industry-wide problem ;)Anceline
F
5

Adaptation of @AndiDog version for big file:

static const int K_READ_BUF_SIZE{ 1024 * 16 };

std::optional<std::string> CalcSha256(std::string filename)
{
    // Initialize openssl
    SHA256_CTX context;
    if(!SHA256_Init(&context))
    {
        return std::nullopt;
    }

    // Read file and update calculated SHA
    char buf[K_READ_BUF_SIZE];
    std::ifstream file(filename, std::ifstream::binary);
    while (file.good())
    {
        file.read(buf, sizeof(buf));
        if(!SHA256_Update(&context, buf, file.gcount()))
        {
            return std::nullopt;
        }
    }

    // Get Final SHA
    unsigned char result[SHA256_DIGEST_LENGTH];
    if(!SHA256_Final(result, &context))
    {
        return std::nullopt;
    }

    // Transform byte-array to string
    std::stringstream shastr;
    shastr << std::hex << std::setfill('0');
    for (const auto &byte: result)
    {
        shastr << std::setw(2) << (int)byte;
    }
    return shastr.str();
}
Featureless answered 16/3, 2019 at 13:25 Comment(0)
C
3

correct syntax at command line should be

echo -n "compute sha1" | openssl sha1

otherwise you'll hash the trailing newline character as well.

Calista answered 29/4, 2011 at 21:58 Comment(0)
B
1

Here is OpenSSL example of calculating sha-1 digest using BIO:

#include <openssl/bio.h>
#include <openssl/evp.h>

std::string sha1(const std::string &input)
{
    BIO * p_bio_md  = nullptr;
    BIO * p_bio_mem = nullptr;

    try
    {
        // make chain: p_bio_md <-> p_bio_mem
        p_bio_md = BIO_new(BIO_f_md());
        if (!p_bio_md) throw std::bad_alloc();
        BIO_set_md(p_bio_md, EVP_sha1());

        p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
        if (!p_bio_mem) throw std::bad_alloc();
        BIO_push(p_bio_md, p_bio_mem);

        // read through p_bio_md
        // read sequence: buf <<-- p_bio_md <<-- p_bio_mem
        std::vector<char> buf(input.size());
        for (;;)
        {
            auto nread = BIO_read(p_bio_md, buf.data(), buf.size());
            if (nread  < 0) { throw std::runtime_error("BIO_read failed"); }
            if (nread == 0) { break; } // eof
        }

        // get result
        char md_buf[EVP_MAX_MD_SIZE];
        auto md_len = BIO_gets(p_bio_md, md_buf, sizeof(md_buf));
        if (md_len <= 0) { throw std::runtime_error("BIO_gets failed"); }

        std::string result(md_buf, md_len);

        // clean
        BIO_free_all(p_bio_md);

        return result;
    }
    catch (...)
    {
        if (p_bio_md) { BIO_free_all(p_bio_md); }
        throw;
    }
}

Though it's longer than just calling SHA1 function from OpenSSL, but it's more universal and can be reworked for using with file streams (thus processing data of any length).

Buffalo answered 10/12, 2015 at 12:18 Comment(0)
H
-1

C version of @Nayfe code, generating SHA1 hash from file:

#include <stdio.h>
#include <openssl/sha.h>

static const int K_READ_BUF_SIZE = { 1024 * 16 };
unsigned char* calculateSHA1(char *filename)
{
    if (!filename) {
        return NULL;
    }

    FILE *fp = fopen(filename, "rb");
    if (fp == NULL) {
        return NULL;
    }

    unsigned char* sha1_digest = malloc(sizeof(char)*SHA_DIGEST_LENGTH);
    SHA_CTX context;

    if(!SHA1_Init(&context))
        return NULL;

    unsigned char buf[K_READ_BUF_SIZE];
    while (!feof(fp))
    {
        size_t total_read = fread(buf, 1, sizeof(buf), fp);
        if(!SHA1_Update(&context, buf, total_read))
        {
            return NULL;
        }
    }
    fclose(fp);

    if(!SHA1_Final(sha1_digest, &context))
        return NULL;

    return sha1_digest;
}

It can be used as follows:

unsigned char *sha1digest = calculateSHA1("/tmp/file1");

The res variable contains the sha1 hash.

You can print it on the screen using the following for-loop:

char *sha1hash = (char *)malloc(sizeof(char) * 41);
sha1hash[40] = '\0';
int i;
for (i = 0; i < SHA_DIGEST_LENGTH; i++)
{
    sprintf(&sha1hash[i*2], "%02x", sha1digest[i]);
}
printf("SHA1 HASH: %s\n", sha1hash);
Hybridize answered 21/2, 2020 at 19:36 Comment(3)
Returning a pointer returned by malloc is usually a bad idea. It would be better to pass the output pointer as parameter to your calculateSHA1 function. Why use malloc anyway ? We already know the size of the output. It would be better to write something like that : unsigned char hash_result[SHA_DIGEST_LENGTH]; error = calculateSHA1(filename, hash_result);.Ruel
Also, your sha1hash[41] = '\0'; is 1 byte too far. Usually, calloc is used to zero out strings.Ruel
First of all, this is just an example of C code using the library. Moreover, is from the programmer to deal with the problems that may appear when dealing with pointers, such as their deallocation. You can return a pointer to a memory location or pass a reference, as you did. About calloc, It is a malloc + zero initialization. If we were precious, it would add complexity.The only "error" here would be the wrong offset when closing the string. Already corrected. Thanks.Hybridize

© 2022 - 2024 — McMap. All rights reserved.