Generate sha256 with OpenSSL and C++
Asked Answered
A

11

77

I'm looking to create a hash with sha256 using openssl and C++. I know there's a similar post at Generate SHA hash in C++ using OpenSSL library, but I'm looking to specifically create sha256.

UPDATE:

Seems to be a problem with the include paths. It can't find any OpenSSL functions even though I included

#include "openssl/sha.h"

and I included the paths in my build

-I/opt/ssl/include/ -L/opt/ssl/lib/ -lcrypto 
Alcazar answered 14/2, 2010 at 19:14 Comment(6)
Also as a bonus, it would be nice if it would output the hash in binary :)Alcazar
I posted a new answer there which explains what you want. You could close this question as duplicate if that answer helps.Hora
@Hora - Everything seems to work right, except the compiler cannot find the functions. It could not even find a reference to SHA1. Also coudln't find any of the SHA256 functions like `SHA256_Final'. Not sure what I'm doing wrong, I included #include "openssl/sha.h" and I included the include and library during the compilation -I/opt/ssl/include/ -L/opt/ssl/lib/ -lcryptoAlcazar
As you can see in the header (google.com/codesearch/p?hl=en#2CnO_mGaYOA/source/…), the SHA256 functions are only defined if OpenSSL is compiled with it. So check the value of OPENSSL_NO_SHA256 to see what's wrong. The -lcrypto parameter should be correct.Hora
It also doesn't work with any sha1 functions either :/Alcazar
Ok solved my own problem. I just used #include <openssl/sha.h> and without -I/opt/ssl/include/ -L/opt/ssl/lib/Alcazar
E
85

Here's how I did it:

void sha256_hash_string (unsigned char hash[SHA256_DIGEST_LENGTH], char outputBuffer[65])
{
    int i = 0;

    for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }

    outputBuffer[64] = 0;
}


void sha256_string(char *string, char outputBuffer[65])
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, string, strlen(string));
    SHA256_Final(hash, &sha256);
    int i = 0;
    for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }
    outputBuffer[64] = 0;
}

int sha256_file(char *path, char outputBuffer[65])
{
    FILE *file = fopen(path, "rb");
    if(!file) return -534;

    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    const int bufSize = 32768;
    unsigned char *buffer = malloc(bufSize);
    int bytesRead = 0;
    if(!buffer) return ENOMEM;
    while((bytesRead = fread(buffer, 1, bufSize, file)))
    {
        SHA256_Update(&sha256, buffer, bytesRead);
    }
    SHA256_Final(hash, &sha256);

    sha256_hash_string(hash, outputBuffer);
    fclose(file);
    free(buffer);
    return 0;
}

It's called like this:

static unsigned char buffer[65];
sha256("string", buffer);
printf("%s\n", buffer);
Electrophoresis answered 16/3, 2010 at 21:43 Comment(9)
Hi, for everyone using the great QT :) - You can also use this, just add to your project file LIBS +=-lcrypto and then you can just convert the code to a class and everything will work fine ;)Hydrotropism
-1: “SHA1_Init(), SHA1_Update() and SHA1_Final() return 1 for success, 0 otherwise.”, openssl.org/docs/crypto/sha.htm.Epochmaking
@noloader Irrelevant since these functions aren’t used here.Overslaugh
@Konrad Rudolph Its the same API interface, and the OpenSSL man pages lead back to the SHA1 stuff. Ignoring return values in high integrity software is very bad karma.Epochmaking
What the heck is sha256_hash_string(...) looks like you reference a non-existent function in your code...Broadfaced
May I ask how to solve the 'byte' in second function was not declaredRequiem
byte is just as the name says, a byte (can be typedef'd with an unsigned char). sha256_hash_string I think he means the function sha256 he previously definedExhibit
It's even better practice to use std::byte, but this code looks like C++98 only.Terni
'SHA256_Init, SHA256_Update, etc.. are all deprecated ... :(Dupree
W
74

std based

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

using namespace std;

#include <openssl/sha.h>
string sha256(const string &str)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, str.c_str(), str.size());
    SHA256_Final(hash, &sha256);
    stringstream ss;
    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        ss << hex << setw(2) << setfill('0') << (int)hash[i];
    }
    return ss.str();
}

int main() {
    cout << sha256("1234567890_1") << endl;
    cout << sha256("1234567890_2") << endl;
    cout << sha256("1234567890_3") << endl;
    cout << sha256("1234567890_4") << endl;
    return 0;
}
Wadsworth answered 17/5, 2012 at 9:8 Comment(9)
Haven't tested this out, but this definitely looks cleaner than all the other "C++" versions.Alcazar
This code compiles and produced the expected output. On ubuntu, you can use: sudo apt-get install libssl-dev && g++ -lcrypto main.cc to compile it.Phratry
This solution is actually better than the accepted one, as ostringstream is much, much safer than messing with arrays and sprintf.Ratchet
According to the OpenSSL docs, "The EVP interface to message digests should almost always be used in preference to the low level interfaces. This is because the code then becomes transparent to the digest used and much more flexible." So keep that in mind when deciding to use this code, i.e. make sure you're positive that you'll only ever use SHA256. Otherwise, it's more worth your time to learn the EVP interface.Improvement
@Phratry for g++, technically the dependency should be after the src file that depends on it (caused complication issues for me on Ubuntu 16.04.1). Should be: g++ main.cc -lcryptoHoraciohorae
this code does not check for errors, It is a shot on the foot do not copy paste with out making the appropriate UpdateJasisa
Sorry,I know its very old. The prototype of SHA256 is unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); as mentioned here sha. May be i am referring old or not sure. As I need to calculate SHA256 of certain files , can you point me in right direction of openssl libs based on CDiaphysis
What's SHA256_DIGEST_LENGTH supposed to be?Cryoscopy
@Cryoscopy the constant is 32 and defined hereWadsworth
I
41

Using OpenSSL's EVP interface (the following is for OpenSSL 1.1):

#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <memory>
#include <openssl/evp.h>

struct OpenSSLFree {
    void operator()(void* ptr) {
        EVP_MD_CTX_free((EVP_MD_CTX*)ptr);
    }
};

template <typename T>
using OpenSSLPointer = std::unique_ptr<T, OpenSSLFree>;

bool computeHash(const std::string& unhashed, std::string& hashed) {
    OpenSSLPointer<EVP_MD_CTX> context(EVP_MD_CTX_new());

    if(context.get() == NULL) {
        return false;
    }

    if(!EVP_DigestInit_ex(context.get(), EVP_sha256(), NULL)) {
        return false;
    }

    if(!EVP_DigestUpdate(context.get(), unhashed.c_str(), unhashed.length())) {
        return false;
    }

    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int lengthOfHash = 0;

    if(!EVP_DigestFinal_ex(context.get(), hash, &lengthOfHash)) {
        return false;
    }

    std::stringstream ss;
    for(unsigned int i = 0; i < lengthOfHash; ++i)
    {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }

    hashed = ss.str();
    return true;
}

int main(int, char**)
{
    std::string pw1 = "password1", pw1hashed;
    std::string pw2 = "password2", pw2hashed;
    std::string pw3 = "password3", pw3hashed;
    std::string pw4 = "password4", pw4hashed;

    computeHash(pw1, pw1hashed);
    computeHash(pw2, pw2hashed);
    computeHash(pw3, pw3hashed);
    computeHash(pw4, pw4hashed);

    std::cout << pw1hashed << std::endl;
    std::cout << pw2hashed << std::endl;
    std::cout << pw3hashed << std::endl;
    std::cout << pw4hashed << std::endl;

    return 0;
}

The advantage of this higher level interface is that you simply need to swap out the EVP_sha256() call with another digest's function, e.g. EVP_sha512(), to use a different digest. So it adds some flexibility.

