Truststores and Keystores done right

Java bringt seit jeher sämtliche Mittel zu Verschlüsselung des Netzwerkverkehrs mittels SSL mit. Leider ist die Konfiguration nicht immer trivial und gerade Anfänger tun sich mit der Ablage von Schlüsseln und Zertifikaten in ihren Anwendungen schwer, sodass durch leicht zugängliche Schlüssel jegliche Sicherheitsbemühungen ad absurdum geführt werden. An dieser Stelle möchte ich deshalb einige Best Practices zur Verwendung von Keystores und Truststores vermitteln, die sich dem Einsteiger aus der Java-Documentation nicht immer sofort erschließen. Die programmatische Vorbereitung und Verwendung von SSL-Verbindungen klammere ich allerdings bewusst aus, weil es dazu Unmengen an Tutorials und Dokumentationen gibt. Vielmehr möchte ich auf die Keystores und Truststores eingehen, die immer mal wieder zum Stolperstein werden.

Zertifikate und Truststores

Öffnet man mit Java eine Verbindung zu einer SSL-gesicherten Ressource (z. B. ein simpler Download über HTTPS), so ist Java selbst in der Lage, die von der Gegenstelle ausgelieferten Zertifikate zu validieren. Dazu bringt jede JRE einen eigenen Zertifikatespeicher mit, der sämtliche Root-Zertifikate großer Zertifizierungsorganisationen zusammenfasst. Dieser sogenannte Truststore liegt jeder Java-Installation unter /jre/security/cacerts bei. Die Datei cacerts ist eine Binärdatei im Java Keystore Format (JKS) und kann mit dem „keytool“, das jeder Java-Installation beiliegt, verwaltet werden. Wichtig zu wissen ist, dass jede Neuinstallation von Java als auch jedes Java-Update neue Zertifikate mit neuen Zertifizierungsstellen als auch zurückgezogene Zertifikatslisten mitbringt. Deshalb ist es ungemein wichtig, die cacerts der jeweils verwendeten JRE oder gleich das komplette JRE immer aktuell zu halten. (Wie man die von der gestarteten JVM verwendete JRE-Installation aufspürt, ist unter „Determine Version and Path of your Java Virtual Machine“ beschrieben)

Weil jede JVM-Instanz automatisch die cacerts der installierten JRE verwendet, muss man als Anwender üblicherweise wenig tun, um diesen Zertifikatespeicher zu nutzen. Alle Anwendungen vertrauen auf den Zertifikatespeicher und die Zertifikate der JRE, mit der die Anwendung ausgeführt wird.

pic-cacerts.png

Hat man jedoch seine eigene CA aufgebaut und möchte nun, dass die eigenen Java-Anwendungen allen damit ausgestellten Zertifikaten vertrauen, dann muss das entsprechende Root-Zertifikat in den Zertifikatespeicher aufgenommen werden. Sinnvollerweise bestückt man dazu die cacerts der verwendeten JREs und rollt die cacerts beispielsweise per Ferninstallation auf die entsprechenden Systeme aus. Alternativ kann man der JVM mit dem folgenden JVM-Parameter auch jeden anderen beliebigen Truststore im JKS-Format unterschieben:

-Djavax.net.ssl.trustStore=/path/to/myowntruststore

Umgekehrt kann man natürlich auch die cacerts selbst von sämtlichen Zertifikaten befreien und ausschließlich das Root-Zertifikat der eigenen CA dort installieren, um beispielsweise allen Zertifikaten außerhalb des eigenen Unternehmens zu misstrauen. Ohne gute Gründe ist davon jedoch eher abzuraten.

Schlüssel und Keystores

Möchte man hingegen in seinen eigenen Anwendungen selbst verschlüsseln, benötigt man natürlich seinen eigenen Schlüssel. Und auch wer seine eigene Tomcat-Instanz per HTTPS betreiben möchte, benötigt einen entsprechenden Schlüssel, um per SSL mit den Clients zu kommunizieren. Allerdings müssen die dazu notwendigen Schlüssel den jeweiligen Anwendungen bzw. der JVM übergeben werden, und zwar egal, ob es sich dabei um eine Desktop- oder Web-Anwendung handelt. Dazu übergibt man den Keystore im JKS-Format mit seinen Schlüsseln per JVM-Parameter an die JRE:

-Djavax.net.ssl.keyStore=/path/to/mykeystore.jks

