JAR-Archiv signieren

Seit Oracle Java 7 Update 51 (1.7.0_51-b13) werden selbstsignierte JAR-Archive durch die standardmäßigen Sicherheitseinstellungen blockiert. Da das Herabsetzen der Sicherheitseinstellungen von „Sehr hoch“ auf „Mittel“ den Benutzern nicht zugemutet werden sollte bzw. kann, empfiehlt sich stattdessen das JAR-Archiv mit einem offiziellen Zertifikat zu signieren.

Nachdem im Herbst und Winter 2013 beinahe unzählige Sicherheitslücken in Oracle Java 6 und 7 gefunden bzw. geschlossen wurden, die auch per Java Web Start oder im Webbrowser gestartete Java-Applets betreffen und das Java-Plugin sogar bei einigen Browser-Herstellern auf der Blacklist gelandet ist, hat Oracle mit Java 7 Update 51 (1.7.0_51-b13) die Sicherheitseinstellungen angepasst, so dass selbstsignierte JAR-Archive absofort standardmäßig blockiert werden. Das hat den Vorteil, dass sowohl gutartige als eben auch eventuell bösartige Java-Applets nun mit einem gekauften SSL-Zertifikat signiert sein müssen – das dürfte in der Praxis den Vorteil haben, dass die Zahl der schädlichen Java-Applets zum Ausnutzen von Sicherheitslücken in Java zurückgehen dürfte. Abgesehen davon gibt ein mit einem offiziellen Zertifikat signiertes Java-Applet dem Benutzer ein gewisses Gefühl an Vertrauen und Sicherheit ein legitimes Java-Applet zu nutzen und nicht z.B. einem Man-in-the-middle-Angriff unterlegen zu sein.

Doch beim Zertifikat fangen die Herausforderungen bzw. Probleme bereits an: Benötigt wird nämlich nicht ein klassisches SSL-Zertifikat wie es z.B. bei Web- oder Mailservern zum Einsatz kommt, sondern ein spezielles „Code Signing“-Zertifikat. Technisch ist dieses jedoch weniger speziell, es handelt sich um ein normales Zertifikat im X.509-System, das dann im „Common Name“ (kurz CN) nicht den Domainnamen sondern den Firmennamen trägt.

Wichtig ist, dass das „Code Signing“-Zertifikat für Java beantragt bzw. ausgestellt wird, da nicht alle Zertifizierungsstellen (auch „Certificate Authority“ genannt) zwangsläufig Java abdecken können. Ein „Code Signing“-Zertifikat für „Microsoft Authenticode“ können verhältnismäßig viele Zertifizierungsstellen ausstellen; damit kann das JAR-Archiv natürlich signiert werden, jedoch ist die Root-CA nicht in Java hinterlegt und Java betrachtet es bei der Ausführung als selbstsigniertes Java-Applet. Darüber hinaus sind nur im proprietären Oracle Java auf allen Betriebssystemen und Plattformen die gleichen Root-Zertifikate enthalten. Freie Java-Implementationen wie z.B. OpenJDK greifen meist auf die Root-Zertifikate des Betriebssystems zurück. Diese beinhalten normalerweise mindestens immer die gleichen Root-Zertifikate wie im proprietären Oracle Java, jedoch oftmals noch zusätzliche Root-Zertifikate. Wählt man dann die Zertifizierungsstelle einer dieser zusätzlichen Root-Zertifikate führt dies in Oracle Java ebenfalls zur Wertung eines selbstsignierten Java-Applets. Selbst wenn einige jüngere Zertifizierungsstellen preisgünstiger scheinen, so empfiehlt sich meines Erachtens immer der Gang zu einer langjährigen Zertifizierungsstelle wie Thawte, da die Benutzer sonst möglicherweise bei einer älteren Java-Version das Nachsehen haben – nicht alle Zertifizierungsstellen sind bereits in beispielsweise Sun Java 6 enthalten. Und obwohl Java 6 eingestellt ist, hindert das manche Benutzer nicht es weiterhin einzusetzen. Manchmal gibt es dafür (leider) auch zwingende technische Gründe. Letztendlich weiß man in den allermeisten Fällen vermutlich nicht welche Java-Version der Benutzer einsetzt und sollte als Entwickler auf Nummer sicher gehen und das Geld für das teurere „Code Signing“-Zertifikat investieren.

Ebenfalls wichtig ist, dass sämtliche Zertifizierungsstellen, die „Code Signing“-Zertifikate für Java ausstellen, dies ausschließlich für Firmen bzw. Organisationen tun. Dies macht privaten Java-Entwicklern das Leben unnötig schwer und einen Bekannten mit einer eigenen Firma zu fragen löst die Dinge nicht unbedingt: Im Zertifikat steht, wie bereits zuvor erwähnt, im „Common Name“ der Name der Firma bzw. Organisation. Dieser „Common Name“ wird dem Benutzer auch beim Start des Java-Applets angezeigt. Abgesehen davon schreiben die AGB der Zertifizierungsstellen meist vor, dass der Antragssteller der spätere Nutzer sein muss. Verstöße werden in der Praxis beim Bekanntwerden mit dem Widerruf des Zertifikats geahndet; diese Zertifikatsperrliste (auch CRL genannt) wird von Java berücksichtigt und führt beim Start des Java-Applets zu entsprechenden Warnmeldungen für den Benutzer.