Improvement answered 20/10, 2016 at 13:32 Comment(4)
if statements without else clauses ... so ... deep ... cannot see light ... Upvoted nonetheless because it uses the recommended EVP interface!Ilona
@LimitedAtonement Indeed, I left "proper" error checking as an exercise to the end developer :) but at a minimum, I made sure I did all the if-checks just to highlight where they're required. Thanks for the +1!Improvement
for anybody else coming here: EVP_MD_CTX could be a unique ptr with custom deleter . much cleaner code. just to nitpick here. std::unique_ptr<EVP_MD_CTX, std::function<void(EVP_MD_CTX*)>> mdCtx(EVP_MD_CTX_new(), [](EVP_MD_CTX* g) { EVP_MD_CTX_free(g); });Aerography
I wonder if using std::stringstream is slower than the other solutions which are using C-style char arrays and sprintf?Graehl
L
6

With the OpenSSL update to 3.0 most of the solution won't work (as the APIs deprecated) and it's recommended to use the EVP functions. Below code shows my take with EVP based on OpenSSL's official documentation

This implementation uses pure C and verified on Ubuntu 22.04

/**
 * Simple program to calculate message digest using openssl'l new EVP method
 *
 * Verified on Ubuntu 22.04 on Jan 29, 2023
 *
 * Install dependency:
 * sudo apt install make gcc libssl-dev -y
 *
 * To compile:
 * gcc sha256sum.c -o shasum.out -lcrypto
 *
 * To cross verify:
 * echo -n "Hello world" | sha256sum
 *
 * Author: Daniel Selvan D. <[email protected]>
 */

#include <stdio.h>
#include <string.h>

#include <openssl/evp.h>

// OpenSSL engine implementation
#define OPENSSL_ENGINE NULL

/**
 * Returns the SHA256 value of the input string
 *
 * @param string input string for which the hash to be calculated
 * @returns string (32 bytes) - SHA256 hash
 */
static const unsigned char *getShaSum(const unsigned char *string)
{
    EVP_MD_CTX *mdCtx = EVP_MD_CTX_new();
    unsigned char mdVal[EVP_MAX_MD_SIZE], *md;
    unsigned int mdLen, i;

    if (!EVP_DigestInit_ex(mdCtx, EVP_sha256(), OPENSSL_ENGINE))
    {
        printf("Message digest initialization failed.\n");
        EVP_MD_CTX_free(mdCtx);
        exit(EXIT_FAILURE);
    }

    // Hashes cnt bytes of data at d into the digest context mdCtx
    if (!EVP_DigestUpdate(mdCtx, string, strlen((const char *)string)))
    {
        printf("Message digest update failed.\n");
        EVP_MD_CTX_free(mdCtx);
        exit(EXIT_FAILURE);
    }

    if (!EVP_DigestFinal_ex(mdCtx, mdVal, &mdLen))
    {
        printf("Message digest finalization failed.\n");
        EVP_MD_CTX_free(mdCtx);
        exit(EXIT_FAILURE);
    }
    EVP_MD_CTX_free(mdCtx);

    printf("DEBUG: Digest is: ");
    for (i = 0; i < mdLen; i++)
        printf("%02x", mdVal[i]);
    printf("\n");

    md = mdVal;

    return md;
}

int main()
{
    // To calculate the hash of a file, read it and pass the pointer
    getShaSum("Hello world");

    exit(EXIT_SUCCESS);
}

The advantage of this higher level interface is that you simply need to swap out the EVP_sha256() call with another digest's function, e.g. EVP_sha512(), to use a different digest.

Laudatory answered 29/1, 2023 at 9:35 Comment(0)
C
5

the functions "SHA256_Init" "SHA256_Update" "SHA256_Final" is deprecated since openssl 3.0, this is the new way to get sha256

#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include "openssl/sha.h"

