Base64编码用于Sec-WebSocket-Accept值

6
不久前,我开始尝试使用Node.js处理后端的WebSockets。一切都正常,但是现在当我回来时,协议已经更新了,我无法使其正常工作。具体问题在于“Sec-WebSocket-Accept”头部。计算它时似乎出了些问题,虽然我真的不知道可能是什么问题。据我所知,我严格按照维基百科上的说明进行操作。以下是我的代码:
var magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
var secWsKey = req.headers['sec-websocket-key'];
var hash = require('crypto')
             .createHash('SHA1')
             .update(secWsKey + magicString)
             .digest('hex');
var b64hash = new Buffer(hash).toString('base64');
var handshake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
            "Upgrade: WebSocket\r\n" +
            "Connection: Upgrade\r\n" +
            "Sec-WebSocket-Accept: " + b64hash + "\r\n" +
            "\r\n";

socket.write(handshake);

一个示例连接:
// The incoming headers
{ upgrade: 'websocket',
  connection: 'Upgrade',
  host: 'localhost:8888',
  origin: 'http://localhost:8888',
  'sec-websocket-key': '4aRdFZG5uYrEUw8dsNLW6g==',
  'sec-websocket-version': '13' }

// The outgoing handshake
HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: YTYwZWRlMjQ4NWFhNzJiYmRjZTQ5ODI4NjUwMWNjNjE1YTM0MzZkNg==

// Result: Error during WebSocket handshake: Sec-WebSocket-Accept mismatch

进一步调查后,我尝试复制维基中的计算哈希值的方法,但失败了。

var hash = require('crypto')
            .createHash('SHA1')
            .update('x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
            .digest('hex');

// Result  : 1d29ab734b0c9585240069a6e4e3e91b61da1969
// Expected: 1d29ab734b0c9585240069a6e4e3e91b61da1969

var buf = new Buffer(hash).toString('base64');

// Result  : MWQyOWFiNzM0YjBjOTU4NTI0MDA2OWE2ZTRlM2U5MWI2MWRhMTk2OQ==
// Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

正如你所看到的,SHA1哈希值是正确的,但base64编码不正确。查看this answer,似乎我应该做得更好。我在PHP中尝试了同样的过程,并得到了相同的结果,所以显然我做错了。
我正在运行Node.js v0.6.8。
越来越接近了
通过进一步尝试熟悉的PHP,并从shell中printf的行为中推导出,我想出了这个可行的代码片段。
$hash = sha1('x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
$hashdec = '';
for ($i = 0; $i < strlen($hash); $i += 2) { 
    $hashdec .= chr(hexdec(substr($hash, $i, 2))); 
};
echo base64_encode($hashdec);
// Result  : HSmrc0sMlYUkAGmm5OPpG2HaGWk=
// Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

我尝试在JavaScript中复制这个,但没有成功。
var magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
var key = "4aRdFZG5uYrEUw8dsNLW6g==";
var magic_key = magic + key;
var hash = require('crypto').createHash('sha1').update(magic_key).digest('hex');
var buf = new Buffer(hash.length / 2);

for (var i = 0; i < hash.length; i += 2) {
    var token = hash.substr(i, 2);
    var int = parseInt(token.toString(16), 16);
    var chr = String.fromCharCode(int);

    buf.write(chr);
}

console.log(buf.toString('base64'));

// Result  : w53dAAEAAADBIIAFAQAAAGGAtwA=
// Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

这可能不是你想听到的,但我强烈建议将协议处理留给其他人。它很复杂,而且经常更改。一个正在进行同行评审的开源解决方案将确保一切始终符合最新的规范。请参见此页面获取Node.js套接字模块 - Alex Turpin
@Xeon06 我非常清楚这一点。但是,由于我喜欢剖析事物以更好地理解它们,我想制作一个可行的PoC。这不是我会在生产环境中使用的东西。 - nikc.org
啊,我能理解。我自己在开始时也制作了自己的WebSocket服务器,只是为了学习而已,但后来转向了更稳定的解决方案。祝你解决问题顺利。 - Alex Turpin
@Xeon06,我并不完全反对你不要重复造轮子的观点。然而,这个协议并没有经常变化。它现在是一个IETF RFC标准。HyBi的线路协议框架在标准化前一年基本上没有改变。更改只涉及错误代码、标准措辞等方面。浏览器支持正在逐步增加,所以感觉事情正在发生变化,但协议并没有变化。 - kanaka
2个回答

5
有时候阅读手册确实有帮助。
hash.digest([encoding])
计算要散列的所有传递数据的摘要。编码可以是'hex','binary'或'base64'。
(重点在于'base64'。)
所以问题通过将代码更改为以下内容得到解决:
var magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
var secWsKey = req.headers['sec-websocket-key'];
var hash = require('crypto')
             .createHash('SHA1')
             .update(secWsKey + magicString)
             .digest('base64'); // <- see that, silly.
var handshake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
            "Upgrade: WebSocket\r\n" +
            "Connection: Upgrade\r\n" +
            "Sec-WebSocket-Accept: " + hash + "\r\n" +
            "\r\n";

socket.write(handshake);

是时候感到愚蠢了。 (再次。)


0

谢谢,但这个问题已经解决了。(没有外部代码。)请看我的答案。 - nikc.org

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