remote-desktop-clients icon indicating copy to clipboard operation
remote-desktop-clients copied to clipboard

bVNC Request: supply client cert for VNC over SSL Tunnel

Open Zeluok opened this issue 3 years ago • 9 comments

Please add the ability to supply a client cert for VNC over SSL Tunnel!

Code touch-points would probably include:

Related code examples:

Any help with this is appreciated! If this is implemented, bVNC would be the only product that does it :)

Use case:

  • Running x11vnc -display :0 -usepw -ssl /path/to/cert -sslonly -sslverify /pemdir/ for VNC, TLS and client cert verification

Benefit:

  • Significant boost to security

Zeluok avatar Oct 23 '21 06:10 Zeluok

I second this request. After recently getting my VNC sessions for my garage servers secured with x509 certs, I can't seem to find any mobile app that will allow me to connect. I am able to connect just fine from multiple desktops (both Linux and Windows, by providing signing cert and password). I do not want to use SSH, so connecting through my normal VNC port using X509vnc security type on the TigerVNC server vncsession requires me to provide the signing certificate from the device attempting to sign in to the VNC session remotely. At present bVNC does not currently support this, as far as I know. This feature would be greatly appreciated.

jlobue10 avatar Apr 27 '22 16:04 jlobue10

SSH tunnel with keys is an excellent alternative to this. Why wouldn't you want to use it?

iiordanov avatar May 01 '22 16:05 iiordanov

Mainly for simplicity and not requiring a different port open on my servers than I'm already using. I connect to my two garage servers securely through TigerVNC Viewer with x509 ED448 certificates from multiple desktops. I'm trying to achieve this same functionality with the bVNC software without requiring SSH. This feature would make the software even better. For now, I've stopped using it sadly (and I paid for Pro).

jlobue10 avatar May 01 '22 16:05 jlobue10

Sorry to hear that you stopped using the software. It's a valid feature request, but it's such a niche request that it's low on the priorities list, unfortunately. Thanks for your patience and understanding!

iiordanov avatar May 01 '22 17:05 iiordanov

Totally understandable. For now, I'll just lose the ability to periodically login to my garage servers away from home on mobile. It's not that big of a deal. If I really needed to be able to do that, I could re-enable TLSVnc security type on the servers and login with VeNCrypt encryption. This is still much better than nothing. As it's not an emergency for me, I will just leave security type as X509vnc and have the certificates accessible on flash drive, and use a desktop or laptop if I'm away from home and need to check on the servers. This will be greatly appreciated, if and when this feature is added.

jlobue10 avatar May 01 '22 18:05 jlobue10

I would have shared this with you if I'd seen your post earlier. Apologies @jlobue10.

I implemented this and recompiled bVNC for myself last year. This implementation assumes that one client cert is presented for all SSL Tunnel connections. I recommend decoupling and having entry fields/file pickers.

public class SecureTunnel implements X509TrustManager {
//...etc
  public SecureTunnel(String address, int port, int hashAlgorithm, String hash, String cert, Handler messageBus) {
  //...etc
  
    public void setup() throws Exception {
      // create socket
      Socket sock = new Socket(m_address, m_port);
      sock.setTcpNoDelay(true);
      
      // create secure tunnel
      Log.i(TAG, "Generating TLS context.");  
      SSLContext sc = SSLContext.getInstance("TLS");  
      String word = "X.509";
      
      X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(
        (InputStream) new ByteArrayInputStream(Base64.decode("<<CLIENT.CRT>>",0)));
        
      KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
      ks.load(null, null);
      ks.setCertificateEntry("certificate", cert);
      ks.setKeyEntry("private-key", KeyFactory.getInstance("RSA").generatePrivate(
        new PKCS8EncodedKeySpec(Base64.decode(
          "<<CLIENT.DER>>",0))),
        word.toCharArray(),
        new java.security.cert.Certificate[] { cert });
        
      KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      kmf.init(ks, word.toCharArray());
      sc.init(kmf.getKeyManagers(), new TrustManager[] {this} , null);
      
      SSLSocketFactory sslfactory = sc.getSocketFactory();
      m_sslsock = (SSLSocket) sslfactory.createSocket(sock, sock.getInetAddress().getHostName(), sock.getPort(), true);
      m_sslsock.setTcpNoDelay(true);
      
      // this can hang without a timeout
      m_sslsock.setSoTimeout(Constants.SOCKET_CONN_TIMEOUT);        
      setParam(m_sslsock);
      
      Log.i(TAG, "Performing TLS handshake.");
      m_sslsock.startHandshake();
      Log.i(TAG, "Secure tunnel established.");
      
      //...etc
    }
    
  }
}

To generate the base64 encoded strings, I used the two utilities: openssl and base64.

//Step 1, Generate a CA key+crt if you don't already have one. 
//(i.e, req -new -key CA.key -x509 -days XX -out CA.crt)

//Step 2, Generate a client certificate.
openssl genrsa -out client.pem 4096
openssl req -new -key client.pem -out client.csr -sha256
openssl x509 -req -days 3650 -in client.csr -CA CA.crt -CAkey CA.key -CAcreateserial -out client.crt
openssl rsa -in client.pem -pubout > client.pub

//Step 3, Format as a DER key.
openssl pkcs8 -topk8 -inform PEM -outform DER -in client.pem -out client.der -nocrypt

//Step 4, Use the following base64 strings in the code above.
cat client.crt | base64
cat client.der | base64

On the server side, use an x11vnc command like: x11vnc -shared -forever -usepw -display :0 -ultrafilexfer -ssl ~/.vnc/certs/server.pem -sslonly -repeat -scale 1440x835 -sslverify ~/.vnc/certs/CA.crt

A side note, you'll note that the original post demonstrated using /path/to/pemdir. I didn't have success with this. What you're meant to do for that is something like openssl x509 -hash -noout -in client.crt to name the client crt file.

x11vnc docs: https://linux.die.net/man/1/x11vnc

And a useful script from LibVNC for mac/linux users (to simplify connecting to vnc using an stunnel) is: https://github.com/LibVNC/x11vnc/blob/master/misc/enhanced_tightvnc_viewer/bin/util/ss_vncviewer

i.e. ./ss_vncviewer -mycert client.pem <<address>> -preferredencoding "hextile" -lowcolorlevel 1 -autoselect=0 -dotwhennocursor=1

Zeluok avatar Jul 16 '22 09:07 Zeluok

Thank you for the detailed write-up and explanation @Zeluok . I think for now I am ok with not checking my two servers from my Android phone or tablet. It's not worth my time or effort to replicate what I already have working with SSL certificates using TigerVNC server and client using a desktop or laptop. If I really need to access my two servers away from home to check on them to issue some command line commands, I can take the SSL certificate files with me and login from a desktop or laptop. This is not ideal, but it's good enough for me for now. My hope is that this feature will officially get added at some point, and through the GUI I can select the X509 certificates that I want to use for authentication and connection. Perhaps I will have to perform some extra steps to make the certificates usable on Android as you pointed out, or perhaps my current certs can be used if and when the client software is updated.

jlobue10 avatar Jul 16 '22 15:07 jlobue10

Fair. Perhaps @iiordanov can refer to the code above for some low-hanging fruit sometime.

Zeluok avatar Jul 17 '22 07:07 Zeluok

I would like to second this feature request. I also am a Pro user and I selected bVNC because it explictly mentioned support for VeNCrypt, so I assumed it would have support for client certificates because I don't see how you have a secure connection without it (an 8 character password is definitely not secure).

My use case is connecting to QEMU over VNC. QEMU, btw, makes you jump through hoops to use VNC passwords because they're so insecure. VeNCrypt with mutual TLS, however, is relatively easy to set up.

I don't want to just tunnel my connection over SSH because that exposes my entire host OS to an attacker in the case where my keys are lost. SSH has so many different ways to interact with the host system that it's very difficult to create an SSH user that you can be 100% sure can only be used for one thing. On the other hand, if my client certificate for this QEMU server is compromised, they get access to one VM. It's not a good situation but at least the VM is running in snapshot mode so that a theoretical attacker can't create a permanent home on my network. So I want client certificates (directly authenticating with VNC) to greatly reduce my attack surface and the associated risk of compromise.

MichaelCosby avatar Jul 17 '22 16:07 MichaelCosby