using namespace std;

string sha256(const string inputStr)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    const unsigned char* data = (const unsigned char*)inputStr.c_str();
    SHA256(data, inputStr.size(), hash);
    stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        ss << hex << setw(2) << setfill('0') << (int)hash[i];
    }
    return ss.str();
}
int main() {
    cout << sha256("test") << endl;
    cout << sha256("test2") << endl;
    return 0;
}
Cordero answered 31/10, 2023 at 16:8 Comment(1)
How should people use this approach when they have a file that is being read in chunks? SHA256_Update(..) was the hero for such cases.Solarize
D
2

If you want to compute the sha256 hash of a file...

auto sha256 = [](std::string fname, 
                 std::vector<unsigned char>& hash) -> bool {
   std::unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)>
            evpCtx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
   EVP_DigestInit_ex(evpCtx.get(), EVP_sha256(), nullptr);

   constexpr size_t buffer_size { 1 << 12 };
   std::vector<char> buffer(buffer_size,'\0');

   std::ifstream fp(fname, std::ios::binary);
   if (!fp.is_open()) {
        std::cerr << "Unable to open '" << fname << "'!\n";
        return false;
   }
   while (fp.good()) {
        fp.read(buffer.data(), buffer_size);
        EVP_DigestUpdate (evpCtx.get(), buffer.data(), fp.gcount());
   }
   fp.close();

   hash.resize(SHA256_DIGEST_LENGTH);
   std::fill(hash.begin(), hash.end(), 0);
   unsigned int len;
   EVP_DigestFinal_ex (evpCtx.get(), hash.data(), &len);

   return true;
};

...

std::vector<unsigned char> hash;
sha256("/etc/profile", hash);
std::stringstream out;
for (size_t i = 0; i < hash.size(); i++)
    out << std::setfill('0') << std::setw(2) 
        << std::hex << int(hash[i]);
std::string hashStr = out.str();
std::cout << hashStr << std::endl;
...
a3fe9f414586c0d3cacbe3b6920a09d8718e503bca22e23fef882203bf765065
Dupree answered 3/12, 2022 at 23:44 Comment(0)
D
1

Here's the function I personally use - I simply derived it from the function I used for sha-1 hashing:

char *str2sha256( const char *str, int length ) {
  int n;
  SHA256_CTX c;
  unsigned char digest[ SHA256_DIGEST_LENGTH ];
  char *out = (char*) malloc( 33 );

  SHA256_Init( &c );

  while ( length > 0 ) {
    if ( length > 512 ) SHA256_Update( &c, str, 512 );
    else SHA256_Update( &c, str, length );

    length -= 512;
    str += 512;
  }

  SHA256_Final ( digest, &c );

  for ( n = 0; n < SHA256_DIGEST_LENGTH; ++n )
    snprintf( &( out[ n*2 ] ), 16*2, "%02x", (unsigned int) digest[ n ] );

  return out;
}
Diorite answered 13/8, 2020 at 14:53 Comment(0)
L
1

I really liked @villapx reply but my edit was rejected so I will post modern c++ take on handling pointer lifecycle for his implementation.

#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <memory>
#include <openssl/evp.h>

struct OpenSSLFree {
    void operator()(void* ptr) {
        EVP_MD_CTX_free((EVP_MD_CTX*)ptr);
    }
};

template <typename T>
using OpenSSLPointer = std::unique_ptr<T, OpenSSLFree>;

bool computeHash(const std::string& unhashed, std::string& hashed) {
    OpenSSLPointer<EVP_MD_CTX> context(EVP_MD_CTX_new());

    if(context.get() == NULL) {
        return false;
    }

    if(!EVP_DigestInit_ex(context.get(), EVP_sha256(), NULL)) {
        return false;
    }

    if(!EVP_DigestUpdate(context.get(), unhashed.c_str(), unhashed.length())) {
        return false;
    }

    unsigned char hash[EVP_MAX_MD_SIZE];
    size_t lengthOfHash = 0;

    if(!EVP_DigestFinal_ex(context.get(), hash, &lengthOfHash)) {
        return false;
    }

    std::stringstream ss;
    for(size_t i{}; i < lengthOfHash; ++i) {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }

    hashed = ss.str();
    return true;
}

