Generate HMAC SHA256 hash using key in C++
Asked Answered
L

6

8

I am looking for some function or a way that would return HMAC SHA256 hash in C++ using secret key. I have seen documentation of Crypto++ and OpenSSL but it does not accept an extra parameter of secret key for computation. Can someone help me by providing some info, code snippets or links.

Loper answered 9/6, 2016 at 23:40 Comment(2)
HMACs by definition have a key input as well as a data input. provide a link to the HMAC you are having trouble with. See HMAC definition. HMACs use a hash function so you will see some form of name joining.Procarp
I removed the OpenSSL and Crypto++ tags. You should add the tags when you are using the technology; and not for something like "What should I use and how should I do it?"Urinary
M
3

OpenSSL docs for HMAC, clearly state the requirement of a 'key' as part of context initialization.

int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len,
               const EVP_MD *md, ENGINE *impl);

HMAC() computes the message authentication code of the n bytes at d using the hash function evp_md and the key key which is key_len bytes long.

Matazzoni answered 10/6, 2016 at 0:17 Comment(0)
T
6

You can use POCO library

Sample code:

class SHA256Engine : public Poco::Crypto::DigestEngine
{
public:
    enum
    {
        BLOCK_SIZE = 64,
        DIGEST_SIZE = 32
    };

    SHA256Engine()
            : DigestEngine("SHA256")
    {
    }

};


Poco::HMACEngine<SHA256Engine> hmac{secretKey};
hmac.update(string);

std::cout << "HMACE hex:" << Poco::DigestEngine::digestToHex(hmac.digest()) << std::endl;// lookout difest() calls reset ;)

Sample integration with POCO using cmake install:

mkdir build_poco/
cd build_poco/ && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./install ../poco/

CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.8)
PROJECT(SamplePoco)

SET(CMAKE_CXX_STANDARD 14)

SET(SOURCE_FILES
        src/main.cpp
        )

SET(_IMPORT_PREFIX lib/build_poco/install)

INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoFoundationTargets.cmake)
INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoNetTargets.cmake)
INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoJSONTargets.cmake)
INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoXMLTargets.cmake)
INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoCryptoTargets.cmake)
INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoUtilTargets.cmake)
INCLUDE(lib/build_poco/install/lib/cmake/Poco/PocoNetSSLTargets.cmake)


