From 6e1b0f5a5da9bb8f73ff62166d09647d9d479ad8 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 18 Aug 2015 19:49:30 +0200 Subject: [client] Use TLS to talk to master and satellite --- .../openslx/dozmod/gui/GraphicalCertHandler.java | 115 +++++++++++++++++++++ .../src/main/java/org/openslx/dozmod/gui/Gui.java | 21 ++++ .../java/org/openslx/dozmod/gui/MainWindow.java | 25 +++-- .../org/openslx/dozmod/gui/window/LoginWindow.java | 7 +- 4 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 dozentenmodul/src/main/java/org/openslx/dozmod/gui/GraphicalCertHandler.java (limited to 'dozentenmodul/src/main/java/org/openslx/dozmod/gui') diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/GraphicalCertHandler.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/GraphicalCertHandler.java new file mode 100644 index 00000000..07b44175 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/GraphicalCertHandler.java @@ -0,0 +1,115 @@ +package org.openslx.dozmod.gui; + +import java.math.BigInteger; +import java.security.KeyManagementException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.log4j.Logger; +import org.openslx.dozmod.authentication.FingerprintManager; +import org.openslx.dozmod.gui.Gui.GuiCallable; +import org.openslx.dozmod.gui.helper.MessageType; + +public class GraphicalCertHandler { + + private static final Logger LOGGER = Logger.getLogger(GraphicalCertHandler.GuiTrustManager.class); + + private class GuiTrustManager implements X509TrustManager { + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { + if (certs == null || certs.length == 0) { + Gui.asyncMessageBox( + "Der Satellit besitzt kein Zertifikat. Verschlüsselte Verbindung nicht möglich.\n\n" + + "Möchten Sie trotzdem fortfahren?", MessageType.WARNING, LOGGER, null); + // TODO: Ask and do + throw new CertificateException("No certificate provided by server"); + } + byte[] encoded = certs[0].getEncoded(); + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + LOGGER.warn("Could not get SHA-256 hash of certificate", e); + throw new CertificateException("Could not get SHA-256 hash of certificate"); + } + md.update(encoded); + byte[] actualFingerprint = md.digest(); + final String actualFingerprintReadable = new BigInteger(actualFingerprint).toString(16); + // Now check the fingerprint + byte[] expectedFingerprint = FingerprintManager.getFingerprint(address); + final String question; + if (expectedFingerprint == null) { + // Not known yet, ask + question = "Magst du die Zahl " + actualFingerprintReadable + "?"; + } else if (Arrays.equals(actualFingerprint, expectedFingerprint)) { + // Known, matches, everything's fine + return; + } else { + // Known, mismatch, panic! + question = "!!! ALARM !!!! ALARM !!! *trage hol*\n\n" + "Der Fingerabdruck von " + address + + " hat sich verändert.\n" + "Erwartet: " + + new BigInteger(expectedFingerprint).toString(16) + "\n" + "Vorgefunden: " + + actualFingerprintReadable + "\n\n" + + "Möchten Sie trotzdem zu diesem Satelliten verbinden?"; + } + // Some question needs to be asked + Boolean userOk = Gui.syncExec(new GuiCallable() { + @Override + public Boolean run() { + return Gui.showMessageBox(null, question, MessageType.QUESTION_YESNO, null, null); + } + }); + if (userOk) { + FingerprintManager.saveFingerprint(address, actualFingerprint); + } else { + throw new CertificateException("Rejected by user"); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + } + + private final String address; + + private final SSLContext sslContext; + + private GraphicalCertHandler(String address) { + SSLContext ctx = null; + try { + ctx = SSLContext.getInstance("TLSv1.2"); + } catch (NoSuchAlgorithmException e) { + Gui.asyncMessageBox("Could not get TLSv1.2 SSL context", MessageType.ERROR, LOGGER, e); + } + if (ctx != null) { + try { + ctx.init(null, new TrustManager[] { new GuiTrustManager() }, null); + } catch (KeyManagementException e) { + Gui.asyncMessageBox("Could not initialize TLSv1.2 SSL context", MessageType.ERROR, LOGGER, e); + ctx = null; + } + } + this.sslContext = ctx; + this.address = address; + } + + public static SSLContext getSslContext(String address) { + return new GraphicalCertHandler(address).sslContext; + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java index 0b961461..a40600fe 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/Gui.java @@ -271,4 +271,25 @@ public class Gui { return ret == JOptionPane.OK_OPTION || ret == JOptionPane.YES_OPTION; } + /** + * Show a message box to the user asynchronously, and optionally log the + * message to the log file. This is most useful when working from another + * thread. + * + * @param message Message to display. Can be multi line. + * @param messageType Type of message (warning, information) + * @param logger Logger instance to log to. Can be null. + * @param exception Exception related to this message. Can be null. + * @return true if OK or YES was clicked, false for CANCEL/NO/(X) + */ + public static void asyncMessageBox(final String message, final MessageType messageType, + final Logger logger, final Throwable exception) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + showMessageBox(null, message, messageType, logger, exception); + } + }); + } + } diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java index 2f9fbf50..f8c179b0 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/MainWindow.java @@ -64,7 +64,6 @@ public abstract class MainWindow { private static final List activities = new ArrayList<>(); - /** * Set the visible page of the main window. * @@ -148,10 +147,10 @@ public abstract class MainWindow { return Gui.syncExec(new GuiCallable() { @Override public Boolean run() { - return Gui.showMessageBox(mainWindow, "Die Kommunikation mit dem bwLehrpool-Zentralserver ist" - + " gestört. Der Aufruf der Funktion " + method - + " ist fehlgeschlagen.\n\n" - + "Möchten Sie den Aufruf wiederholen?", + return Gui.showMessageBox(mainWindow, + "Die Kommunikation mit dem bwLehrpool-Zentralserver ist" + + " gestört. Der Aufruf der Funktion " + method + + " ist fehlgeschlagen.\n\n" + "Möchten Sie den Aufruf wiederholen?", MessageType.ERROR_RETRY, LOGGER, t); } }); @@ -167,10 +166,10 @@ public abstract class MainWindow { return Gui.syncExec(new GuiCallable() { @Override public Boolean run() { - return Gui.showMessageBox(mainWindow, "Die Kommunikation mit dem Satellitenserver ist" - + " gestört. Der Aufruf der Funktion " + method - + " ist fehlgeschlagen.\n\n" - + "Möchten Sie den Aufruf wiederholen?", + return Gui.showMessageBox(mainWindow, + "Die Kommunikation mit dem Satellitenserver ist" + + " gestört. Der Aufruf der Funktion " + method + + " ist fehlgeschlagen.\n\n" + "Möchten Sie den Aufruf wiederholen?", MessageType.ERROR_RETRY, LOGGER, t); } }); @@ -251,10 +250,14 @@ public abstract class MainWindow { // Wait for proxy server init App.waitForInit(); try { - WhoamiInfo whoami = ThriftManager.getNewSatClient(session.address).whoami(session.token); + WhoamiInfo whoami = ThriftManager.getNewSatelliteClient( + GraphicalCertHandler.getSslContext(session.address), session.address, + App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS).whoami(session.token); // TODO: Satellite whoami call Session.initialize(whoami, session.address, session.token, session.masterToken); - ThriftManager.setSatelliteAddress(Session.getSatelliteAddress()); + ThriftManager.setSatelliteAddress( + GraphicalCertHandler.getSslContext(Session.getSatelliteAddress()), + Session.getSatelliteAddress(), App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS); LOGGER.info("Saved session used for resume."); } catch (Exception e1) { LOGGER.info("Session resume failed.", e1); diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java index 36b59eed..56a0607c 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LoginWindow.java @@ -32,6 +32,7 @@ import org.openslx.dozmod.authentication.EcpAuthenticator; import org.openslx.dozmod.authentication.ShibbolethEcp; import org.openslx.dozmod.authentication.ShibbolethEcp.ReturnCode; import org.openslx.dozmod.authentication.TestAccountAuthenticator; +import org.openslx.dozmod.gui.GraphicalCertHandler; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.MainWindow; import org.openslx.dozmod.gui.helper.MessageType; @@ -333,7 +334,8 @@ public class LoginWindow extends LoginWindowLayout { // TODO: Show satellite selection if > 1 //String satAddress = data.satellites.get(0).addressList.get(0); String satAddress = "132.230.8.113"; // TODO: HACK HACK - Client client = ThriftManager.getNewSatClient(satAddress); + Client client = ThriftManager.getNewSatelliteClient(GraphicalCertHandler.getSslContext(satAddress), satAddress, + App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS); if (client == null) { Gui.showMessageBox(this, "Login erfolgreich, aber der Satellit antwortet nicht", MessageType.ERROR, LOGGER, null); @@ -360,7 +362,8 @@ public class LoginWindow extends LoginWindowLayout { } if (whoami != null) { Session.initialize(whoami, satAddress, data.satelliteToken, data.masterToken); - ThriftManager.setSatelliteAddress(Session.getSatelliteAddress()); + ThriftManager.setSatelliteAddress(GraphicalCertHandler.getSslContext(Session.getSatelliteAddress()), + Session.getSatelliteAddress(), App.THRIFT_SSL_PORT, App.THRIFT_TIMEOUT_MS); // now read the config to see if the user already agreed to the disclaimer // if (DisclaimerWindow.shouldBeShown()) // VirtualizerNoticeWindow.open(); -- cgit v1.2.3-55-g7522