int main(int, char**)
{
    std::string pw1 = "password1", pw1hashed;
    std::string pw2 = "password2", pw2hashed;
    std::string pw3 = "password3", pw3hashed;
    std::string pw4 = "password4", pw4hashed;

    computeHash(pw1, pw1hashed);
    computeHash(pw2, pw2hashed);
    computeHash(pw3, pw3hashed);
    computeHash(pw4, pw4hashed);

    std::cout << pw1hashed << std::endl;
    std::cout << pw2hashed << std::endl;
    std::cout << pw3hashed << std::endl;
    std::cout << pw4hashed << std::endl;

    return 0;
}
Louie answered 19/6 at 9:31 Comment(2)
Your edit popped back up for me, not sure why (also not sure why the queue editors denied it). I just accepted it on my answer; I think it is a nice readability improvement on my code, without changing the spirit of my answerImprovement
@Improvement thanks for the feedback, looks good now! (proud)Louie
R
0

OpenSSL deprecated most of the older (Ex : SHA.h) Api starting OpenSSL 3.0 so i do not recommend using it, but rather migrate to the new OpenSSL 3.xx API see https://www.openssl.org/docs/man3.0/man7/migration_guide.html

Now regarding your question :

  • Create & Init a new digest context using EVP_MD_CTX_new()
  • Initialize the Digest Context using EVP_DigestInit_ex() to use a specific digest type like EVP_sha256() and optionally your ENGINE Impl
  • Hash your bytes using EVP_DigestUpdate()
  • Retrieve the Digest value using EVP_DigestFinal_ex()
  • Free your context using EVP_MD_CTX_free

P.S : Do not forget to allocate memory for your output Digest, you can use SHA256_DIGEST_LENGTH in the case of EVP_sha256(), and obviously handle any errors

Rewire answered 24/1 at 15:8 Comment(0)
L
-1

I think that you only have to replace SHA1 function with SHA256 function with tatk code from link in Your post

Laflam answered 14/2, 2010 at 19:30 Comment(0)
M
-1

A more "C++"ish version

#include <iostream>
#include <sstream>

#include "openssl/sha.h"

using namespace std;

string to_hex(unsigned char s) {
    stringstream ss;
    ss << hex << (int) s;
    return ss.str();
}   

string sha256(string line) {    
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, line.c_str(), line.length());
    SHA256_Final(hash, &sha256);

    string output = "";    
    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        output += to_hex(hash[i]);
    }
    return output;
}

int main() {
    cout << sha256("hello, world") << endl;

    return 0;
}
Microcurie answered 28/9, 2011 at 0:55 Comment(6)
-1: “SHA1_Init(), SHA1_Update() and SHA1_Final() return 1 for success, 0 otherwise.”, openssl.org/docs/crypto/sha.htm.Epochmaking
didn't want to obscure the code with C-style return value checks. DIY if you careMicrocurie
"DIY if you care" - sorry to inconvenience you. Folks will blindly copy/paste it. Ignoring return values is a dangerous practice, and should not be demonstarated (especially in high integrity code).Epochmaking
@Epochmaking I cried reading your comment because it's true, people will write "secure" applications by copy-and-paste programming. But I also agree to some degree with Max only because it shouldn't be on our shoulders to prevent stupid people from incorrectly using our knowledge, or stupid people from using "secure" software written by a copy and paste programmer.Embracery
"DIY if you care" But seriously now, who cares about error conditions?Ilona
this one excludes 0s from resulting hash sometimesPlumber

© 2022 - 2024 — McMap. All rights reserved.