Von der trockenen Theorie in die Praxis: Die im Internet auf so ziemlich jedem mit Halbwissen überhäuftem Blog oder in Foren vorgeschlagenen Java-Programme wie „ImportKey“ waren in der Zeit bis einschließlich Java 5 relevant. Seit Java 6 können Zertifikate und private Schlüssel ohne eigene „ImportKey“-Programme direkt mit dem keytool in den sogenannten Java KeyStore (JKS) importiert werden. Da ich persönlich kein begeisterter Java-Nutzer bin, in der Praxis teilweise (ungewollte) Inkompatibilitäten bei den Java KeyStores zwischen verschiedenen Java-Versionen-/Implementationen bestehen und bei Verwendung einer Versionsverwaltung binäre JKS-Dateien meiner Meinung nach sowieso vermieden werden sollten, verwende ich im nachfolgenden Beispiel so weit wie möglich das bewährte openssl. Im ersten Schritt wird der Certificate Signing Request (kurz CSR) erstellt:

robert@tux:~ > openssl req -nodes -new -newkey rsa:4096 -sha256 -out csr.pem
Generating a 4096 bit RSA private key
....++
....++
writing new private key to 'privkey.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:
AQ
State or Province Name (full name) []:Antarktika
Locality Name (eg, city) [Default City]:Snow Hill Island
Organization Name (eg, company) [Default Company Ltd]:Tux OHG
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:Tux OHG
Email Address []:tux@example.net

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:

An optional company name []:
robert@tux:~ >

Die dabei erzeugte Datei csr.pem ist der Certificate Signing Request, der private Schlüssel befindet sich in der Datei privkey.pem. Letzterer sollte immer geschützt und niemals aus der Hand gegeben werden, während der CSR hingegen an die Zertifizierungsstelle zum Signieren übergeben wird. Während man auf die Zertifizierungsstelle wartet, kann man beispielsweise bereits die verschiedenen Zertifikate der Zertifikatskette in eine PEM-Datei zusammenführen. Die Reihenfolge ist dabei entscheidend; begonnen wird mit dem Zwischenzertifikat (auch „Intermediate CA“ genannt), welches das eigene Zertifikat signiert, über eventuelle weitere Zwischenzertifikate bis hin zum Root-Zertifikat. Die genaue Zertifikatskette hängt von der Zertifizierungsstelle ab, diese stellt auch die benötigten Zertifikate für den nächsten Schritt bereit. Im Fall von Thawte ist das Zwischenzertifikat in der Thawte Certificate Support INFO2172 zu finden, das Root-Zertifikat muss separat bei den Thawte Root Certificates heruntergeladen werden.

robert@tux:~ > cat intermediate-ca.pem root-ca.pem > bundle.pem
robert@tux:~ >

Erhält man nach spätestens einigen Werktagen dann das Zertifikat von der Zertifizierungsstelle (nachdem man natürlich zuvor den CSR eingereicht hat), so legt man dieses in der Datei cert.pem ab. Mit dem nachfolgenden Befehl bündelt man mit openssl das „Code Signing“-Zertifikat, den privaten Schlüssel und die Zertifikatskette in einer PKCS#12-Datei:

robert@tux:~ > openssl pkcs12 -export -in cert.pem -inkey privkey.pem -out keystore.p12 -name tux -passout pass:Geheim123 -chain -CAfile bundle.pem
robert@tux:~ >

Schlägt dieser Schritt fehl, so handelt es sich meist um die Fehlermeldung „Error unable to get issuer certificate getting chain.“ – eine unvollständige bzw. fehlerhafte Zertifikatskette. Meist fehlt dann ein Zwischenzertifikat, die Reihenfolge stimmt nicht, es wurde ein falsches Zertifikat verwendet oder manchmal wurde nur das Root-Zertifikat vergessen. Mit dem Aufruf openssl x509 -noout -text -in *.pem kann der Inhalt der PEM-Dateien angesehen und überprüft werden. Im nächsten Schritt wird die PKCS#12-Datei mit keytool in den Java KeyStore importiert; dabei ist die Verwendung von Passwörtern (wie auch schon bei openssl) zwingend erforderlich:

robert@tux:~ > keytool -importkeystore \
> -deststorepass Geheim123 -destkeypass Geheim123 -destkeystore keystore.jks -deststoretype JKS \
> -srcstorepass Geheim123 -srckeypass Geheim123 -srckeystore keystore.p12 -srcstoretype pkcs12 \
> -alias tux
robert@tux:~ >

