diff options
author | Simon Rettberg | 2015-08-18 19:49:30 +0200 |
---|---|---|
committer | Simon Rettberg | 2015-08-18 19:49:30 +0200 |
commit | 6e1b0f5a5da9bb8f73ff62166d09647d9d479ad8 (patch) | |
tree | 2b8dd3e12f96f2dbd5df8fa7caa1e8c34af06745 /dozentenmodul/src/main | |
parent | [client] Remove old unused class (diff) | |
download | tutor-module-6e1b0f5a5da9bb8f73ff62166d09647d9d479ad8.tar.gz tutor-module-6e1b0f5a5da9bb8f73ff62166d09647d9d479ad8.tar.xz tutor-module-6e1b0f5a5da9bb8f73ff62166d09647d9d479ad8.zip |
[client] Use TLS to talk to master and satellite
Diffstat (limited to 'dozentenmodul/src/main')
9 files changed, 228 insertions, 17 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/App.java b/dozentenmodul/src/main/java/org/openslx/dozmod/App.java index 29e6d961..4be5a34d 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/App.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/App.java @@ -8,11 +8,13 @@ import java.awt.event.ComponentEvent; import java.awt.event.ContainerEvent; import java.io.File; import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.net.ssl.SSLContext; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -35,6 +37,12 @@ public class App { // Logger private final static Logger LOGGER = Logger.getLogger(App.class); + public static final int THRIFT_PORT = 9090; + + public static final int THRIFT_SSL_PORT = THRIFT_PORT + 1; + + public static final int THRIFT_TIMEOUT_MS = 15000; + private static Thread proxyThread = null; private static void setupLogger() { @@ -141,7 +149,17 @@ public class App { // setup global thrift connection error handler before anything else // Set master server to use (TODO: make configurable via command line) - ThriftManager.setMasterServerAddress("bwlp-masterserver.ruf.uni-freiburg.de"); + try { + ThriftManager.setMasterServerAddress(SSLContext.getDefault(), "bwlp-masterserver.ruf.uni-freiburg.de", THRIFT_SSL_PORT, + THRIFT_TIMEOUT_MS); + } catch (final NoSuchAlgorithmException e1) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + Gui.showMessageBox(null, "SSL nicht verfügbar", MessageType.ERROR, LOGGER, e1); + } + }); + } SwingUtilities.invokeLater(new Runnable() { @Override diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/Config.java b/dozentenmodul/src/main/java/org/openslx/dozmod/Config.java index db384ecf..f242f31f 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/Config.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/Config.java @@ -85,7 +85,7 @@ public class Config { // Check if we got a path configFile = new File(configPath + File.separatorChar + "bwSuite" + File.separatorChar - + "config.properties"); + + "config.properties").getAbsoluteFile(); // Check if the directory exists. if (!configFile.getParentFile().exists()) { diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java index 4e0174d4..5b7b1ccd 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/EcpAuthenticator.java @@ -64,9 +64,10 @@ public class EcpAuthenticator implements Authenticator { } // create the session for the user from the response of the ECP List<Satellite> sats = new ArrayList<>(); + // TODO: Handle cert fingerprint if (response.satellites2 != null) { for (Entry<String, List<String>> it : response.satellites2.entrySet()) { - sats.add(new Satellite(it.getValue(), it.getKey())); + sats.add(new Satellite(it.getValue(), it.getKey(), null)); } } data = new AuthenticationData(response.token, response.sessionId, sats); diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/FingerprintManager.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/FingerprintManager.java new file mode 100644 index 00000000..58b0d305 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/FingerprintManager.java @@ -0,0 +1,49 @@ +package org.openslx.dozmod.authentication; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; +import org.openslx.dozmod.Config; + +public class FingerprintManager { + + private static final Logger LOGGER = Logger.getLogger(FingerprintManager.class); + + private static final File file = new File(Config.getPath(), "fingerprints.properties"); + private static final Properties prop = new Properties(); + + static { + if (file.exists()) { + try { + prop.load(new FileInputStream(file)); + } catch (IOException e) { + LOGGER.warn("Could not load cached fingerprints from " + file.toString()); + } + } + } + + public static void saveFingerprint(String address, byte[] fingerprint) { + saveFingerprint(address, fingerprint, true); + } + + public static void saveFingerprint(String address, byte[] fingerprint, boolean replace) { + if (replace || !prop.containsKey(address)) { + prop.setProperty(address, Base64.encodeBase64String(fingerprint)); + try { + prop.store(new FileOutputStream(file), "Written by bwLehrstuhl"); + } catch (IOException e) { + LOGGER.warn("Could not store fingerprint"); + } + } + } + + public static byte[] getFingerprint(String address) { + return Base64.decodeBase64(prop.getProperty(address)); + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java index 8868b53a..5d254d85 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/authentication/TestAccountAuthenticator.java @@ -34,7 +34,8 @@ public class TestAccountAuthenticator implements Authenticator { if (authResult != null && authResult.authToken != null) { LOGGER.info(authResult); List<Satellite> sats = new ArrayList<>(); - sats.add(new Satellite(Arrays.asList(new String[] { authResult.serverAddress }), "default")); + // TODO: Handle cert fingerprint + sats.add(new Satellite(Arrays.asList(new String[] { authResult.serverAddress }), "default", null)); AuthenticationData data = new AuthenticationData(authResult.authToken, authResult.sessionId, sats); callback.postLogin(ReturnCode.NO_ERROR, data, null); } else { 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<Boolean>() { + @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<ActivityPanel> activities = new ArrayList<>(); - /** * Set the visible page of the main window. * @@ -148,10 +147,10 @@ public abstract class MainWindow { return Gui.syncExec(new GuiCallable<Boolean>() { @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<Boolean>() { @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(); |