Эх сурвалжийг харах

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 15 жил өмнө
parent
commit
adfe6ac166

+ 14 - 2
README.md

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

+ 2 - 2
TODO

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

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.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) {

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

+ 5 - 1
vnc.js

@@ -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 - 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
 
+'''
+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"