Mit der erzeugten JKS-Datei können nun beliebige JAR-Archive signiert werden. Die Angabe eines Aliases im keytool-Aufruf ist ebenfalls immer erforderlich, da generell mehrere Zertifikate in einem Java KeyStore abgelegt werden können. Warum allerdings bei nur einem Zertifikat keine automatische Alias-Vergabe erfolgt, erschließt sich mir trotzdem nicht.

Das Signieren eines Java-Applets erfordert, dass es sich dafür bereits in einem JAR-Archiv befindet, mit was jar erledigt werden kann. Zu beachten ist, dass seit Oracle Java 7 Update 25 (1.7.0_25-b17) in der Datei manifest die Attribute permissions und codebase zwingend verwendet werden müssen, will man später nicht an den standardmäßigen Sicherheitseinstellungen scheitern. Weiterführende Informationen dazu finden sich im Java Rich Internet Applications Guide.

Zusätzlich sollte beim Signieren des JAR-Archivs eine Timestamp Authority (kurz TSA) angegeben werden: Ein „Code Signing“-Zertifikat ist maximal für 2 Jahre gültig. Läuft das Zertifikat ab, erhält der Benutzer beim Start entsprechende Warnmeldungen. Um das zu vermeiden kann beim Signieren ein vertrauenswürdiger Zeitstempel bzw. Signaturzeitstempel genutzt werden – hierfür muss das Zertifikat zum Zeitpunkt der Signierung des JAR-Archivs natürlich noch gültig sein. Ist also eine Timestamp Authority angegeben, so kann ein JAR-Archiv vom Benutzer noch viele Jahre nach Ablauf des eigentlichen „Code Signing“-Zertifikats weitergenutzt werden – im Internet werden Fälle von über 9 Jahren beschrieben. Die URL der Timestamp Authority stellt üblicherweise die Zertifizierungsstelle auf der Webseite oder in den FAQ bereit; im nachfolgenden Beispiel handelt es sich um die Timestamp Authority (mit SHA-256) von Symantec, dem Mutterunternehmen von Thawte.

Beim eigentlichen Signieren mit dem jarsigner müssen sowohl die JKS-Datei, das zugehörige Kennwort sowie der KeyStore-Alias als auch das JAR-Archiv angegeben werden. Die Timestamp Authority ist zwar optional (führt allerdings bei Nichtverwendung zu einer Warnmeldung), sollte jedoch aufgrund der Vorteile unbedingt verwendet werden:

robert@tux:~ > jarsigner -keystore keystore.jks -storepass Geheim123 -keypass Geheim123 -tsa http://sha256timestamp.ws.symantec.com/sha256/timestamp fisch.jar tux
jar signed.
robert@tux:~ >

Wurde das Java-Applet erfolgreich signiert, so kann man diese Signatur ebenfalls mit dem jarsigner überprüfen. Dabei wird auch die Zertifikatskette ausgegeben; die nachfolgenden Ausgaben zu den Thawte-Zertifikaten sind übrigens echt, gemäß Thawte korrekt und können somit für Vergleiche verwendet werden.

robert@tux:~ > jarsigner -verify -verbose -certs fisch.jar

s 602 Tue Jan 19 23:56:06 CET 2016 META-INF/MANIFEST.MF

[entry was signed on 19.01.16 23:56]
X.509, CN=Tux OHG, O=Tux OHG, L=Snow Hill Island, ST=Antarktika, C=AQ
[certificate is valid from 18.01.16 01:00 to 19.03.18 00:59]
X.509, CN=thawte SHA256 Code Signing CA, O="thawte, Inc.", C=US
[certificate is valid from 10.12.13 01:00 to 10.12.23 00:59]
X.509, CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US
[certificate is valid from 17.11.06 01:00 to 17.07.36 01:59]

592 Tue Jan 19 23:56:08 CET 2016 META-INF/FISCH.SF
8154 Tue Jan 19 23:56:08 CET 2016 META-INF/FISCH.RSA
0 Tue Jan 19 23:56:00 CET 2016 META-INF/
sm 7956 Tue Jan 19 23:56:00 CET 2016 j2dir/fisch.class

[entry was signed on 19.01.16 23:56]
X.509, CN=Tux OHG, O=Tux OHG, L=Snow Hill Island, ST=Antarktika, C=AQ
[certificate is valid from 18.01.16 01:00 to 19.03.18 00:59]
X.509, CN=thawte SHA256 Code Signing CA, O="thawte, Inc.", C=US
[certificate is valid from 10.12.13 01:00 to 10.12.23 00:59]
X.509, CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US
[certificate is valid from 17.11.06 01:00 to 17.07.36 01:59]


s = signature was verified
m = entry is listed in manifest
k = at least one certificate was found in keystore
i = at least one certificate was found in identity scope

jar verified.

robert@tux:~ >

Abschließend sollte das Java-Applet nun mit verschiedenen Java-Versionen auf verschiedenen Plattformen und Betriebssystemen ausführlich getestet werden bevor die JAR-Datei dann z.B. in eine Webseite oder einen Java Web Start eingebunden und den Benutzern regulär zur Verfügung gestellt wird.