ADD_EXECUTABLE(SamplePoco ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(SamplePoco
        Poco::Foundation
        Poco::Crypto
        Poco::Util
        Poco::JSON
        Poco::NetSSL
        )
TARGET_INCLUDE_DIRECTORIES(SamplePoco PUBLIC src/)

Sample implementation used here: https://github.com/gelldur/abucoins-api-cpp

Tello answered 7/10, 2017 at 19:43 Comment(0)
B
6

For consistency, following is a sample of function to generate SHA256-HMAC using OpenSSL

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

#include <string>
#include <string_view>
#include <array>

std::string CalcHmacSHA256(std::string_view decodedKey, std::string_view msg)
{
    std::array<unsigned char, EVP_MAX_MD_SIZE> hash;
    unsigned int hashLen;

    HMAC(
        EVP_sha256(),
        decodedKey.data(),
        static_cast<int>(decodedKey.size()),
        reinterpret_cast<unsigned char const*>(msg.data()),
        static_cast<int>(msg.size()),
        hash.data(),
        &hashLen
    );

    return std::string{reinterpret_cast<char const*>(hash.data()), hashLen};
}

For the record, I like Crypto++ better as in case of Crypto++ generated binary is smaller. The drawback is that Crypto++ does not have a CMake module.

Brandt answered 28/10, 2020 at 9:36 Comment(4)
On MacOS, I am getting error: Undefined symbols for architecture x86_64, "_EVP_sha256", "_HMAC", referenced from ...Artificiality
@VincentGuyard Did you pass -lssl -lcrypto keys to g++?Brandt
I could deal with this adding theses lines in my CMakeLists.txt: include_directories(/usr/local/opt/openssl@3/include) target_link_libraries(fillerchecker /usr/local/opt/openssl@3/lib/libcrypto.a)Artificiality
In order to use openssl in the CMake project, you should use find_package(OpenSSL REQUIRED) and then TARGET_LINK_LIBRARIES (${PROJECT_NAME} OpenSSL::Crypto)Brandt
B
4

Following is a sample of function to generate SHA256-HMAC using Crypto++

#include <string>
#include <string_view>

#include <cryptopp/filters.h>
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::HashFilter;

#include <cryptopp/hmac.h>
using CryptoPP::HMAC;

#include <cryptopp/sha.h>
using CryptoPP::SHA256;

std::string CalcHmacSHA256(std::string_view decodedSecretKey, std::string_view request)
{
    // Calculate HMAC
    HMAC<SHA256> hmac(reinterpret_cast<CryptoPP::byte const*>(decodedSecretKey.data()), decodedSecretKey.size());

    std::string calculated_hmac;
    auto sink = std::make_unique<StringSink>(calculated_hmac);

    auto filter = std::make_unique<HashFilter>(hmac, sink.get());
    sink.release();

    StringSource(reinterpret_cast<CryptoPP::byte const*>(request.data()), request.size(), true, filter.get()); // StringSource
    filter.release();

    return calculated_hmac;
}

#include <iostream>

int main() {
    std::cout << CalcHmacSHA256("key", "data");
}

The source is CME iLink2 specification

Brandt answered 29/5, 2020 at 18:43 Comment(4)
The page you link to doesn't seem to use make_uniqueRheta
Once HashFilter object is created I call std::unique_ptr::release member function to release ownership from unique_ptr to HashFilter.Brandt
Right, the CME version of this function does not use std::unique_ptrBrandt
OK, that makes senseRheta
M
3

OpenSSL docs for HMAC, clearly state the requirement of a 'key' as part of context initialization.

int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len,
               const EVP_MD *md, ENGINE *impl);

HMAC() computes the message authentication code of the n bytes at d using the hash function evp_md and the key key which is key_len bytes long.

Matazzoni answered 10/6, 2016 at 0:17 Comment(0)
L
3

I had to modify @DmytroOvdiienko's answer a bit to get hexadecimal output:

#include <iomanip>

...

std::string CalcHmacSHA256(std::string_view decodedKey, std::string_view msg)
{
    std::array<unsigned char, EVP_MAX_MD_SIZE> hash;
    unsigned int hashLen;
    HMAC(
        EVP_sha256(),
        decodedKey.data(),
        static_cast<int>(decodedKey.size()),
        reinterpret_cast<unsigned char const*>(msg.data()),
        static_cast<int>(msg.size()),
        hash.data(),
        &hashLen
    );
    std::stringstream out;
    for (unsigned int i=0; i < hashLen; i++) {
        out << std::setfill('0') << std::setw(2) << std::right << std::hex << (int)hash.data()[i];
    }
    return out.str();
}

int main(int, char**) {
    std::string key = "ESiFg448MqOmhQyxbt6HEHHPnAA1OE8nX0o9ANIVMIvWLISQS0MivDrkZvnBxMEI";
    std::string msg = "foo";
    std::string_view key_view{key};
    std::string_view msg_view{msg};
    std::cout << CalcHmacSHA256(key_view, msg_view) << std::endl;
}

The <iomanip>, setfill, setw, right are needed to make sure single-digit hex values are prefixed with a 0. An alternative is to use boost:

#include <boost/format.hpp>

...

        out << boost::format("%02x") % (int)hash.data()[i];
Lawman answered 30/4, 2022 at 5:24 Comment(0)
S
0

You can use cpp-cryptlite to generate HMAC SHA256 hash, Following is the code snippet:

std::string src_str = "abcdefg";
std::string secret_key = "xxxxxx";  // this value is an example
boost::uint8_t digest[32];  // cryptlite::sha256::HASH_SIZE
cryptlite::hmac<cryptlite::sha256>::calc(src_str, secret_key, digest);
// and digest is the output hash
Scherer answered 27/12, 2020 at 14:4 Comment(1)
It depends on boost library. It is neither good nor bad. Just it is.Brandt

© 2022 - 2024 — McMap. All rights reserved.