Here is a case study showing how I implemented XMPP Client Certificate Authentication with Openfire.
Scenario:
User âAndrewâ installs a Windows XMPP Client along with a certificate signed by the local Certification Authority.
The certificate has a private key and owner designated as âCN=andrew@mychat.mydomain.localâ.
The first time Andrew runs the XMPP Client, he gets presented with a dialog to enter JID.
He enters âandrew@mychat.mydomain.localâ, hits âLoginâ button and after a short delay, is
presented with a roster containing mydomain.local contacts.
Technical Specs:
The XMPP Client uses Jivesoftware Smack library v4.1.4.
The Openfire XMPP Server v3.10.0 is hosted on Linux box âmychatâ.
The Server listens for XMPP clients on port 5222 and requires TLS security for all connections.
The Openfire sole SASL Mechanism to be employed is âEXTERNALâ.
Openfire Configuration:
Set up keystore and truststore following instructions http://www.javacodegeeks.com/2015/02/secure-openfire-xmpp-server.html.
This describes how to install Certificate trust chain in both the the keystore and truststore.
The client.truststore file co-located with keystore and truststore must also contain the Certificate trust chain.
BEWARE: Use the default âchangeitâ keystore password initially to avoid issues with logging in to the admin console after making these changes.
Also, if on Linux, ensure the client.truststore, keystore and truststore files are owned by the Openfire user.
These are what I believe are the significant property settings for Client Certificate Authentication in this scenario:
xmpp.client.cert.policy = needed
xmpp.client.certificate.accept-selfsigned = false
xmpp.client.certificate.verify = true
xmpp.client.certificate.verify.chain = true
xmpp.client.certificate.verify.root = true
xmpp.client.certificate.verify.validity = true
xmpp.client.tls.policy = true
xmpp.domain = mychat.mydomain.local
xmpp.socket.ssl.active = true
sasl.mechs = EXTERNAL
In Security Settings - Client Connection Security: Select âRequiredâ.
Note I also had an âxmpp.client.certificate.crlâ property set but this appears to be optional judging from the code.
Certificate Revocation Lists are important for production systems.
Openfire Troubleshooting:
Turn on SSL debugging on the client using Java VM argument â-Djavax.net.debug=sslâ. If the Openfire configuration is correct,
you will see the âmydomainâ CA Certificate listed during SSL Handshaking under the CertificateRequest Cert Authorities:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Cert Authorities:
<CN=mydomain-ROOT-CA>
*** ServerHelloDone
matching alias: andrew@mychat.mydomain.local
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=andrew@mychat.mydomain.local
âŚ
If the CA Certificate is missing, then a âbad_certificateâ error is reported.
The same error is reported if the client certificate is missing from the client application keystore or
the keystore is omitted when setting up the SSL Context. This problem can be determined from the debug log
by whether there is a line at the start of the log containing something like âfound key for : andrew@mychat.mydomain.localâ.
If the certificate chain is missing from client.truststore, then there is NullPointerException generated from within the SSL implementation code.
If the certificate chain is missing from trustore, then this error occurs after successful SSL Handshake completion:
org.jivesoftware.util.CertificateManager - Path builder: unable to find valid certification path to requested target
Smack Configuration:
Here is a routine to create an XMPPTCPConnectionConfiguration object which can be used as a guide:
private XMPPTCPConnectionConfiguration getConfiguration(
String host /* eg. âmychat.mydomain.localâ */,
String keypass /* Keystore password */ )
throws NoSuchAlgorithmException,
KeyStoreException,
CertificateException,
FileNotFoundException,
IOException,
UnrecoverableKeyException,
KeyManagementException
{
XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder();
// Allow empty password
configBuilder.allowEmptyOrNullUsernames();
// Host and service name are the same. Port is 5222 by default.
configBuilder.setServiceName(host);
configBuilder.setHost(host);
// JKS with private key placed in home directory
File keystorePath = new File(System.getProperty(âuser.homeâ), âandrew.mychat.jksâ);
// Create SSLContext same as Openfire ie. the âTLSv1â
SSLContext sslContext = SSLContext.getInstance( âTLSv1â );
KeyStore ks = KeyStore.getInstance(âJKSâ);
ks.load(new FileInputStream(keystorePath), keypass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(âSunX509â);
kmf.init(ks, keypass);
KeyManager[] kms = kmf.getKeyManagers();
sslContext.init(kms, null, new java.security.SecureRandom());
configBuilder.setCustomSSLContext(sslContext);
XMPPTCPConnectionConfiguration config = configBuilder.build();
return config;
}
Note that the client keystore contains the certificate chain and this is particularly important if there are intermediate CA certificates in the chain.
Final Comments:
The most important thing I want to say is that Client Certificate Authentication does work on Openfire V3.10 for TLS on port 5222.
I failed to get it working until I followed the keystore and truststore setup procedure on the javacodegeeks site.
Note that âxmpp.client.certificate.accept-selfsigned = falseâ means self-signed client certificates are not supported.
That is not to there may be a work-around, but the designers of SSL were keen to prevent âman-in-middleâ attacks and
self-signed certificates break the spirit of their intentions.