Usually I would use a JKS truststore for Tomcat to add trusted certificates (CAs). However this was not possible for this project, because all certificates will be managed inside an LDAP accessible via a XKMS service. Searching for a solution to extend Tomcat to support XKMS based certificate validation I came across the JSSE Standard.
Reading throw the documentation was not so straightforward and clear. But searching through the internet finally helped me to achieve my goal. In this blog post, I'll show you what I had to do, to enabled XKMS based SSL certificate validation in Tomcat.
To manage your SSL truststore settings you can use standard System or Tomcat properties:System Properties | Tomcat Properties | Purpose |
---|---|---|
javax.net.ssl.trustStore | truststoreFile | System location for JKS truststore file |
javax.net.ssl.trustStorePassword | truststorePass | Password for JKS truststore |
javax.net.ssl.trustStoreProvider | truststoreProvider | Provider for a truststoreFactory implementation |
javax.net.ssl.trustStoreType | truststoreType | Type of your truststore. Default is "JKS" |
n/a | trustManagerClassName | Custom trust manager class to use to validate client certificates |
Settings are considered in the following order:
- Tomcat truststore properties
- System Properties
- Tomcat keystore properties
- Default Values
You can review this behavior in the Tomcat JSSESocketFactory init method.
The easiest way to achieve my goal was to implement my own
XKMSTrustManager
implementing the javax.net.ssl.X509TrustManager
interface.public class XKMSTrustManager implements X509TrustManager { private static final Logger LOG = LoggerFactory.getLogger(XKMSTrustManager.class); private XKMSInvoker xkms; public XKMSTrustManager() throws MalformedURLException { XKMSService xkmsService = new XKMSService( URI.create(System.getProperty("xkms.wsdl.location", "http://localhost:8040/services/XKMS/?wsdl")) .toURL()); xkms = new XKMSInvoker(xkmsService.getXKMSPort()); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { LOG.debug("Check client trust for: {}", chain); validateTrust(chain); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { LOG.debug("Check server trust for: {}", chain); validateTrust(chain); } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[] {}; } protected void validateTrust(X509Certificate[] chain) throws CertificateException { if (chain == null) { throw new CertificateException("Certificate chain is null"); } if (!xkms.validateCertificate(chain)) { LOG.error("Certificate chain is not trusted: {}", chain); throw new CertificateException("Certificate chain is not trusted"); } } }
Tomcat\conf\server.xml
<Server port="9005" shutdown="SHUTDOWN"> <Service name="Catalina"> <Connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" keystoreFile="idp-ssl-key.jks" keystorePass="tompass" trustManagerClassName="org.talend.ws.security.jsse.XKMSTrustManager" clientAuth="true" sslProtocol="TLS" /> </Service> </Server>
However setting a trustManager is only possible if this option is provided by your application or if you have access to the source code of the SSL SocketFactory. In all other cases you will have to implement your own Security Provider providing your own truststore factory. This task is much more challenging. During my internet research for this topic I found several pages, which should be a good reference for you, if you have to go this way:
JCA Reference Guide - Crypto Provider
Howto Implement a JCA Provider
JSSE Reference Guide - Customized Certificate Storage
Custom CA Truststore in addition to System CA Truststore
HowTo Register global security provider
<java-home>/lib/security/java.security
security.provider.2=sun.security.provider.Sun security.provider.3=sun.security.rsa.SunRsaSign security.provider.4=sun.security.provider.SunJCEAdvantage: Multiple providers. Adding just the "missing piece".
Disadvantage: System wide configuration
Override security provider settings with system properties
Changing Security Settings via Code
Security.insertProviderAt(new FooBarProvider(), 1);
Register a TrustManager
put("TrustManagerFactory.SunX509", "sun.security.ssl.TrustManagerFactoryImpl$SimpleFactory"); put("TrustManagerFactory.PKIX", "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory");Using a Custom Certificate Trust Store
Sun JSSE Provider Implementation
No comments:
Post a Comment