Base64 encoding for Sec-WebSocket-Accept value
不久前,我开始尝试使用node.js来处理后端。它工作得很好,但是现在当我返回时,协议已经更新,我不能让它正常工作了。
具体来说,问题是
以下是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 " + "Upgrade: WebSocket " + "Connection: Upgrade " + "Sec-WebSocket-Accept:" + b64hash +" " + " "; socket.write(handshake); |
示例连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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中复制计算散列,但失败了。
1 2 3 4 5 6 7 8 9 10 11 12 | 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中的
1 2 3 4 5 6 7 8 | $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中复制它,但没有任何效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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([encoding])
Calculates the digest of all of the passed data to be hashed. The encoding can be 'hex', 'binary' or 'base64'.
(强调我的)
因此,通过将代码更改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 " + "Upgrade: WebSocket " + "Connection: Upgrade " + "Sec-WebSocket-Accept:" + hash +" " + " "; socket.write(handshake); |
是时候觉得自己很傻了。(再次)
使用http://pajhome.org.uk/crypt/md5/sha1.html和代码
1 2 3 | b64pad ="="; var b64hash = b64_sha1(secWsKey + magicString); console.log(b64hash); |