使用C++生成带有密钥的HMAC SHA256哈希值

8

我正在寻找一种在C++中使用密钥返回HMAC SHA256哈希的函数或方法。我已经看过Crypto++和OpenSSL的文档,但它们不接受额外的密钥参数进行计算。请问有人可以通过提供一些信息、代码片段或链接来帮助我吗?


HMACs 的定义中包含了一个密钥输入和数据输入。请提供你遇到问题的 HMAC 链接。参见 HMAC 定义。HMACs 使用哈希函数,因此你会看到一些形式的名称连接。 - zaph
我删除了 OpenSSL 和 Crypto++ 标签。当你使用这些技术时,应该添加标签;而不是用于像“我应该使用什么和如何做?”这样的问题。 - jww
6个回答

6
为了保持一致性,以下是使用OpenSSL生成SHA256-HMAC的函数示例。
#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};
}

顺便说一下,我更喜欢Crypto++,因为它生成的二进制文件更小。缺点是Crypto++没有CMake模块。


在 MacOS 上,我遇到了错误:Undefined symbols for architecture x86_64, "_EVP_sha256", "_HMAC",这些被引用但未定义... - Vincent Guyard
@VincentGuyard 你有向 g++ 传递 -lssl -lcrypto 键吗? - Dmytro Ovdiienko
我可以通过在我的CMakeLists.txt文件中添加以下行来处理这个问题:include_directories(/usr/local/opt/openssl@3/include)target_link_libraries(fillerchecker /usr/local/opt/openssl@3/lib/libcrypto.a) - Vincent Guyard
为了在CMake项目中使用openssl,您应该使用find_package(OpenSSL REQUIRED),然后使用TARGET_LINK_LIBRARIES (${PROJECT_NAME} OpenSSL::Crypto) - Dmytro Ovdiienko

6

您可以使用 POCO 库

示例代码:

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

使用cmake install与POCO进行样例集成:

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/)

这里使用的示例实现:https://github.com/gelldur/abucoins-api-cpp


4
以下是使用Crypto++生成SHA256-HMAC的函数示例:
#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");
}

此处内容源自CME iLink2规范


你链接到的页面似乎没有使用make_unique - M.M
一旦创建了HashFilter对象,我调用std::unique_ptr::release成员函数将所有权从unique_ptr释放到HashFilter - Dmytro Ovdiienko
没错,这个函数的CME版本没有使用std::unique_ptr - Dmytro Ovdiienko
好的,明白了。 - M.M

3

HMAC 的 OpenSSL 文档明确指出,上下文初始化的一部分需要“密钥”。

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

HMAC()使用哈希函数evp_md和长度为key_len字节的密钥key计算d中n个字节的消息认证码。

3

我不得不稍微修改@DmytroOvdiienko的答案才能获得十六进制输出:

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

使用 <iomanip>setfillsetwright 来确保单个十六进制值前缀为 0。另一种选择是使用 boost 库:

#include <boost/format.hpp>

...

        out << boost::format("%02x") % (int)hash.data()[i];

0

您可以使用cpp-cryptlite生成HMAC SHA256哈希值,以下是代码片段:

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

它取决于boost库。它既不好也不坏。它只是存在。 - Dmytro Ovdiienko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接