# SSL-Verbindung mit Client-Zertifikat (Private Key) scheitert



## musiKk (10. Jun 2009)

Guten Tag,

ich habe hier einen Server, der nur sichere Verbindungen mit Client-Zertifikat zulässt. Dieses ist zudem selbst signiert. Mein Vorgehen bislang ist wie folgt (Java 6):

Ich habe ein PEM-File mit dem Private Key. Daraus habe ich einen Java-Keystore im JKS-Format erstellt: 
	
	
	
	





```
keytool -import -keystore keystore.jks -file mein_keyfile.pem
```
Mit -list kann ich mir den Eintrag auch toll anzeigen lassen, also scheint zumindest die Erstellung schonmal zu funktionieren.

Dann bin ich auf diese Seite, habe das dort vorhandene Programm InstallCert angepasst und einen Truststore erzeugt (den habe ich, wie das wohl meist geschieht, cacerts genannt).

So. Nun also das ganze mal ausprobieren. Erstmal schaue ich mit einem SSLSocket, ob der Truststore funktioniert:

```
System.setProperty("javax.net.ssl.trustStore", "pfad/zum/cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "mein_password");

SSLContext ctxt = SSLContext.getDefault();

SSLSocketFactory ssf = ctxt.getSocketFactory();
SSLSocket socket = (SSLSocket) ssf.createSocket("rechnername", 443);
socket.startHandshake();
socket.close();
```
Das funktioniert (mit einem HandshakeCompletedListener sieht man das auch, wollte den Code nur kurz halten). Dass mein Truststore funktioniert sehe ich auch daran, dass wenn die Properties nicht gesetzt sind, entsprechende Exceptions fliegen (wie in oben verlinktem Blogeintrag).

Jetzt versuche ich aber z. B. auf den Webserver zuzugreifen, zu welchem ich ja nur per Client-Zertifikat komme. Das klappt nicht:

```
System.setProperty("javax.net.ssl.keyStore", "pfad/zum/keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "mein_password");

System.setProperty("javax.net.ssl.trustStore", "pfad/zum/cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "mein_password");

url = new URL("https://hostname/pfad/zu/einer/existierenden/datei");
URLConnection urlc = url.openConnection();

InputStream is = urlc.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));

String line = null;
while ((line = br.readLine()) != null) {
	System.out.println(line);
}
```
Dabei erhalte ich diese schöne Exception (erscheint in Zeile 10 in obigem Listing):

