Commit adfe6ac1 authored by Joel Martin's avatar Joel Martin

Support for SSL/TLS ('wss://') on both sides.

On the client side, this adds the as3crypto library to web-socket-js
so that the WebSocket 'wss://' scheme is supported which is WebSocket
over SSL/TLS.

Couple of downsides to the fall-back method:

    - This balloons the size of the web-socket-js object from about 12K to 172K.

    - Getting it working required disabling RFC2718 web proxy support
      in web-socket-js.

    - It makes the web-socket-js fallback even slower with the
      encryption overhead.

The server side (wsproxy.py) uses python SSL support. The proxy
automatically detects the type of incoming connection whether flash
policy request, SSL/TLS handshake ('wss://') or plain socket
('ws://').

Also added a check-box to the web page to enable/disabled 'wss://'
encryption.
parent ca5785f5
...@@ -6,19 +6,24 @@ Description ...@@ -6,19 +6,24 @@ Description
----------- -----------
A VNC client implemented using HTML5, specifically Canvas and A VNC client implemented using HTML5, specifically Canvas and
WebSocket. WebSocket (supports 'wss://' encryption).
For browsers that do not have builtin WebSocket support, the project For browsers that do not have builtin WebSocket support, the project
includes web-socket-js, a WebSocket emulator using Adobe Flash includes web-socket-js, a WebSocket emulator using Adobe Flash
(http://github.com/gimite/web-socket-js). (http://github.com/gimite/web-socket-js).
In addition, as3crypto has been added to web-socket-js to implement
WebSocket SSL/TLS encryption, i.e. the "wss://" URI scheme.
(http://github.com/lyokato/as3crypto_patched).
Requirements Requirements
------------ ------------
Until there is VNC server support for WebSocket connections, you need Until there is VNC server support for WebSocket connections, you need
to use a WebSocket to TCP socket proxy. There is a python proxy to use a WebSocket to TCP socket proxy. There is a python proxy
included ('wsproxy'). included ('wsproxy'). One advantage of using the proxy is that it has
builtin support for SSL/TLS encryption (i.e. "wss://").
There a few reasons why a proxy is required: There a few reasons why a proxy is required:
...@@ -38,6 +43,13 @@ There a few reasons why a proxy is required: ...@@ -38,6 +43,13 @@ There a few reasons why a proxy is required:
the client asks the proxy (using the initial query string) to add the client asks the proxy (using the initial query string) to add
sequence numbers to each packet. sequence numbers to each packet.
To encrypt the traffic using the WebSocket 'wss://' URI scheme you
need to generate a certificate for the proxy to load. You can generate
a self-signed certificate using openssl. The common name should be the
hostname of the server where the proxy will be running:
`openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem`
Usage Usage
----- -----
......
- Add WSS/https/SSL support to page and wsproxy.py
- Make C version of wsproxy.py - Make C version of wsproxy.py
- Implement UI option for VNC shared mode. - Implement UI option for VNC shared mode.
- Upgrade to protocol 3.8 - Upgrade to protocol 3.8
- implement ZRLE encoding - implement ZRLE encoding
- Get web-socket-js RFC2817 proxying working again.
File deleted
flash-src/WebSocketMain.swf
\ No newline at end of file
...@@ -16,6 +16,10 @@ import mx.controls.*; ...@@ -16,6 +16,10 @@ import mx.controls.*;
import mx.events.*; import mx.events.*;
import mx.utils.*; import mx.utils.*;
import com.adobe.net.proxies.RFC2817Socket; import com.adobe.net.proxies.RFC2817Socket;
import com.hurlant.crypto.tls.TLSSocket;
import com.hurlant.crypto.tls.TLSConfig;
import com.hurlant.crypto.tls.TLSEngine;
import com.hurlant.crypto.tls.TLSSecurityParameters;
[Event(name="message", type="WebSocketMessageEvent")] [Event(name="message", type="WebSocketMessageEvent")]
[Event(name="open", type="flash.events.Event")] [Event(name="open", type="flash.events.Event")]
...@@ -27,7 +31,11 @@ public class WebSocket extends EventDispatcher { ...@@ -27,7 +31,11 @@ public class WebSocket extends EventDispatcher {
private static var OPEN:int = 1; private static var OPEN:int = 1;
private static var CLOSED:int = 2; private static var CLOSED:int = 2;
private var socket:RFC2817Socket; //private var rawSocket:RFC2817Socket;
private var rawSocket:Socket;
private var tlsSocket:TLSSocket;
private var tlsConfig:TLSConfig;
private var socket:Socket;
private var main:WebSocketMain; private var main:WebSocketMain;
private var scheme:String; private var scheme:String;
private var host:String; private var host:String;
...@@ -59,6 +67,7 @@ public class WebSocket extends EventDispatcher { ...@@ -59,6 +67,7 @@ public class WebSocket extends EventDispatcher {
// "Header1: xxx\r\nHeader2: yyyy\r\n" // "Header1: xxx\r\nHeader2: yyyy\r\n"
this.headers = headers; this.headers = headers;
/*
socket = new RFC2817Socket(); socket = new RFC2817Socket();
// if no proxy information is supplied, it acts like a normal Socket // if no proxy information is supplied, it acts like a normal Socket
...@@ -66,13 +75,30 @@ public class WebSocket extends EventDispatcher { ...@@ -66,13 +75,30 @@ public class WebSocket extends EventDispatcher {
if (proxyHost != null && proxyPort != 0){ if (proxyHost != null && proxyPort != 0){
socket.setProxyInfo(proxyHost, proxyPort); socket.setProxyInfo(proxyHost, proxyPort);
} }
*/
socket.addEventListener(Event.CLOSE, onSocketClose);
socket.addEventListener(Event.CONNECT, onSocketConnect); ExternalInterface.call("console.log", "[WebSocket] scheme: " + scheme);
socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError); rawSocket = new Socket();
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); rawSocket.addEventListener(Event.CLOSE, onSocketClose);
socket.connect(host, port); rawSocket.addEventListener(Event.CONNECT, onSocketConnect);
rawSocket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
rawSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
if (scheme == "wss") {
tlsConfig= new TLSConfig(TLSEngine.CLIENT,
null, null, null, null, null,
TLSSecurityParameters.PROTOCOL_VERSION);
tlsConfig.trustSelfSignedCertificates = true;
tlsConfig.ignoreCommonNameMismatch = true;
tlsSocket = new TLSSocket();
tlsSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
socket = (tlsSocket as Socket);
} else {
rawSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
socket = (rawSocket as Socket);
}
rawSocket.connect(host, port);
} }
public function send(data:String):int { public function send(data:String):int {
...@@ -118,6 +144,12 @@ public class WebSocket extends EventDispatcher { ...@@ -118,6 +144,12 @@ public class WebSocket extends EventDispatcher {
private function onSocketConnect(event:Event):void { private function onSocketConnect(event:Event):void {
main.log("connected"); main.log("connected");
if (scheme == "wss") {
ExternalInterface.call("console.log", "[WebSocket] starting SSL/TLS");
tlsSocket.startTLS(rawSocket, host, tlsConfig);
}
var hostValue:String = host + (port == 80 ? "" : ":" + port); var hostValue:String = host + (port == 80 ? "" : ":" + port);
var cookie:String = ""; var cookie:String = "";
if (main.getCallerHost() == host) { if (main.getCallerHost() == host) {
......
File added
../../../as3crypto_patched/src/com/hurlant
\ No newline at end of file
Canvas Browser Compatibility:
http://philip.html5.org/tests/canvas/suite/tests/results.html
WebSockets API standard:
http://dev.w3.org/html5/websockets/
Browser Keyboard Events detailed:
http://unixpapa.com/js/key.html
ActionScript (Flash) WebSocket implementation:
http://github.com/gimite/web-socket-js
ActionScript (Flash) crypto/TLS library:
http://code.google.com/p/as3crypto
http://github.com/lyokato/as3crypto_patched
TLS Protocol:
http://en.wikipedia.org/wiki/Transport_Layer_Security
Generate self-signed certificate:
http://docs.python.org/dev/library/ssl.html#certificates
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
Host: <input id='host' style='width:100'>&nbsp; Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp; Port: <input id='port' style='width:50'>&nbsp;
Password: <input id='password' type='password' style='width:80'>&nbsp; Password: <input id='password' type='password' style='width:80'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Loading' <input id='connectButton' type='button' value='Loading'
style='width:100px' disabled>&nbsp; style='width:100px' disabled>&nbsp;
<br><br> <br><br>
...@@ -75,6 +76,7 @@ ...@@ -75,6 +76,7 @@
$('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1]; $('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1]; $('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
$('password').value = (url.match(/password=([^&#]*)/) || ['',''])[1]; $('password').value = (url.match(/password=([^&#]*)/) || ['',''])[1];
$('encrypt').checked = (url.match(/encrypt=([^&#]*)/) || ['',''])[1];
} }
} }
</script> </script>
......
...@@ -906,7 +906,11 @@ updateState: function(state, statusMsg) { ...@@ -906,7 +906,11 @@ updateState: function(state, statusMsg) {
init_ws: function () { init_ws: function () {
console.log(">> init_ws"); console.log(">> init_ws");
var uri = "ws://" + RFB.host + ":" + RFB.port + "/?b64encode"; var scheme = "ws://";
if ($('encrypt').checked) {
scheme = "wss://";
}
var uri = scheme + RFB.host + ":" + RFB.port + "/?b64encode";
if (RFB.use_seq) { if (RFB.use_seq) {
uri += "&seq_num"; uri += "&seq_num";
} }
......
#!/usr/bin/python
'''
A super simple HTTP/HTTPS webserver for python. Automatically detect
You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import traceback, sys
import socket
import ssl
#import http.server as server # python 3.X
import SimpleHTTPServer as server # python 2.X
def do_request(connstream, from_addr):
x = object()
server.SimpleHTTPRequestHandler(connstream, from_addr, x)
def serve():
bindsocket = socket.socket()
#bindsocket.bind(('localhost', PORT))
bindsocket.bind(('', PORT))
bindsocket.listen(5)
print("serving on port", PORT)
while True:
try:
newsocket, from_addr = bindsocket.accept()
peek = newsocket.recv(1024, socket.MSG_PEEK)
if peek.startswith("\x16"):
connstream = ssl.wrap_socket(
newsocket,
server_side=True,
certfile='self.pem',
ssl_version=ssl.PROTOCOL_TLSv1)
else:
connstream = newsocket
do_request(connstream, from_addr)
except Exception:
traceback.print_exc()
try:
PORT = int(sys.argv[1])
except:
print "%s port" % sys.argv[0]
sys.exit(2)
serve()
#!/usr/bin/python #!/usr/bin/python
'''
A WebSocket to TCP socket proxy with support for "wss://" encryption.
You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import sys, os, socket, ssl, time, traceback, re import sys, os, socket, ssl, time, traceback, re
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from select import select from select import select
...@@ -129,7 +138,7 @@ def do_handshake(sock): ...@@ -129,7 +138,7 @@ def do_handshake(sock):
retsock = ssl.wrap_socket( retsock = ssl.wrap_socket(
sock, sock,
server_side=True, server_side=True,
certfile='wsproxy.pem', certfile='self.pem',
ssl_version=ssl.PROTOCOL_TLSv1) ssl_version=ssl.PROTOCOL_TLSv1)
scheme = "wss" scheme = "wss"
print "Using SSL/TLS" print "Using SSL/TLS"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment