如何将SHA1生成的十六进制字符串转换为Base64字符串?
你现在的问题根本不是库选错了,而是把十六进制字符串直接当文本做Base64编码了——这就会得到那种超长的错误结果。正确的做法是:先把SHA1生成的十六进制字符串转回原始的二进制字节流,再对这个字节流做Base64编码。
为什么会错?
SHA1哈希的本质是20字节的二进制数据(不是字符串)。你现在拿到的b37a4f2cc0624f1690f64606cf385945b2bec4ea是这20字节转成的十六进制表示(每字节用两个十六进制字符表示)。
你当前的操作是:把这个40字符的十六进制字符串当成普通文本,直接喂给Base64编码器——这相当于编码了40字节的ASCII字符,自然会得到YjM3YTRmMmNjMDYyNGYxNjkwZjY0NjA2Y2YzODU5NDViMmJlYzRlYQ==这种错误结果。
而正确的操作是:把十六进制字符串解码回20字节的原始二进制数据,再对这20字节做Base64编码,这样才能得到s3pPLMBiTxaQ9kYGzzhZRbK+xOo=。
解决方案
1. 修正编码流程(最优)
优先推荐直接用SHA1库输出的二进制结果做Base64编码,跳过十六进制转换这一步:
- 调用SHA1库时,不要获取十六进制字符串,而是直接获取生成的20字节二进制数组。
- 把这个二进制数组传给Base64库的编码函数(注意:要传字节数组,不是字符串)。
2. 如果只能拿到十六进制字符串,先转成二进制
如果你的SHA1库只能输出十六进制字符串,那你需要先把它转成二进制字节数组。这里给你一个简单的C++实现:
#include <vector> #include <string> #include <cstdint> #include <cstdlib> std::vector<uint8_t> hex_to_bytes(const std::string& hex_str) { std::vector<uint8_t> bytes; bytes.reserve(hex_str.length() / 2); for (size_t i = 0; i < hex_str.size(); i += 2) { // 每次取两个十六进制字符转成一个字节 std::string byte_chunk = hex_str.substr(i, 2); uint8_t byte = static_cast<uint8_t>(strtol(byte_chunk.c_str(), nullptr, 16)); bytes.push_back(byte); } return bytes; }
然后用这个函数把你的十六进制字符串转成字节数组,再传给Base64库编码即可。
3. 可选:换更省心的库
如果你不想自己处理转换逻辑,可以考虑用更现代的库:
- Boost库:如果你的项目已经在用Boost,它的
boost::beast::websocket模块直接封装了WebSocket握手的逻辑(包括Sec-WebSocket-Accept的生成),完全不用自己处理SHA1和Base64。 - Crypto++:轻量级密码库,支持直接生成SHA1二进制结果,也有Base64编码工具,文档齐全。
- 单头文件库:比如找只处理Base64和十六进制转换的单头文件实现,避免自己写代码出错。
额外提醒(WebSocket握手关键)
别忘了WebSocket握手的完整流程:你需要把客户端发送的Sec-WebSocket-Key字段值,加上固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11,然后对拼接后的字符串做SHA1哈希,再把哈希的二进制结果转成Base64,最后把这个值放到响应头的Sec-WebSocket-Accept字段里。
内容的提问来源于stack exchange,提问作者user8535817

