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:
parent
ca5785f570
commit
adfe6ac166
16
README.md
16
README.md
|
@ -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
4
TODO
|
@ -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.
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
flash-src/WebSocketMain.swf
|
|
@ -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.
|
@ -0,0 +1 @@
|
||||||
|
../../../as3crypto_patched/src/com/hurlant
|
|
@ -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
|
2
vnc.html
2
vnc.html
|
@ -7,6 +7,7 @@
|
||||||
Host: <input id='host' style='width:100'>
|
Host: <input id='host' style='width:100'>
|
||||||
Port: <input id='port' style='width:50'>
|
Port: <input id='port' style='width:50'>
|
||||||
Password: <input id='password' type='password' style='width:80'>
|
Password: <input id='password' type='password' style='width:80'>
|
||||||
|
Encrypt: <input id='encrypt' type='checkbox'>
|
||||||
<input id='connectButton' type='button' value='Loading'
|
<input id='connectButton' type='button' value='Loading'
|
||||||
style='width:100px' disabled>
|
style='width:100px' disabled>
|
||||||
<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
6
vnc.js
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
11
wsproxy.py
11
wsproxy.py
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue