Client Certificate Authentication
Bei der "Client Certificate Authentication" machen Client und Server einen Handshake, bei dem sich beide Parteien kennen müssen.
Dazu muss neben den normalen Keystores auch jeweils ein Truststore mit den Zertifikaten des jeweils anderen zur Verfügung stehen.
In Kürze: Der Client kennt den Server und der Server kennt den Client.
|
Client Certificate Authentication kommt aktuell nur bei den Log Service-Endpunkten zum Einsatz. |
Zertifikate, Keystores und Truststores
Hier werden exemplarisch die Schritte zur Erstellung der Zertifikate, Keystores und Truststores sowie die Konfiguration im Karaf gezeigt.
Die Schritte werden in folgendem Verzeichnis ausgeführt: <KARAF-INSTALL-DIR>/etc/virtimo/ssl
1. Virtimo Keystore für den Karaf
Generierung eines Paars selbst-signierter Public und Private Keys für den Karaf.
Dieser Schritt kann übersprungen werden, da der Keystore bereits existiert.
Er wird in der <KARAF-INSTALL-DIR>/etc/org.ops4j.pax.web.cfg referenziert.
keytool -genkey \
-alias virtimo \
-keystore virtimo_keystore.jks \
-dname "EMAILADDRESS=support@virtimo.de, CN=Virtimo AG, OU=Lab, O=Virtimo AG, L=Berlin, ST=Germany, C=DE" \
-storepass virtimo \
-keypass virtimo \
-storetype jks \
-validity 1000 \
-keyalg RSA
Zum Verständnis den Inhalt unseres Keystores anzeigen lassen:
keytool -list -keystore virtimo_keystore.jks
2. Export des Virtimo-Zertifikats
Dieses wird aus dem Virtimo Keystore exportiert.
keytool -export \
-alias virtimo \
-file virtimo.cer \
-keystore virtimo_keystore.jks \
-storepass virtimo \
-storetype jks
Zum Verständnis den Inhalt des Virtimo Zertifikats anzeigen lassen:
keytool -printcert -file virtimo.cer
...
> EMAILADDRESS=support@virtimo.de, CN=Virtimo AG, OU=Lab, O=Virtimo AG, L=Berlin, ST=Germany, C=DE
3. Import des Virtimo-Zertifikats in den Client Truststore
Dieser wird angelegt, wenn noch nicht vorhanden.
keytool -import \
-file virtimo.cer \
-alias default \
-keystore clienttrust.jks \
-storepass passw0rd \
-keypass passw0rd \
-storetype jks
4. Erstellung des Client Keystores
Generierung eines Paars selbst-signierter Public und Private Keys für den Client, welcher der Aufrufer unserer Endpunkte ist. Hier exemplarisch am Beispiel mit zwei Aliasen.
keytool -genkey \
-alias user0 \
-keystore clientkey.jks \
-dname "CN=employee0, OU=Lab, O=Virtimo AG, L=Berlin, ST=Germany, C=DE" \
-storepass passw0rd \
-keypass passw0rd \
-storetype jks \
-validity 1000 \
-keyalg RSA
keytool -genkey \
-alias admin0 \
-keystore clientkey.jks \
-dname "CN=manager0, OU=Lab, O=Virtimo AG, L=Berlin, ST=Germany, C=DE" \
-storepass passw0rd \
-keypass passw0rd \
-storetype jks \
-validity 1000 \
-keyalg RSA
Zum Verständnis den Inhalt des Keystores anzeigen lassen:
keytool -list -keystore clientkey.jks
5. Client Zertifikat Export und Virtimo Truststore Import
Export der Client Zertifikate per oben gesetzten Aliases und Import dieser in den Virtimo Truststore.
keytool -export \
-alias user0 \
-file user0.cer \
-keystore clientkey.jks \
-storepass passw0rd \
-storetype jks
keytool -export \
-alias admin0 \
-file admin0.cer \
-keystore clientkey.jks \
-storepass passw0rd \
-storetype jks
keytool -import \
-file user0.cer \
-alias user0 \
-keystore virtimo_truststore.jks \
-storepass virtimo \
-keypass virtimo \
-storetype jks
keytool -import \
-file admin0.cer \
-alias admin0 \
-keystore virtimo_truststore.jks \
-storepass virtimo \
-keypass virtimo \
-storetype jks
Anpassung Karaf
Damit der Karaf den zuvor angelegten Truststore verwendet, muss die Konfigurationsdatei
<KARAF-INSTALL-DIR>/etc/org.ops4j.pax.web.cfg um folgende Eigenschaften erweitert werden:
org.ops4j.pax.web.ssl.truststore = ${karaf.etc}/virtimo/ssl/virtimo_truststore.jks
org.ops4j.pax.web.ssl.truststore.password = virtimo
org.ops4j.pax.web.ssl.clientauthwanted = true
org.ops4j.pax.web.ssl.clientauthneeded = false
Mindestens einer der Parameter org.ops4j.pax.web.ssl.clientauthwanted oder org.ops4j.pax.web.ssl.clientauthneeded muss auf true stehen.
Sind beide auf false gesetzt (Default), wird kein Client-Zertifikat an den Endpunkt übermittelt.
Falls das Zertifikat nicht am Endpunkt ankommt: Im aktuellen (26.11.2021) PAX Web Source Code stehen andere property-Namen und die funktionieren nicht mit dem Karaf 4.3.3.
org.ops4j.pax.web.ssl.clientauth.wanted = true
org.ops4j.pax.web.ssl.clientauth.needed = false
|
Nach der BPC-Installation steht standardmäßig ein vordefinierter Truststore zur Verfügung.
Dieser befindet sich im Verzeichnis |
Java Client Code
Die folgenden Beispiele zeigen, wie ein Java-Client BPC-Endpunkte unter Verwendung der erstellten Client-Key- und Truststores aufruft.
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.methods.GetMethod;
public class SSLMutualAuthTestSimple {
private final static String KEYSTORE_FILE_PATH = "/home/<user>/<BPC-INSTALL-DIR>/karaf/etc/virtimo/ssl/clientkey.jks";
private final static String KEYSTORE_PASSWORD = "passw0rd";
private final static String TRUSTSTORE_FILE_PATH = "/home/<user>/<BPC-INSTALL-DIR>/karaf/etc/virtimo/ssl/clienttrust.jks";
private final static String TRUSTSTORE_PASSWORD = "passw0rd";
public static void main(String[] args) {
try {
// System.setProperty("javax.net.debug", "ssl,handshake"); // for debugging ssl and handshake stuff
System.setProperty("javax.net.ssl.keyStore", KEYSTORE_FILE_PATH);
System.setProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD);
System.setProperty("javax.net.ssl.trustStore", TRUSTSTORE_FILE_PATH);
System.setProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD);
GetMethod method = new GetMethod();
method.setURI(new URI("https://localhost:8282/cxf/bpc-logservice/log/1234567890?config=true", false));
HttpClient client = new HttpClient();
client.executeMethod(method);
System.out.println(method.getResponseBodyAsString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.PrivateKeyDetails;
import org.apache.http.ssl.PrivateKeyStrategy;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.security.KeyStore;
import java.util.Map;
public class SSLMutualAuthTestComplex {
private final static String KEYSTORE_FILE_PATH = "/home/<user>/<BPC-INSTALL-DIR>/karaf/etc/virtimo/ssl/clientkey.jks";
private final static String KEYSTORE_PASSWORD = "passw0rd";
private final static String TRUSTSTORE_FILE_PATH = "/home/<user>/<BPC-INSTALL-DIR>/karaf/etc/virtimo/ssl/clienttrust.jks";
private final static String TRUSTSTORE_PASSWORD = "passw0rd";
public static void main(String[] args) {
try {
// System.setProperty("javax.net.debug", "all"); // to debug all "net" related stuff
String CERT_ALIAS = "admin0";
KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream keyStoreFile = new FileInputStream(new File(KEYSTORE_FILE_PATH));
keyStore.load(keyStoreFile, KEYSTORE_PASSWORD.toCharArray());
KeyStore trustStore = KeyStore.getInstance("JKS");
FileInputStream trustStoreFile = new FileInputStream(new File(TRUSTSTORE_FILE_PATH));
trustStore.load(trustStoreFile, TRUSTSTORE_PASSWORD.toCharArray());
// Create all-trusting host name verifier
// This should not be used in a production environment!!
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, KEYSTORE_PASSWORD.toCharArray(), new PrivateKeyStrategy() {
@Override
public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
return CERT_ALIAS;
}
})
.loadTrustMaterial(trustStore, null)
.build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"},
null,
allHostsValid /* SSLConnectionSocketFactory.getDefaultHostnameVerifier() */
);
try (CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build()) {
HttpGet request = new HttpGet("https://localhost:8282/cxf/bpc-logservice/log/1234567890?config=true");
request.setHeader("Accept", "application/json");
HttpResponse response = client.execute(request);
System.out.println("Response code: " + response.getStatusLine().getStatusCode());
System.out.println("Response content:");
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line;
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}