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
WebSocket.
WebSocket (supports 'wss://' encryption).
For browsers that do not have builtin WebSocket support, the project
includes web-socket-js, a WebSocket emulator using Adobe Flash
(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
------------
Until there is VNC server support for WebSocket connections, you need
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:
@ -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
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
-----

4
TODO
View File

@ -1,8 +1,8 @@
- Add WSS/https/SSL support to page and wsproxy.py
- Make C version of wsproxy.py
- Implement UI option for VNC shared mode.
- Upgrade to protocol 3.8
- 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.utils.*;
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="open", type="flash.events.Event")]
@ -27,7 +31,11 @@ public class WebSocket extends EventDispatcher {
private static var OPEN:int = 1;
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 scheme:String;
private var host:String;
@ -59,6 +67,7 @@ public class WebSocket extends EventDispatcher {
// "Header1: xxx\r\nHeader2: yyyy\r\n"
this.headers = headers;
/*
socket = new RFC2817Socket();
// 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){
socket.setProxyInfo(proxyHost, proxyPort);
}
socket.addEventListener(Event.CLOSE, onSocketClose);
socket.addEventListener(Event.CONNECT, onSocketConnect);
socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
socket.connect(host, port);
*/
ExternalInterface.call("console.log", "[WebSocket] scheme: " + scheme);
rawSocket = new Socket();
rawSocket.addEventListener(Event.CLOSE, onSocketClose);
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 {
@ -118,6 +144,12 @@ public class WebSocket extends EventDispatcher {
private function onSocketConnect(event:Event):void {
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 cookie:String = "";
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;
Port: <input id='port' style='width:50'>&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'
style='width:100px' disabled>&nbsp;
<br><br>
@ -75,6 +76,7 @@
$('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
$('password').value = (url.match(/password=([^&#]*)/) || ['',''])[1];
$('encrypt').checked = (url.match(/encrypt=([^&#]*)/) || ['',''])[1];
}
}
</script>

6
vnc.js
View File

@ -906,7 +906,11 @@ updateState: function(state, statusMsg) {
init_ws: function () {
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) {
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
'''
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
from base64 import b64encode, b64decode
from select import select
@ -129,7 +138,7 @@ def do_handshake(sock):
retsock = ssl.wrap_socket(
sock,
server_side=True,
certfile='wsproxy.pem',
certfile='self.pem',
ssl_version=ssl.PROTOCOL_TLSv1)
scheme = "wss"
print "Using SSL/TLS"