```
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1657)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:932)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:746)
	at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:75)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:687)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:632)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:652)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1000)
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
	at client.Cert.main(Cert.java:47)
```
Dabei ist es irrelevant, ob ich das Client-Zertifikat einfüge, oder nicht. Die Passwörter der Keystores stimmen; jedenfalls funktionieren sie bei einem "keytool -list". Der Pfad zu der Datei stimmt auch, weil per Firefox und importiertem Zertifikat (dort hat es natürlich nicht das proprietäre JKS-Format, sondern PKCS#11) klappt es.

Irgendetwas habe ich wohl noch falsch. Meine Frage natürlich nun: Was könnte das sein? Ich habe irgendwie das Gefühl, dass der private Schlüssel aus dem .pem-File nicht in den Keystore übertragen wird, aber ich kann mir nach viel Googelei nicht so recht erklären, wie es anders gemacht werden könnte.


----------



## musiKk (11. Jun 2009)

Naja, was auch immer; ich habs jetzt. Es lag wirklich gleich am Anfang, das keytool funktioniert doch etwas anders, als ich dachte (um nicht zu sagen unintuitiv).


----------



## scaary (12. Jun 2009)

Hi.
Wie hasten das geloest?
Ich hab nen aehnliches Probelm mit nem Zugriff auf ne Seite, hab das ganze auch mit Keytool eingebunden laut folgender Anleitung:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Aber trotzdem krieg ich noch ne fehlermeldung.

Als testseite (um zu gucken, damit ich das hinbekomme), hab ich einfach mal die Seite des Arbeitsamts genommen:


```
import java.io.*;
import java.net.*;

public class Loader{

    static URL kJobs;

    static InputStream is = null; 
    static DataInputStream dis;
    static String s;

    public static void main(String[] args) {
	try {
	    kJobs = new URL("https://jobboerse2.arbeitsagentur.de/vam/vamController/SchnellsucheAG/anzeigeSchnellsuche?rqc=11950951693&rqv=-474859825961505844");
	    is = kJobs.openStream();
	    dis = new DataInputStream(new BufferedInputStream(is));

	    while ((s = dis.readLine()) != null) {
		System.out.println(s);
	    }
	    
	} catch (MalformedURLException mue) {

	    System.out.println("Ouch - a MalformedURLException happened.");
	    mue.printStackTrace();
	    System.exit(1);

	} catch (IOException ioe) {

	    System.out.println("Oops- an IOException happened.");
	    ioe.printStackTrace();
	    System.exit(1);
	} finally {
	    try {
		is.close();
	    } catch (IOException ioe) {
		// just going to ignore this one
	    }
	}
    }
}
```

Dabei liest er die Seite erstmal einfach nur ein und gibt sie wieder aus ^^

Kannst du mir sagen, woran das liegt?
Danke!


Edit:

Ahja, fehlermeldung schaut wie folgt:


```
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Oops- an IOException happened.
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
	at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
	at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
	at sun.net.[url]www.protocol.https.HttpsClient.afterConnect(Unknown[/url] Source)
	at sun.net.[url]www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown[/url] Source)
	at sun.net.[url]www.protocol.http.HttpURLConnection.getInputStream(Unknown[/url] Source)
	at sun.net.[url]www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown[/url] Source)
	at java.net.URL.openStream(Unknown Source)
	at Loader.main(Loader.java:16)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
	at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
	at sun.security.validator.Validator.validate(Unknown Source)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
	... 14 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
	at java.security.cert.CertPathBuilder.build(Unknown Source)
	... 20 more
```


----------



## musiKk (12. Jun 2009)

Das kannst Du mit dem oben verlinkten Programm InstallCert lösen.


----------



## scaary (13. Jun 2009)

HI.Ah, danke.

Aber irgendwie peil ich nicht so ganz wie ich das Programm in Eclipse aufrufen kann.

Geht das nur unter Linux?

Oder geht das nur per shell?

Danke!


----------



## musiKk (13. Jun 2009)

Das ist reines Java mit all seiner plattformunabhängigkeit, darum sollte es eigentlich gehn.

Du kannst es auch anders machen. Geh mit Deinem Browser auf die Seite und schau dort in den Sicherheitsinformationen nach der CA. Dann suchst Du in den Zertifikaten des Browsers nach dem Zertifikat der CA und exportierst die dort. Bei mir im Opera heißt die für Deine Beispielseite "TC Trust Center, Germany, Class 3 CA" (ich weiß nicht, ob der Name variabel sein kann). Dieses exportierte Zertifikat (am besten im PEM-Format) kannst du mit dem keytool einem Truststore (_cacerts_) hinzufügen (oder einen neuen erstellen):

```
keytool -importcert -file das_exportierte_zertifikat.pem -keystore cacerts -storepass password
```
Das keytool liegt in _<jdk-pfad>/bin/keytool_.


----------



## scaary (13. Jun 2009)

Deshalb hat es mich gewundert ^^

Ich mach warscheinlihch noch was falsch.

Wenn ich das Proggie aufrufe, kommt folgende Ausgabe


```
Usage: java InstallCert <host>[:port] [passphrase]
```

Das wars, Programm zu ende.


Das mit dem Keytool hab ich uebrigens schon gemacht.

Ich bin auf die Seite, hab geschaut welches SSL Protokoll verwendet wird, hab das exportiert, und danach mit dem Keytool eingebunden. es geht immernoch nicht, und ich hab einfach keinen dunst warum 

Wie gesagt, ich hab ja die Anleitung, nach der ich das gemacht hab, in meinem ersten post verlinkt, und laut ausgabe des Keytools wurde das zertifikat auch erfolgreich eingefuegtl, aber trozdem weigert sich mein Programm eine Verbindung her zu stellen... .

Muss ich host, port und passphrase erst noch initialisieren?

(wenn ja, was genau mach ich da rein? Woher soll ich denn den port wissen?).

Danke !


----------



## musiKk (13. Jun 2009)

Keine Ahnung. In dem Tutorial wird ja der globale truststore des JRE bearbeitet. Davon habe ich bisher immer abgesehen. Ich habe lieber meinen eigenen truststore erstellt und den dann im Programm wie oben eingebunden.

Usage weist darauf hin, mit welchen Parametern Du das Programm aufzurufen hast. Das ist aber keine rocket science. Du kannst ja auch den Quelltext entsprechend ändern. Host ist die Arbeitsagentur, der Port kann wegbleiben, weils der Standardport ist (443) und die passphrase ist für den truststore.


----------



## scaary (13. Jun 2009)

Mhh, ok, bei mir scheiterts grade noch ein wenig am Verstaendnis, fuerchte ich.

Also, das Programm, dass du verlinkt hast, importiert das protokoll in den globalen Truststore?

Oder was genau macht das (wenns denn mal laeuft ^^).

Ich hab anschienend auch noch nicht verstanden, wie ich das Programm aufrufe.

Ich habs derzeit einfach in Eclipse mit ner eigenen Klasse und ruf es da auf, und kriege dann die Usage fehlermeldung.

Das funktioniert aber offensichtlich nicht, da ich args keine Parameter uebergebe, und args daher 0 ist.

Wie also rufe ich das Programm mit den entsprechenden parametern auf?

(sorry dass ich dich jetzt mit Basics langweile, aber das wurde in der Uni bisher noch nicht angesprochen).

Danke!


----------



## musiKk (14. Jun 2009)

scaary hat gesagt.:


> Also, das Programm, dass du verlinkt hast, importiert das protokoll in den globalen Truststore?





			
				http://blogs.sun.com/andreas/entry/no_more_unable_to_find hat gesagt.:
			
		

> [...]and then added it to a Java KeyStore 'jssecacerts' in the current directory.





scaary hat gesagt.:


> Das funktioniert aber offensichtlich nicht, da ich args keine Parameter uebergebe, und args daher 0 ist.
> 
> Wie also rufe ich das Programm mit den entsprechenden parametern auf?


Entweder in der Run Configuration bei Program Arguments ${string_prompt} angeben oder die Variablen im Quelltext einfach selbst belegen (was auch nicht schwer gewesen wäre, die nötigen Variablen haben sinnvolle Bezeichnungen).


----------



## scaary (14. Jun 2009)

OKidoki, es hat jetzt funktioniert ,vielen Dank fuer deine hilfe ^^


----------



## malzbier (22. Apr 2010)

Hi Musikk,

ich habe das gleiche Problem wie du. Wenn ich client.pem mit keytool in keystore importieren, geht das privat key irgendwie verloren.
Kannste mir sagen, wie du das löst?Danke!


----------



## musiKk (22. Apr 2010)

Auch wenn der Thread schon zehn Monate alt ist... ich habe dazu mal was geschrieben.


----------



## malzbier (27. Apr 2010)

Es funktioniert! Vielen Dank.


----------

