【问题标题】:C++ Base64 of Sha1 - WebSocket HandshakeSha1 的 C++ Base64 - WebSocket 握手
【发布时间】:2025-12-11 04:35:01
【问题描述】:

我目前正在尝试运行可以与 WebSocket 通信的 C++ 服务器。 HandShake 由几个步骤组成,而最后一个步骤我没有成功。

第一步是生成一个SHA1编码的字符串,我成功获得了正确的十六进制字符串。 (例如http://en.wikipedia.org/wiki/WebSocket & https://www.rfc-editor.org/rfc/rfc6455)。

我的输出在这两种情况下都与文档中所述的相同:

Wikipedia: 1d 29 ab 73 4b 0c 95 85 24 00 69 a6 e4 e3 e9 1b 61 da 19 69
My Server: 1d 29 ab 73 4b 0c 95 85 24 00 69 a6 e4 e3 e9 1b 61 da 19 69

IETF Docu: b3 7a 4f 2c c0 62 4f 16 90 f6 46 06 cf 38 59 45 b2 be c4 ea
My Server: b3 7a 4f 2c c0 62 4f 16 90 f6 46 06 cf 38 59 45 b2 be c4 ea

所以这是对的。当我现在进行 Base64 编码时,我得到以下结果:

Wikipedia: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
My Server: MWQyOWFiNzM0YjBjOTU4NTI0MDA2OWE2ZTRlM2U5MWI2MWRhMTk2OQ==

IETF Docu: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
My Server: YjM3YTRmMmNjMDYyNGYxNjkwZjY0NjA2Y2YzODU5NDViMmJlYzRlYQ==

而这完全不同。我确认我的 Base64 算法适用于某些在线转换器,并且它们都产生了我的服务器所做的输出。所以问题是输入格式。我在一个 javascript 论坛中找到了一个论坛条目,其中一个有同样的问题,答案是,我们应该传递 20 个字符的二进制表示,而不是传递 40 个字符的十六进制字符串。

我知道 openssl SHA1 返回二进制表示,但由于某些原因我不能使用该库。我使用的 SHA1 库将编码的输出放在一个 int 数组中。输出如下所示(IETF 示例):

result[0] = 3011137324
result[1] = 3227668246
result[2] = 2432058886
result[3] = 3476576581
result[4] = 2998846698

我把它转换成这样的十六进制:

std::ostringstream oss;
oss << std::setfill('0');
            for (int i = 0; i < 5; ++i) {
                    oss << std::setw(8) << std::hex << result[i];
            }

现在是个大问题。如何将我的十六进制字符串转换为二进制?

提前非常感谢。 马库斯

编辑

如果有人对代码感兴趣: https://github.com/MarkusPfundstein/C---Websocket-Server

【问题讨论】:

    标签: c++ binary hex websocket handshake


    【解决方案1】:

    我在 c 中测试 websocket,发现字节顺序错误。调整顺序(反向)解决了我的 base64 编码问题,导致正确接受的密钥字符串:

    unsigned char byteResult [20];
        for(i = 0; i < 5; i++) {
            byteResult[(i * 4) + 3] = sha.result[i] & 0x000000ff;
            byteResult[(i * 4) + 2] = (sha.result[i] & 0x0000ff00) >> 8;
            byteResult[(i * 4) + 1] = (sha.result[i] & 0x00ff0000) >> 16;
            byteResult[(i * 4) + 0] = (sha.result[i] & 0xff000000) >> 24;
        }
    

    【讨论】:

    【解决方案2】:

    大多数 Baser64 编码器都需要一个字节数组/二进制数据流。您想使用位掩码和逻辑移位将整数拆分为字节。在 32 位系统上,每个 int 包含 4 个字节,您可以将它们提取如下:

    for(i = 0; i < 5; i++) {
    
        byteResult[(i * 4) + 0] = result[i] & 0x000000ff;
        byteResult[(i * 4) + 1] = (result[i] & 0x0000ff00) >> 8;
        byteResult[(i * 4) + 2] = (result[i] & 0x00ff0000) >> 16;
        byteResult[(i * 4) + 3] = (result[i] & 0xff000000) >> 24;
    }
    

    其中byteResult 是一个比结果数组大4 倍的byte[]。我假设字节被打包到这里的整数的顺序,它可能是相反的。

    将此字节[] 传递到您的 Base64 编码器中。

    【讨论】:

    • 太棒了,我会尽快尝试的
    • 我很抱歉,但它不起作用:(结果是一个数组 5 ,所以我不必枚举直到 i
    • 对不起,你说的很对,应该是 5 - 答案已更新。另请参阅我关于字节排序的注释。输出有什么奇怪的地方?
    • 这是字节结果:,Oz�Ob�F��EY8��ľ�b37a4f2cc0624f1690f64606cf385945b2bec4ea
    • 您的 Base64 编码器采用什么输入?你直接把byte[]传给它吗?
    【解决方案3】:

    在一个稍微相关的注释上(我看到你已经发现了EVP BIO base64 way...):

    result[0] = 3011137324
    ...
    oss << std::setw(8) << std::hex << result[i];
    

    如果我理解正确,这会导致输出 b37a4f2c,这是您的 IETF 文档示例。在这里要非常小心,因为您正在处理特定于平台endianess 的开放水域危险。 0n3011137324 确实是 0xb37a4f2c 但只在小端机器上,比如英特尔架构。您最好将&amp;result[0] 重新解释为unsigned char*,然后将其作为字节数组处理,而不是(无符号)整数数组。

    【讨论】: