Die BPC Version 4.1 wird nicht mehr gewartet.

Sollten Sie diese BPC Version nutzen, empfehlen wir Ihnen eine Migration auf eine aktuelle Version. Die Dokumentation zur neusten BPC Version finden Sie hier. Sollten Sie Fragen haben, wenden Sie sich bitte an unseren Support.

Client Certificate Authentication

In "Client Certificate Authentication," the client and server perform a handshake in which both parties must recognize each other. To do this, in addition to the normal keystores, each must also have a truststore containing the other party’s certificates.
In short: The client knows the server, and the server knows the client.

Client Certificate Authentication is currently used only for Log Service endpoints.

Certificates, Keystores, and Trust Stores

This section provides an example of the steps for creating certificates, keystores, and trust stores, as well as configuring them in Karaf. The steps are performed in the following directory: [karaf]/etc/virtimo/ssl

1. Virtimo Keystore for Karaf

Generate a pair of self-signed public and private keys for Karaf. This step can be skipped because the keystore already exists. It is referenced in [karaf]/etc/org.ops4j.pax.web.cfg.

virtimo_keystore.jks
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

To better understand, view the contents of our keystore:

keytool -list -keystore virtimo_keystore.jks

2. Export the Virtimo certificate

This is exported from the Virtimo keystore.

virtimo_keystore.jks → Virtimo.cer
keytool -export \
  -alias virtimo \
  -file virtimo.cer \
  -keystore virtimo_keystore.jks \
  -storepass virtimo \
  -storetype jks

To better understand the process, view the contents of the Virtimo certificate:

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 the Virtimo certificate into the client truststore

This will be created if it does not already exist.

Virtimo.cer → clienttrust.jks
keytool -import \
  -file virtimo.cer \
  -alias default \
  -keystore clienttrust.jks \
  -storepass passw0rd \
  -keypass passw0rd \
  -storetype jks

4. Create the client keystore

Generate a pair of self-signed public and private keys for the client, which is the caller of our endpoints. Here is an example using two aliases.

clientkey.jks
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

To better understand this, view the contents of the keystore:

keytool -list -keystore clientkey.jks

5. Exporting Client Certificates and Importing Them into the Virtimo Truststore

Export the client certificates using the aliases set above and import them into the Virtimo Truststore.

Export Client Certificates
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
Virtimo Truststore Import
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

Karaf Configuration

Karaf must be aware of the truststore created above and know to use it. To do this, extend the [karaf]/etc/org.ops4j.pax.web.cfg.

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

If org.ops4j.pax.web.ssl.clientauthwanted and org.ops4j.pax.web.ssl.clientauthneeded are set to false (default), then no client certificate will arrive at the endpoint for verification!

If problems arise in the future (certificate does not arrive at the endpoint): In the current (November 26, 2021) PAX Web source code, different property names are used, and these do not work with Karaf 4.3.3.

org.ops4j.pax.web.ssl.clientauth.wanted = true
org.ops4j.pax.web.ssl.clientauth.needed = false

By default, a trust store is always present after installing BPC. This trust store is located at [karaf]/etc/virtimo/ssl/virtimo_truststore.jks.
The default password is virtimo.
These entries are set by default in the Karaf configuration. The administrator can import additional certificates into this store. However, you can also use your own store by specifying the path to the trust store and the password.

Java Client Code

Here are two examples (simple and complex) of how a Java client (caller of a BPC endpoint) can then access the prepared BPC endpoints using the client keystore and client truststore created above.

SSLMutualAuthTestSimple.java
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/karaf/etc/virtimo/ssl/clientkey.jks";
    private final static String KEYSTORE_PASSWORD = "passw0rd";
    private final static String TRUSTSTORE_FILE_PATH = "/home/<user>/bpc/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();
        }
    }
}
SSLMutualAuthTestComplex.java
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/karaf/etc/virtimo/ssl/clientkey.jks";
    private final static String KEYSTORE_PASSWORD = "passw0rd";
    private final static String TRUSTSTORE_FILE_PATH = "/home/<user>/bpc/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
            // Das sollte produktiv nicht verwendet werden!!!
            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();
        }
    }
}

Keywords: