diff --git a/kse/build.gradle b/kse/build.gradle index 8e3cf487..b32c7ff2 100644 --- a/kse/build.gradle +++ b/kse/build.gradle @@ -112,6 +112,8 @@ dependencies { implementation('org.violetlib:vaqua:11') implementation('io.github.java-diff-utils:java-diff-utils:4.12') implementation('org.openjdk.nashorn:nashorn-core:15.4') + implementation('io.github.hakky54:sslcontext-kickstart:8.2.0') + implementation('org.slf4j:slf4j-simple:2.0.9') testImplementation('org.assertj:assertj-core:3.23.1') testImplementation('org.junit.jupiter:junit-jupiter-api:5.9.0') diff --git a/kse/src/main/java/org/kse/gui/actions/ExamineSslAction.java b/kse/src/main/java/org/kse/gui/actions/ExamineSslAction.java index 1e5e0cd2..7871e3da 100644 --- a/kse/src/main/java/org/kse/gui/actions/ExamineSslAction.java +++ b/kse/src/main/java/org/kse/gui/actions/ExamineSslAction.java @@ -21,7 +21,9 @@ import java.awt.Toolkit; import java.awt.event.InputEvent; +import java.security.cert.X509Certificate; import java.text.MessageFormat; +import java.util.List; import javax.swing.ImageIcon; import javax.swing.KeyStroke; @@ -31,8 +33,6 @@ import org.kse.gui.dialogs.DExaminingSsl; import org.kse.gui.dialogs.DViewCertificate; import org.kse.gui.error.DError; -import org.kse.utilities.history.KeyStoreHistory; -import org.kse.utilities.ssl.SslConnectionInfos; /** * Action to examine an SSL connection's certificates. @@ -70,27 +70,25 @@ protected void doAction() { String sslHost = dExamineSsl.getSslHost(); int sslPort = dExamineSsl.getSslPort(); - boolean useClientAuth = dExamineSsl.useClientAuth(); - KeyStoreHistory ksh = dExamineSsl.getKeyStore(); if (dExamineSsl.wasCancelled()) { return; } - DExaminingSsl dExaminingSsl = new DExaminingSsl(frame, sslHost, sslPort, useClientAuth, ksh); + DExaminingSsl dExaminingSsl = new DExaminingSsl(frame, sslHost, sslPort); dExaminingSsl.setLocationRelativeTo(frame); dExaminingSsl.startExamination(); dExaminingSsl.setVisible(true); - SslConnectionInfos sslInfos = dExaminingSsl.getSSLConnectionInfos(); + List certificates = dExaminingSsl.getServerCertificates(); - if (sslInfos == null || sslInfos.getServerCertificates() == null) { + if (certificates.isEmpty()) { return; } DViewCertificate dViewCertificate = new DViewCertificate(frame, MessageFormat.format( res.getString("ExamineSslAction.CertDetailsSsl.Title"), sslHost, Integer.toString(sslPort)), - sslInfos.getServerCertificates(), kseFrame, + certificates.toArray(new X509Certificate[0]), kseFrame, DViewCertificate.IMPORT_EXPORT); dViewCertificate.setLocationRelativeTo(frame); dViewCertificate.setVisible(true); diff --git a/kse/src/main/java/org/kse/gui/dialogs/DExamineSsl.java b/kse/src/main/java/org/kse/gui/dialogs/DExamineSsl.java index 2bdf4304..3deecadf 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DExamineSsl.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DExamineSsl.java @@ -30,25 +30,20 @@ import javax.swing.AbstractAction; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; -import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; -import javax.swing.JSeparator; import javax.swing.KeyStroke; import org.kse.gui.preferences.ApplicationSettings; -import org.kse.crypto.Password; import org.kse.gui.JEscDialog; import org.kse.gui.KseFrame; import org.kse.gui.MiGUtil; import org.kse.gui.PlatformUtil; -import org.kse.gui.actions.OpenAction; import org.kse.utilities.DialogViewer; import org.kse.utilities.history.KeyStoreHistory; @@ -68,9 +63,6 @@ public class DExamineSsl extends JEscDialog { private JComboBox jcbSslHost; private JLabel jlSslPort; private JComboBox jcbSslPort; - private JCheckBox jcbClientAuth; - private JComboBox jcbKeyStore; - private JButton jbLoadKeystore; private JPanel jpButtons; private JButton jbOK; private JButton jbCancel; @@ -108,15 +100,6 @@ private void initComponents() { jcbSslPort.setToolTipText(res.getString("DExamineSsl.jtfSslPort.tooltip")); jcbSslPort.setModel(new DefaultComboBoxModel<>(getSslPorts())); - jcbClientAuth = new JCheckBox(res.getString("DExamineSsl.jlEnableClientAuth.text")); - - jcbKeyStore = new JComboBox<>(getKeystoreNames()); - jcbKeyStore.setToolTipText(res.getString("DExamineSsl.jcbKeyStore.tooltip")); - - jbLoadKeystore = new JButton(); - jbLoadKeystore.setIcon(new ImageIcon(getClass().getResource("images/open.png"))); - jbLoadKeystore.setToolTipText(res.getString("DExamineSsl.jbLoadKeystore.tooltip")); - jbOK = new JButton(res.getString("DExamineSsl.jbOK.text")); jbCancel = new JButton(res.getString("DExamineSsl.jbCancel.text")); @@ -132,20 +115,8 @@ private void initComponents() { pane.add(jcbSslHost, "sgx, growx, wrap"); pane.add(jlSslPort, "skip"); pane.add(jcbSslPort, "sgx, growx, wrap para"); - MiGUtil.addSeparator(pane, res.getString("DExamineSsl.jlClientAuth.text")); - pane.add(jcbClientAuth, "left, spanx, wrap"); - pane.add(new JLabel(res.getString("DExamineSsl.jlKeyStore.text")), "skip"); - pane.add(jcbKeyStore, "sgx, growx"); - pane.add(jbLoadKeystore, "growx, wrap para"); - pane.add(new JSeparator(), "spanx, growx, wrap"); pane.add(jpButtons, "right, spanx"); - jcbClientAuth.addActionListener(evt -> updateClientAuthComponents()); - jbLoadKeystore.addActionListener(evt -> { - OpenAction openAction = new OpenAction(kseFrame); - openAction.actionPerformed(evt); - updateClientAuthComponents(); - }); jbOK.addActionListener(evt -> okPressed()); jbCancel.addActionListener(evt -> cancelPressed()); @@ -165,8 +136,6 @@ public void windowClosing(WindowEvent evt) { } }); - updateClientAuthComponents(); - getRootPane().setDefaultButton(jbOK); setResizable(false); @@ -197,24 +166,6 @@ public int getSslPort() { return sslPort; } - /** - * User wants to use SSL client authentication? - * - * @return True if user wants to use SSL client authentication - */ - public boolean useClientAuth() { - return jcbClientAuth.isSelected(); - } - - /** - * Get selected key store - * - * @return KeyStore (wrapped in a history object) - */ - public KeyStoreHistory getKeyStore() { - return (KeyStoreHistory) jcbKeyStore.getSelectedItem(); - } - /** * Was the dialog cancelled? * @@ -234,13 +185,6 @@ private String[] getSslPorts() { return sslPorts.split(";"); } - private void updateClientAuthComponents() { - jcbKeyStore.setEnabled(jcbClientAuth.isSelected()); - jbLoadKeystore.setEnabled(jcbClientAuth.isSelected()); - - jcbKeyStore.setModel(getKeystoreNames()); - } - private void okPressed() { String sslHost = ((String) jcbSslHost.getSelectedItem()).trim(); @@ -277,23 +221,6 @@ private void okPressed() { this.sslHost = sslHost; this.sslPort = sslPort; - // check selected key store - if (useClientAuth()) { - KeyStoreHistory ksh = (KeyStoreHistory) jcbKeyStore.getSelectedItem(); - if (ksh == null) { - JOptionPane.showMessageDialog(this, res.getString("DExamineSsl.NoKeyStoreSelected.message"), getTitle(), - JOptionPane.WARNING_MESSAGE); - return; - } - - Password keyStorePassword = ksh.getCurrentState().getPassword(); - if (keyStorePassword == null && ksh.getCurrentState().getType().hasEntryPasswords()) { - JOptionPane.showMessageDialog(this, res.getString("DExamineSsl.NoPasswordSetForKeyStore.message"), - getTitle(), JOptionPane.WARNING_MESSAGE); - return; - } - } - // save host/port in preferences applicationSettings.addSslHost(sslHost); applicationSettings.addSslPort(sslPortStr); diff --git a/kse/src/main/java/org/kse/gui/dialogs/DExaminingSsl.java b/kse/src/main/java/org/kse/gui/dialogs/DExaminingSsl.java index caf895cd..9b908088 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DExaminingSsl.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DExaminingSsl.java @@ -26,8 +26,10 @@ import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.security.KeyStore; +import java.net.URL; +import java.security.cert.X509Certificate; import java.text.MessageFormat; +import java.util.List; import java.util.ResourceBundle; import javax.swing.AbstractAction; @@ -43,14 +45,11 @@ import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import org.kse.crypto.Password; +import nl.altindag.ssl.util.CertificateUtils; import org.kse.gui.JEscDialog; import org.kse.gui.PlatformUtil; import org.kse.gui.error.DProblem; import org.kse.gui.error.Problem; -import org.kse.utilities.history.KeyStoreHistory; -import org.kse.utilities.ssl.SslConnectionInfos; -import org.kse.utilities.ssl.SslUtils; /** * Examines an SSL connection's certificates - a process which the user may @@ -72,9 +71,7 @@ public class DExaminingSsl extends JEscDialog { private String sslHost; private int sslPort; - private KeyStore keyStore; - private char[] password; - private SslConnectionInfos sslInfos; + private List serverCertificates; private Thread examiner; /** @@ -83,24 +80,13 @@ public class DExaminingSsl extends JEscDialog { * @param parent The parent frame * @param sslHost SSL connection's host name * @param sslPort SSL connection's port number - * @param useClientAuth Try to connect with client certificate - * @param ksh KeyStore with client certificate */ - public DExaminingSsl(JFrame parent, String sslHost, int sslPort, boolean useClientAuth, KeyStoreHistory ksh) { + public DExaminingSsl(JFrame parent, String sslHost, int sslPort) { super(parent, Dialog.ModalityType.DOCUMENT_MODAL); this.sslHost = sslHost; this.sslPort = sslPort; - if (useClientAuth) { - this.keyStore = ksh.getCurrentState().getKeyStore(); - - // some keystore types like MSCAPI and PKCS#11 have no password stored in their state - Password pwd = ksh.getCurrentState().getPassword(); - if (pwd != null) { - this.password = pwd.toCharArray(); - } - } initComponents(); } @@ -166,14 +152,8 @@ public void startExamination() { examiner.start(); } - /** - * Get the SSL connection's certificates and some details like protocol version or cipher suite. - * - * @return The SSL connection's details or null if the user cancelled - * the dialog or an error occurred - */ - public SslConnectionInfos getSSLConnectionInfos() { - return sslInfos; + public List getServerCertificates() { + return serverCertificates; } private void cancelPressed() { @@ -192,7 +172,8 @@ private class ExamineSsl implements Runnable { @Override public void run() { try { - sslInfos = SslUtils.readSSLConnectionInfos(sslHost, sslPort, keyStore, password); + URL url = new URL(MessageFormat.format("https://{0}:{1}/", sslHost, "" + sslPort)); + serverCertificates = CertificateUtils.getCertificatesFromExternalSource(url.toString()); SwingUtilities.invokeLater(() -> { if (DExaminingSsl.this.isShowing()) { diff --git a/kse/src/main/java/org/kse/utilities/ssl/CustomSslSocketFactory.java b/kse/src/main/java/org/kse/utilities/ssl/CustomSslSocketFactory.java deleted file mode 100644 index ab79215a..00000000 --- a/kse/src/main/java/org/kse/utilities/ssl/CustomSslSocketFactory.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2004 - 2013 Wayne Grant - * 2013 - 2023 Kai Kramer - * - * This file is part of KeyStore Explorer. - * - * KeyStore Explorer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeyStore Explorer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeyStore Explorer. If not, see . - */ -package org.kse.utilities.ssl; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; - -import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -/** - * Custom SSL factory that adds a HandshakeCompletedListener to the SSLSocket. - */ -public class CustomSslSocketFactory extends SSLSocketFactory { - - private final SSLSocketFactory sslSocketFactory; - - private HandshakeCompletedListener handshakeListener; - - private boolean sniEnabled; - - /** - * Constructor - * - * @param sslSocketFactory The actual SSLSocketFactory (used by this class) - * @param handshakeListener The class that handles "handshake completed" events - */ - public CustomSslSocketFactory(SSLSocketFactory sslSocketFactory, HandshakeCompletedListener handshakeListener, - boolean sniEnabled) { - this.sslSocketFactory = sslSocketFactory; - this.handshakeListener = handshakeListener; - this.sniEnabled = sniEnabled; - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - - SSLSocket socket = (SSLSocket) this.sslSocketFactory.createSocket(s, host, port, autoClose); - - if (!sniEnabled) { - disableSNI(socket); - } - - if (this.handshakeListener != null) { - socket.addHandshakeCompletedListener(this.handshakeListener); - } - - return socket; - } - - private void disableSNI(SSLSocket socket) { - // effectively disable SNI by passing an empty server name list (works only in Java 8 or higher) - SSLParameters sslParameters = socket.getSSLParameters(); - Method setServerNamesMethod; - try { - setServerNamesMethod = sslParameters.getClass().getMethod("setServerNames", List.class); - setServerNamesMethod.invoke(sslParameters, new ArrayList<>()); - socket.setSSLParameters(sslParameters); - } catch (Exception e) { - // Java 6/7, nothing we can do here (setting jsse.enableSNIExtension wouldn't work here anymore) - } - } - - @Override - public Socket createSocket(String paramString, int paramInt) throws IOException { - - SSLSocket socket = (SSLSocket) this.sslSocketFactory.createSocket(paramString, paramInt); - - if (this.handshakeListener != null) { - socket.addHandshakeCompletedListener(this.handshakeListener); - } - - return socket; - } - - @Override - public Socket createSocket(String paramString, int paramInt1, InetAddress paramInetAddress, int paramInt2) - throws IOException { - - SSLSocket socket = (SSLSocket) this.sslSocketFactory.createSocket(paramString, paramInt1, paramInetAddress, - paramInt2); - - if (this.handshakeListener != null) { - socket.addHandshakeCompletedListener(this.handshakeListener); - } - - return socket; - } - - @Override - public Socket createSocket(InetAddress paramInetAddress, int paramInt) throws IOException { - - SSLSocket socket = (SSLSocket) this.sslSocketFactory.createSocket(paramInetAddress, paramInt); - - if (this.handshakeListener != null) { - socket.addHandshakeCompletedListener(this.handshakeListener); - } - - return socket; - } - - @Override - public Socket createSocket(InetAddress paramInetAddress1, int paramInt1, InetAddress paramInetAddress2, - int paramInt2) throws IOException { - - SSLSocket socket = (SSLSocket) this.sslSocketFactory.createSocket(paramInetAddress1, paramInt1, - paramInetAddress2, paramInt2); - - if (this.handshakeListener != null) { - socket.addHandshakeCompletedListener(this.handshakeListener); - } - - return socket; - } - - @Override - public String[] getDefaultCipherSuites() { - return this.sslSocketFactory.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return this.sslSocketFactory.getSupportedCipherSuites(); - } - -} \ No newline at end of file diff --git a/kse/src/main/java/org/kse/utilities/ssl/RetrieveSslInfosHandshakeListener.java b/kse/src/main/java/org/kse/utilities/ssl/RetrieveSslInfosHandshakeListener.java deleted file mode 100644 index 73176f88..00000000 --- a/kse/src/main/java/org/kse/utilities/ssl/RetrieveSslInfosHandshakeListener.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2004 - 2013 Wayne Grant - * 2013 - 2023 Kai Kramer - * - * This file is part of KeyStore Explorer. - * - * KeyStore Explorer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeyStore Explorer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeyStore Explorer. If not, see . - */ -package org.kse.utilities.ssl; - -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.util.Arrays; - -import javax.net.ssl.HandshakeCompletedEvent; -import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; - -public class RetrieveSslInfosHandshakeListener implements HandshakeCompletedListener { - - private SslConnectionInfos sslConnectionInfos = new SslConnectionInfos(); - - @Override - public void handshakeCompleted(HandshakeCompletedEvent event) { - - SSLSession session = event.getSession(); - sslConnectionInfos.setPeerHost(session.getPeerHost()); - sslConnectionInfos.setPeerPort(session.getPeerPort()); - sslConnectionInfos.setProtocol(session.getProtocol()); - sslConnectionInfos.setCipherSuite(session.getCipherSuite()); - - Certificate[] locChain = session.getLocalCertificates(); - if (locChain != null) { - X509Certificate[] clientCertificates = Arrays.copyOf(locChain, locChain.length, X509Certificate[].class); - sslConnectionInfos.setClientCertificates(clientCertificates); - } - - try { - Certificate[] chain = session.getPeerCertificates(); - if (chain != null) { - X509Certificate[] serverCertificates = Arrays.copyOf(chain, chain.length, X509Certificate[].class); - sslConnectionInfos.setServerCertificates(serverCertificates); - } - } catch (SSLPeerUnverifiedException e) { - // do nothing - } - } - - public SslConnectionInfos getSslConnectionInfos() { - return sslConnectionInfos; - } -} \ No newline at end of file diff --git a/kse/src/main/java/org/kse/utilities/ssl/SslConnectionInfos.java b/kse/src/main/java/org/kse/utilities/ssl/SslConnectionInfos.java deleted file mode 100644 index 5a032cbd..00000000 --- a/kse/src/main/java/org/kse/utilities/ssl/SslConnectionInfos.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2004 - 2013 Wayne Grant - * 2013 - 2023 Kai Kramer - * - * This file is part of KeyStore Explorer. - * - * KeyStore Explorer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeyStore Explorer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeyStore Explorer. If not, see . - */ -package org.kse.utilities.ssl; - -import java.security.cert.X509Certificate; - -/** - * Data transfer class for SSL infos. - */ -public class SslConnectionInfos { - - String peerHost; - int peerPort; - String protocol; - String cipherSuite; - boolean sniEnabled; - X509Certificate[] serverCertificates; - X509Certificate[] clientCertificates; - - public String getPeerHost() { - return peerHost; - } - - public void setPeerHost(String peerHost) { - this.peerHost = peerHost; - } - - public int getPeerPort() { - return peerPort; - } - - public void setPeerPort(int peerPort) { - this.peerPort = peerPort; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getCipherSuite() { - return cipherSuite; - } - - public void setCipherSuite(String cipherSuite) { - this.cipherSuite = cipherSuite; - } - - public boolean isSniEnabled() { - return sniEnabled; - } - - public void setSniEnabled(boolean sniEnabled) { - this.sniEnabled = sniEnabled; - } - - public X509Certificate[] getServerCertificates() { - return serverCertificates; - } - - public void setServerCertificates(X509Certificate[] serverCertificates) { - this.serverCertificates = serverCertificates; - } - - public X509Certificate[] getClientCertificates() { - return clientCertificates; - } - - public void setClientCertificates(X509Certificate[] clientCertificates) { - this.clientCertificates = clientCertificates; - } -} diff --git a/kse/src/main/java/org/kse/utilities/ssl/SslUtils.java b/kse/src/main/java/org/kse/utilities/ssl/SslUtils.java deleted file mode 100644 index ddf9d195..00000000 --- a/kse/src/main/java/org/kse/utilities/ssl/SslUtils.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2004 - 2013 Wayne Grant - * 2013 - 2023 Kai Kramer - * - * This file is part of KeyStore Explorer. - * - * KeyStore Explorer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeyStore Explorer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeyStore Explorer. If not, see . - */ -package org.kse.utilities.ssl; - -import java.io.IOException; -import java.net.URL; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.text.MessageFormat; -import java.util.ResourceBundle; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLProtocolException; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; - -import org.kse.crypto.CryptoException; - -public class SslUtils { - - private static ResourceBundle res = ResourceBundle.getBundle("org/kse/utilities/ssl/resources"); - - private SslUtils() { - } - - /** - * Load certificates from an SSL connection. - * - * @param host Connection host - * @param port Connection port - * @param keyStore KeyStore with a key pair for SSL client authentication - * @param password The password for the KeyStore - * @return SSL infos - * @throws CryptoException Problem encountered while loading the certificate(s) - * @throws IOException An I/O error occurred - */ - public static SslConnectionInfos readSSLConnectionInfos(String host, int port, KeyStore keyStore, char[] password) - throws CryptoException, IOException { - - URL url = new URL(MessageFormat.format("https://{0}:{1}/", host, "" + port)); - HttpsURLConnection connection = null; - - System.setProperty("javax.net.debug", "ssl"); - - try { - connection = (HttpsURLConnection) url.openConnection(); - - // create a key manager for client authentication - X509KeyManager km = null; - if (keyStore != null) { - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509", "SunJSSE"); - keyManagerFactory.init(keyStore, password); - for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) { - if (keyManager instanceof X509KeyManager) { - km = (X509KeyManager) keyManager; - break; - } - } - } - - // We are only interested in getting the SSL certificates even if they are invalid - // either in and of themselves or for the host name they are associated with - - // 1) set connection's SSL Socket factory to have a very trusting trust manager - SSLContext context = SSLContext.getInstance("TLS"); - X509TrustingManager tm = new X509TrustingManager(); - context.init(new KeyManager[] { km }, new TrustManager[] { tm }, null); - - // 2) set a host name verifier that always verifies the host name - connection.setHostnameVerifier((hostname, sslSession) -> true); - - // register our handshake completed listener in order to retrieve SSL connection infos later - SSLSocketFactory factory = context.getSocketFactory(); - RetrieveSslInfosHandshakeListener handshakeListener = new RetrieveSslInfosHandshakeListener(); - boolean sniEnabled = true; - connection.setSSLSocketFactory(new CustomSslSocketFactory(factory, handshakeListener, sniEnabled)); - - try { - connection.connect(); - } catch (SSLProtocolException e) { - // handle server misconfiguration (works only in Java 8 or higher) - if (e.getMessage().contains("unrecognized_name")) { - sniEnabled = false; - connection.setSSLSocketFactory(new CustomSslSocketFactory(factory, handshakeListener, sniEnabled)); - connection.connect(); - } else { - throw e; - } - } - - // this is necessary in order to cause a handshake exception when the client cert is not accepted - if (keyStore != null) { - connection.getResponseMessage(); - } - - SslConnectionInfos sslConnectionInfos = handshakeListener.getSslConnectionInfos(); - sslConnectionInfos.setSniEnabled(sniEnabled); - - return sslConnectionInfos; - - } catch (GeneralSecurityException ex) { - throw new CryptoException(res.getString("NoLoadCertificate.exception.message"), ex); - } finally { - if (connection != null) { - connection.disconnect(); - } - } - } - - /** - * Implementation of the X509TrustManager. In this implementation we - * always trust the server as we are only interested in getting its - * certificates for examination. - */ - private static class X509TrustingManager implements X509TrustManager { - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - throw new UnsupportedOperationException(); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - } -} diff --git a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties index cd25d07c..6856bdff 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -14,20 +14,13 @@ DCompareCertificates.Title = Compare Certificates ''{0}'' vs ''{1}'' DCompareCertificates.jbOK.text = Ok DCompareCertificates.jlMatch.text = Matching: {0}% -DExamineSsl.NoKeyStoreSelected.message = No KeyStore selected. -DExamineSsl.NoPasswordSetForKeyStore.message = Cannot use KeyStore if no password is set for it. DExamineSsl.PositiveIntegerSslPortReq.message = SSL port must be a positive integer. DExamineSsl.SslHostReq.message = SSL Host required. DExamineSsl.SslPortReq.message = SSL Port required. DExamineSsl.Title = Examine SSL DExamineSsl.jbCancel.text = Cancel -DExamineSsl.jbLoadKeystore.tooltip = Open an existing KeyStore from disk DExamineSsl.jbOK.text = OK -DExamineSsl.jcbKeyStore.tooltip = Select the KeyStore that is being used for SSL client authentication -DExamineSsl.jlClientAuth.text = Client Authentication DExamineSsl.jlConnSettings.text = Connection Settings -DExamineSsl.jlEnableClientAuth.text = Enable Client Authentication -DExamineSsl.jlKeyStore.text = KeyStore: DExamineSsl.jlSslHost.text = SSL Host: DExamineSsl.jlSslPort.text = SSL Port: DExamineSsl.jtfSslHost.tooltip = Host name of SSL connection to examine