Sec-WebSocket-Accept值的Base64编码
前段时间,我开始尝试用Node.js来处理后端的WebSockets。 它工作正常,但现在当我返回协议已更新,我不能得到它正常工作了。
具体来说,问题是Sec-WebSocket-Accept
头。 在计算时,我似乎做错了什么,尽pipe我不能真正理解那可能是什么。 据我所知,我遵循维基百科上的指示 。
这是我的代码:
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
仔细研究这个,我试图在wiki中复制计算的散列,并且失败。
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编码不是。 看着这个答案 ,看来我会做对了。 我在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=
有时阅读手册实际上有帮助。
hash.digest([编码])
计算所有通过散列的数据的摘要。 编码可以是“hex”,“二进制”或“ 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);
是时候感到愚蠢了。 (再次。)
使用这个http://pajhome.org.uk/crypt/md5/sha1.html和代码
b64pad = "="; var b64hash = b64_sha1(secWsKey + magicString); console.log(b64hash);