You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SHA256哈希十六进制输出缺失前导零问题排查求助

问题:SHA256哈希转十六进制时缺失前导零

我用C++结合OpenSSL库实现了一个函数,将字符串做SHA256哈希后转换为十六进制格式。输出结果和预期几乎一致,但因为缺失前导零导致长度变短:

对比输出:

64: ae6a9df8bdf4545392e6b1354252af8546282b49033a9118b12e9511892197c6  // 预期结果
64: ae6a9df8bdf4545392e6b1354252af8546282b4933a9118b12e9511892197c6   // 我的输出

我的代码如下:

#include <openssl/evp.h>

#include <cstdint>
#include <array>
#include <string_view>
#include <charconv>

#include <iostream>

int main(){
    std::string oceanic = "oceanic 815";

    EVP_MD_CTX *context = EVP_MD_CTX_new(); 
    EVP_DigestInit(context, EVP_sha256());
    EVP_DigestUpdate(context, oceanic.data(), oceanic.size());

    std::array<uint8_t, EVP_MAX_MD_SIZE> hash{};
    unsigned int written = 0;
    EVP_DigestFinal(context, hash.data(), &written);

    std::cout << written << '/' << EVP_MAX_MD_SIZE << '\n';

    // 预期结果来自在线SHA256工具
    std::string_view expected = "ae6a9df8bdf4545392e6b1354252af8546282b49033a9118b12e9511892197c6";
    std::cout << expected.size() << ": " << expected << '\n';

    std::array<char, 64> hex{};
    for (size_t iHash = 0, iHex = 0; iHash < written; ++iHash)
    {
        std::to_chars(&hex[iHex], &hex[iHex + 2], hash[iHash], 16);
        iHex += 2;

        // 下面这段写法同样会出问题
        // const char hexMap[] = "0123456789ABCDEF";
        // const char ch = hash[iHash];
        // hex[iHash] = hexMap[(ch & 0xF0) >> 4];
        // hex[iHash + 1] = hexMap[ch & 0xF];
        // iHash += 2;
    }

    std::cout << hex.size() << ": " << std::string_view(hex.data(), hex.size()) << '\n';
    return 0;
}

补充:我不想使用std::stringstream,因为不需要动态分配内存。这段代码基于Stack Overflow的一个回答编写。请问我哪里出错了?


问题原因与解决办法

1. std::to_chars的行为导致前导零丢失

std::to_chars默认不会为小于0x10的字节补前导零,比如字节值0x03会被转换成"3"而不是"03"。这会导致后续字节的写入位置偏移,最终生成的十六进制字符串缺失前导零,结构错误。

要修复这个问题,需要手动检查转换后的字符长度,不足两位时补前导零:

for (size_t iHash = 0, iHex = 0; iHash < written; ++iHash)
{
    auto [ptr, ec] = std::to_chars(&hex[iHex], &hex[iHex + 2], hash[iHash], 16);
    // 如果只生成了1个字符,手动插入前导零
    if (ptr - &hex[iHex] == 1) {
        std::memmove(&hex[iHex + 1], &hex[iHex], 1);
        hex[iHex] = '0';
    }
    iHex += 2;
}

2. 注释中手动映射写法的错误

注释里的手动转十六进制代码有两个明显错误:

  • 索引错误:用iHash作为十六进制数组的索引,而正确的索引应该是iHex(iHash是原哈希字节的索引,iHex是十六进制字符串的当前位置);
  • 循环变量冲突:循环内的iHash += 2会破坏外层循环的计数逻辑,导致跳过大量哈希字节。

修正后的手动映射写法(更推荐,格式可控性更强):

std::array<char, 64> hex{};
const char hexMap[] = "0123456789abcdef"; // 和预期结果的小写格式保持一致
for (size_t iHash = 0, iHex = 0; iHash < written; ++iHash)
{
    uint8_t ch = hash[iHash];
    hex[iHex++] = hexMap[(ch >> 4) & 0x0F]; // 取高4位
    hex[iHex++] = hexMap[ch & 0x0F];        // 取低4位
}

最终效果

使用修正后的代码,就能生成和预期完全一致的、带有完整前导零的64位十六进制哈希字符串了。


内容的提问来源于stack exchange,提问作者Sergey Kolesnik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:48:07