From: Brian P. Hinz Date: Sun, 13 Sep 2015 14:39:54 +0000 (-0400) Subject: Improve handling of x509 authentication X-Git-Tag: v1.5.90~44 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=95f39a5;p=tigervnc.git Improve handling of x509 authentication Fixes #193 and also adds automatic certificate saving feature like the Fltk viewer. Also pulls in CA certificates from all trusted authorities known to the JRE for the case when the server is using a certificate from a top-level authority. --- diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java index 6f799bb4..a3246c5f 100644 --- a/java/com/tigervnc/rfb/CSecurityTLS.java +++ b/java/com/tigervnc/rfb/CSecurityTLS.java @@ -24,16 +24,21 @@ package com.tigervnc.rfb; import javax.net.ssl.*; -import java.security.*; -import java.security.cert.*; +import java.security.KeyManagementException; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.MessageDigest; +import java.security.cert.*; import java.io.File; -import java.io.InputStream; import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import javax.swing.JOptionPane; +import javax.xml.bind.DatatypeConverter; import com.tigervnc.rdr.*; import com.tigervnc.network.*; @@ -129,7 +134,7 @@ public class CSecurityTLS extends CSecurity { manager = new SSLEngineManager(engine, is, os); manager.doHandshake(); } catch(java.lang.Exception e) { - throw new Exception(e.toString()); + throw new Exception(e.getMessage()); } //checkSession(); @@ -200,19 +205,38 @@ public class CSecurityTLS extends CSecurity { MyX509TrustManager() throws java.security.GeneralSecurityException { - TrustManagerFactory tmf = - TrustManagerFactory.getInstance("PKIX"); KeyStore ks = KeyStore.getInstance("JKS"); CertificateFactory cf = CertificateFactory.getInstance("X.509"); try { ks.load(null, null); + String a = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(a); + tmf.init((KeyStore)null); + for (TrustManager m : tmf.getTrustManagers()) + if (m instanceof X509TrustManager) + for (X509Certificate c : ((X509TrustManager)m).getAcceptedIssuers()) + ks.setCertificateEntry(c.getSubjectX500Principal().getName(), c); + File castore = new File(FileUtils.getVncHomeDir()+"x509_savedcerts.pem"); + if (castore.exists() && castore.canRead()) { + InputStream caStream = new FileInputStream(castore); + Collection cacerts = + cf.generateCertificates(caStream); + for (Certificate cert : cacerts) { + String dn = + ((X509Certificate)cert).getSubjectX500Principal().getName(); + ks.setCertificateEntry(dn, (X509Certificate)cert); + } + } File cacert = new File(cafile); - if (!cacert.exists() || !cacert.canRead()) - return; - InputStream caStream = new FileInputStream(cafile); - X509Certificate ca = (X509Certificate)cf.generateCertificate(caStream); - ks.setCertificateEntry("CA", ca); - PKIXBuilderParameters params = new PKIXBuilderParameters(ks, new X509CertSelector()); + if (cacert.exists() && cacert.canRead()) { + InputStream caStream = new FileInputStream(cafile); + Certificate cert = cf.generateCertificate(caStream); + String dn = + ((X509Certificate)cert).getSubjectX500Principal().getName(); + ks.setCertificateEntry(dn, (X509Certificate)cert); + } + PKIXBuilderParameters params = + new PKIXBuilderParameters(ks, new X509CertSelector()); File crlcert = new File(crlfile); if (!crlcert.exists() || !crlcert.canRead()) { params.setRevocationEnabled(false); @@ -224,13 +248,14 @@ public class CSecurityTLS extends CSecurity { params.addCertStore(store); params.setRevocationEnabled(true); } + tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(new CertPathTrustManagerParameters(params)); + tm = (X509TrustManager)tmf.getTrustManagers()[0]; } catch (java.io.FileNotFoundException e) { vlog.error(e.toString()); } catch (java.io.IOException e) { vlog.error(e.toString()); } - tm = (X509TrustManager)tmf.getTrustManagers()[0]; } public void checkClientTrusted(X509Certificate[] chain, String authType) @@ -242,20 +267,71 @@ public class CSecurityTLS extends CSecurity { public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + MessageDigest md = null; try { - tm.checkServerTrusted(chain, authType); + md = MessageDigest.getInstance("SHA-1"); + tm.checkServerTrusted(chain, authType); } catch (CertificateException e) { - Object[] answer = {"Proceed", "Exit"}; - int ret = JOptionPane.showOptionDialog(null, - e.getCause().getLocalizedMessage()+"\n"+ - "Continue connecting to this host?", - "Confirm certificate exception?", - JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, - null, answer, answer[0]); - if (ret == JOptionPane.NO_OPTION) - System.exit(1); + if (e.getCause() instanceof CertPathBuilderException) { + Object[] answer = {"YES", "NO"}; + X509Certificate cert = chain[0]; + md.update(cert.getEncoded()); + String thumbprint = + DatatypeConverter.printHexBinary(md.digest()); + thumbprint = thumbprint.replaceAll("..(?!$)", "$0 "); + int ret = JOptionPane.showOptionDialog(null, + "This certificate has been signed by an unknown authority\n"+ + "\n"+ + " Subject: "+cert.getSubjectX500Principal().getName()+"\n"+ + " Issuer: "+cert.getIssuerX500Principal().getName()+"\n"+ + " Serial Number: "+cert.getSerialNumber()+"\n"+ + " Version: "+cert.getVersion()+"\n"+ + " Signature Algorithm: "+cert.getPublicKey().getAlgorithm()+"\n"+ + " Not Valid Before: "+cert.getNotBefore()+"\n"+ + " Not Valid After: "+cert.getNotAfter()+"\n"+ + " SHA1 Fingerprint: "+thumbprint+"\n"+ + "\n"+ + "Do you want to save it and continue?", + "Certificate Issuer Unknown", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, + null, answer, answer[0]); + if (ret == JOptionPane.YES_OPTION) { + File vncDir = new File(FileUtils.getVncHomeDir()); + if (!vncDir.exists() && !vncDir.mkdir()) { + vlog.info("Certificate save failed, unable to create ~/.vnc"); + return; + } + for (int i = 0; i < chain.length; i++) { + byte[] der = chain[i].getEncoded(); + String pem = DatatypeConverter.printBase64Binary(der); + pem = pem.replaceAll("(.{64})", "$1\n"); + FileWriter fw = null; + try { + String castore = + FileUtils.getVncHomeDir()+"x509_savedcerts.pem"; + fw = new FileWriter(castore, true); + fw.write("-----BEGIN CERTIFICATE-----\n"); + fw.write(pem+"\n"); + fw.write("-----END CERTIFICATE-----\n"); + } catch (IOException ioe) { + throw new Exception(ioe.getCause().getMessage()); + } finally { + try { + if (fw != null) + fw.close(); + } catch(IOException ioe2) { + throw new Exception(ioe2.getCause().getMessage()); + } + } + } + } else { + System.exit(1); + } + } else { + throw new Exception(e.getCause().getMessage()); + } } catch (java.lang.Exception e) { - throw new Exception(e.toString()); + throw new Exception(e.getCause().getMessage()); } } @@ -263,6 +339,7 @@ public class CSecurityTLS extends CSecurity { { return tm.getAcceptedIssuers(); } + } public final int getType() { return anon ? Security.secTypeTLSNone : Security.secTypeX509None; }