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.
This commit is contained in:
Joel Martin 2010-04-30 16:41:09 -05:00
parent ca5785f570
commit adfe6ac166
11 changed files with 148 additions and 14 deletions

View File

@ -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:
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
----- -----

4
TODO
View File

@ -1,8 +1,8 @@
- 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.

View File

@ -0,0 +1 @@
flash-src/WebSocketMain.swf

View File

@ -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 {
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 {
// "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 {
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 {
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) {

Binary file not shown.

View File

@ -0,0 +1 @@
../../../as3crypto_patched/src/com/hurlant

21
links Normal file
View File

@ -0,0 +1,21 @@
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

View File

@ -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 @@
$('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>

6
vnc.js
View File

@ -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";
} }

53
webs.py Executable file
View File

@ -0,0 +1,53 @@
#!/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()

View File

@ -1,5 +1,14 @@
#!/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):
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"