Nicht selten laufen auf einem Host mehrere Anwendungen und Serverinstanzen und recht schnell hat man es aufgrund unterschiedlicher Vertrauensketten für die Keys der einzelnen Serverinstanzen mit mehreren Keys in mehreren Keystores zu tun. Dabei empfehle ich, für einen bestimmten Anwendungsbereich bzw. einen Besitzer immer einen separaten Keystore anzulegen. Kommuniziert eine Anwendung mit mehreren anderen Anwendung und benötigt dazu für jede Anwendung einen eigenen Schlüssel, so packt man alle Schlüssel in einen Keystore, der ausschließlich von dieser Anwendung verwendet wird. Läuft die Anwendung im Kontext eines bestimmten Users, ist es auch sinnvoll, den Keystore mit diesen Keys im Benutzerverzeichnis des Users abzulegen. Damit ist der Key nicht nur durch sein Passwort, sondern darüber hinaus auch auf File-Ebene vor Missbrauch geschützt. Um die Keystores identifizieren zu können, sollte man diese dann auch so benennen, dass aus dem Namen des Keystores der Besitzer oder die Java-Instanz hervorgehen, die diesen Keystore benutzen bzw. denen der Keystore gehört.

pic-keystores.png

Betreiben Sie beispielsweise einen Tomcat (Dienst) mit dem User „tomcat“ (Besitzer) dann bezeichnen Sie den Keystore am sinnvollsten so:

	/home/tomcat/tomcat.jks

Betreiben Sie dagegen mit dem User „servicerunner“ (Besitzer) einen Tomcat (Dienst) und außerdem mit dem gleichen User eine separate Anwendung xyApp (Dienst/Anwendung), dann packen Sie alle mit dem Tomcat in Verbindung stehenden Keys (erste JVM-Instanz) in einen Keystore; Alle Schlüssel, die für den Betrieb der Anwendung xyApp benötigt werden (zweite JVM-Instanz), packen Sie dagegen in einen zweiten Keystore. Beispielsweise so:

	/home/servicerunner/tomcat.jks
	/home/servicerunner/xyapp.jks

Betreiben Sie dagegen mit unterschiedlichen Usern gleichzeitig auf einem System mehrere Java-Anwendungen und beispielsweise auch mehrere Tomcat-Instanzen mit mehreren Connectoren bzw. Ports (z. B. weil sie eine eigene Testinstanz für eigene Servererweiterungen verwenden wollen und diese unter Port 9443 mit selbstsignierten Zertifikaten betreiben wollen), dann bezeichnen und bestücken Sie die Keystores beispielsweise so:

	/home/servicerunner/tomcat-prod-port8443.keystore für die Tomcat-Instanz PROD
	/home/servicerunner/xyapp.jks für die Anwendung xyApp
	/home/tomcat-test/tomcat-test-port9443.jks für die Tomcat-Instanz TEST

Resultierend daraus wissen Sie genau, welche User welchen Key verantworten und welche JVM-Instanzen damit betrieben werden. Diese Vorgehensweise lässt sich natürlich auch auf andere Java-basierte Anwendungen und Produkte adaptieren, sei es nun ein Produkt mit Eclipse-Basis oder ein schlichtes Tomcat-basiertes Produkt wie Atlassian JIRA oder Sonatype Nexus.

Ergänzend sei noch erwähnt, dass man mehrere Keys eines Anwenders oder Dienstes auch in einem Keystore zusammenfassen kann, dann aber muss die Anwendung in der Lage sein, aus mehreren Keys anhand eines vorgegebenen Key-Namen, den richtigen Key für den jeweiligen Verschlüsselungsfall auszuwählen. Bei der Konfiguration von Tomcat-Connectoren geht das beispielsweise über das Property „keyAlias“.

Truststores vs. Keystores

Wie oben aufgezeigt ist es dringend zu empfehlen, die Truststores mit den Zertifikaten von den Keystores mit den Schlüsseln strikt zu trennen. In der Regel ist es sinnvoll einen unternehmensweiten Truststore mit allen vertrauenswürdigen Zertifikaten zu pflegen und diesen auf alle Systeme auszurollen. Sofern eine Anwendung eigene Schlüssel verwenden soll, z. B. um eine SSL-Verschlüsselte Anwendung auf einem Tomcat anzubieten, sollten die dafür notwendigen Keys so auf unterschiedliche Keystores verteilt werden, dass immer nur der verantwortliche Dienst oder User darauf zugreifen kann. Damit wird ein Missbrauch deutlich erschwert. Es ist daher ratsam, sich vor dem Betrieb von eigenen Anwendungen nochmals zu vergewissern, dass die Keystores und Truststores wirklich nur die relevanten Schlüssel bzw. notwendigen Zertifikate enthalten und diese passend geschützt abgelegt sind.

Einsteigern empfehle ich den sehr komfortablen http://keystore-explorer.org/ von dem auch die Screenshots stammen. Gerade wen man anfangs mit sehr vielen Key- und Truststores operiert, gestaltet sich die Verwaltung damit wesentlich übersichtlicher und intuitiver als mit dem Kommandozeilenwerkzeug „keytool“. Das JDK-eigene „keytool“ spielt seine Stärken nämlich vorallem auf Servern aus, auf denen man in der Regel mit der Kommandozeile besser unterwegs ist.