Title: Using client certificate authentication
[TOC]
## Introduction
One-way SSL authentication is used to let a client verify the identity of the server it is
communicating with. The server itself does not verify the identity of the client. In
two-way SSL authentication, a client first verifies the identity of the server after which
the server identifies the client. This way, the identity of both the client and server can
be established allowing a trust relation to be created.
This article describes how to configure the ACE server and the management agent(s) in such
way that they use two-way SSL authentication. The remainder of this article assumes the
reader has basic knowledge of the principles behind ACE, and has basic knowledge about
creating and using certificates.
## Outline
As described in detail in the [authentication design
documentation](/docs/design/authentication-design.html), there are multiple communication
paths that can (and need) to be secured. For two-way SSL authentication, several scenarios
can be identified:
1. only the communication between management agent and ACE server is secured by means of
two-way SSL. This implies that there is only a trust relation between management agent
and ACE server, but the other clients that make use of the ACE server have no trust
relation (i.e., they still communicate by means of one-way SSL or might not even use
SSL at all);
2. all the communication paths for the ACE server are secured by means of two-way SSL.
This means that not only a trust relation exists between management agent and ACE
server, but also between, for example, the web-UI and the ACE server or the REST-API
and the ACE server[^1].
In conclusion, we need to configure the trust relation between management agent and the
ACE server, and, optionally, the trust relation between ACE server and other components.
## Configuring two-way SSL authentication
For two-way SSL authentication, you need two (or more) certificates. These can be issued
either by an official external certificate authority (CA), or by means of a self-signed
CA[^2].
The details on how to create a self-signed CA and certificates is well documented on many
places on the Internet, and therefore goes beyond this article. Let's assume we've got the
following:
* a self-signed CA whose certificate is added to a Java keystore file, called
truststore. This file will be used as *truststore* for both the management
agent and the ACE server[^3];
* a certificate (signed by our self-signed CA) for the management agent, available in a
Java keystore file, called keystore-ma;
* a certificate (signed by our self-signed CA) for the ACE server, available in a Java
keystore file, called keystore-server.
For the management agent, we need to add some system properties in order to let Java find
and use the correct truststore and keystore (see also the [JSSE Reference Guide for JDK
6](http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#Customization))[^4]:
:::sh
[localhost:/]$ java \
-Dagent.identification.agentid=MyTarget \
-Dagent.discovery.serverurls=https://10.0.1.16:8443 \
-Dagent.connection.authtype=CLIENTCERT \
-Dagent.connection.sslProtocol=TLS \
-Dagent.connection.keyfile=/path/to/keystore \
-Dagent.connection.keypass=secret \
-Dagent.connection.trustfile=/path/to/truststore \
-Dagent.connection.trustpass=secret \
-jar target.jar
*Note to double check the paths to both files, as there will not be printed any error in
case one of them points to an incorrect file!*
For the ACE server, the configuration is provided by means of a property-file called
platform.properties. Similar to the management agent, we should add some
additional properties to it:
:::properties
-Dorg.osgi.service.http.port.secure=8443
-Dorg.apache.felix.https.enable=true
-Dorg.apache.felix.https.truststore=/path/to/truststore
-Dorg.apache.felix.https.truststore.password=secret
-Dorg.apache.felix.https.keystore=/path/to/keystore-server
-Dorg.apache.felix.https.keystore.password=secret
-Dorg.apache.felix.https.clientcertificate=needs
This will not only ensure that the Jetty container inside ACE will obtain the correct
keystore and truststore and start a listener on port 8443, but also mandates that
all clients **must** provide a certificate upon connecting (as denoted by the last
property). Without this, clients that do not offer a certificate will simply be accepted
as well, hence resulting in only one-way SSL authentication.
In order to secure all internal communication paths as well, we need to specify some
additional properties in platform.properties:
:::properties
-Djavax.net.ssl.keyStore=/path/to/keystore-server
-Djavax.net.ssl.keyStorePassword=secret
-Djavax.net.ssl.trustStore=/path/to/truststore
-Djavax.net.ssl.trustStorePassword=secret
This will ensure that all created HTTPS connections will use the mentioned keystore and
truststore. Note that in order to let **all** communication to use HTTPS, you need to
modify the configuration files of ACE (as found in the conf directory) to mention
this, for example, the org.apache.ace.webui.vaadin.cfg file would look like:
:::properties
# The endpoint of the Vaadin UI
org.apache.ace.server.servlet.endpoint = /ace
# Vaadin UI settings
ui.authentication.enabled = true
#ui.authentication.user.name = d
#ui.authentication.user.password = f
# ACE MS settings
ace.host = https://10.0.1.16:8443/
# OBR settings
obr.url = https://10.0.1.16:8443/obr/
Alternatively, one could also provide a keystore with a *different* certificate for
securing the internal communication as well. The only thing needed is to change the
javax.net.ssl.keyStore property to let it point to another keystore file.
### Using multiple different keystores
So far, we only used the "standard" Java functionality to secure the communication paths
with two-way SSL authentication. While this works for most use cases, one can think of
more sophisticated scenario's in which multiple trust relations between different hosts
have to be created. For example, when the OBR of ACE runs on a different host, secured
with its own certificate. In order to support this use case, we can leverage the
authentication framework of ACE by providing it configurations for all URLs that need
their own keystore and/or truststore. In our OBR example, we could supply the following
configuration to the ConnectionFactory:
:::properties
authentication.baseURL = https://10.0.1.17:8443/obr/
authentication.type = client_cert
# optional: use a specific keystore for this URL
authentication.keystore.file = /path/to/obr-keystore
authentication.keystore.storepass = secret
# optional: use a specific truststore for this URL
authentication.truststore.file = /path/to/obr-truststore
authentication.truststore.storepass = secret
Different configurations can be supplied for different URLs, allowing many different trust
relations to be established.
Be sure that in order to let ACE correctly map certificates to users, you need to install
the ClientCertAuthenticationProcessor as additional authentication processor!
## Troubleshooting
If after configuring the authentication of ACE things no longer work, it can be hard to
find the exact cause of this. In this section, some pointers are given to help you to find
the probably cause of the problem.
How should I name the certificates?
: One should use the hostname of the calling side as common name (CN) of the certificate's
distinguished name (DN), for example, CN=localhost or CN=10.0.1.16;
How should I name the users that are authenticated through certificates?
: The user should have the same name as the common name of the certificate, for example,
localhost or 10.0.1.16;
I've enabled two-way SSL authentication, but it doesn't work!
: There can be many reasons for this, like, can the truststore and keystore files be
loaded (*no warnings or errors will be printed for this!*), or, is the name of the
certificate matching the name of the host, or …? In general, if it doesn't work, one
should enable SSL-debugging in Java by adding -Djavax.net.debug=ssl as system
property. This will print *lots* of information about the keystore and truststore, the
communication itself as well as detailed error messages. Also, the authentication parts in
ACE provide lots of debugging information, logged at DEBUG level.
What if my target runs on a machine with a dynamic IP address? Can I still use client certificates for authentication?
: Not directly. Java uses the common name of the certificate and *assumes* this to be a
valid, resolvable, hostname. If not, it will fail to accept the certificate as being
valid. In the near future, ACE should [support this
functionality](https://issues.apache.org/jira/browse/ACE-271).
[^1]: One can argue whether this is strictly necessary for **all** internal communication
paths, as we will see later on, one can configure which paths use two-way SSL
authentication and which paths do not.
[^2]: Using a self-signed CA for two-way SSL authentication is not that much of a problem
as one needs to make the certificate of the client available to the server, and the other
way around. When both certificates are signed by the same CA, and both sides also trust
this self-signed CA, the trust relation between client and server can be established as
well.
[^3]: Based on the certificate in the truststore, each side will be able to validate the
certificate of the other side.
[^4]: You probably do not want to specify the credentials using the commandline, see also
[ACE-496](https://issues.apache.org/jira/browse/ACE-496).