Explorar o código

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.
Joel Martin %!s(int64=15) %!d(string=hai) anos
pai
achega
adfe6ac166

+ 14 - 2
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
 -----
 -----

+ 2 - 2
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.

BIN=BIN
include/web-socket-js/WebSocketMain.swf


+ 1 - 0
include/web-socket-js/WebSocketMain.swf

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

+ 40 - 8
include/web-socket-js/flash-src/WebSocket.as

@@ -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);
-    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 {
   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) {

BIN=BIN
include/web-socket-js/flash-src/WebSocketMain.swf


+ 1 - 0
include/web-socket-js/flash-src/com/hurlant

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

+ 21 - 0
links

@@ -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 - 0
vnc.html

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

+ 5 - 1
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";
     }
     }

+ 53 - 0
webs.py

@@ -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()

+ 10 - 1
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"