JBoss to Spring Boot and Spring Security upgrade: X509 Authentication problems - java

UPGRADING FROM: JBoss EAP 6.4 / Spring Security 4.2.1 (XML Configuration)
UPGRADING TO: Spring Boot 2.2.4 / Spring Security 5.2.1 (JavaConfig)
We have a REST endpoint protected by Spring Security that uses X509 authentication and a Test Client.
I breakpoint in Spring Security's Filter Chain at X509AuthenticationFilter.extractClientCertificate:
X509Certificate[] certs = (X509Certificate[]) request
.getAttribute("javax.servlet.request.X509Certificate");
I am no longer seeing the X509 Certificate coming across in the Request after going to Spring Boot and Spring Security 5.2 JavaConfig.
Didn't post source code because I don't expect anyone to fix my issue ... I'm just looking for some suggestions on where to dig or possible paths to explore. Some thoughts ...
Does Spring Security in Spring Boot use the embedded Tomcat Connectors? If I have a Tomcat Connector for HTTPS and the client hits us on HTTPS, do I need to somehow tell the Tomcat Connector for HTTPS to extract the X509 Cert from the Request?
How or what puts the X509 Certificate into the Request with an attribute of javax.servlet.request.X509Certificate? I was debugging against the old (working) JBoss EAP app and never could figure out how it got into the Request, only that it was there when I breakpoint in Spring Security's X509AuthenticationFilter.

If anyone ever comes across this ... I resolved it and will be happy to answer questions if it helps others.
It turns out not to be an issue with Spring Security, but rather with how we had the Tomcat Connectors configured.
We needed to:
Set up the truststore for the Spring Boot Tomcat Secure Connector correctly, using the method calls below that are compatible with org.apache.coyote.http11.Http11NioProtocol.
Also (and this is essential) - setClientAuth to "true".
It's ugly, I dug into the source code for Tomcat-embed-core, but there are a series of strings that are the equivalent of "force the client to send the certificate as part of the Request". Those strings are: "true", "yes", "require", "required". They all do the same thing, so we just used "true".
protocol.setTruststoreFile(truststorePath); // local directory path
protocol.setTruststoreType(truststoreType); // a string of either PKCS12 or JKS
protocol.setTruststorePass(truststorePass); // whatever password you set on your Keystore
protocol.setClientAuth("true"); // Makes the client provide cert in Request over HTTPS
Tomcat Connectors are not well-documented. There is a method call:
protocol.setSSLCACertificateFile(trustStorePath);
that does nothing under org.apache.coyote.http11.Http11NioProtocol.
Apparently it is meant to work with org.apache.coyote.http11.Http11AprProtocol (Apache Portable Runtime). That was a red herring for us, we made that method call, but it was not in fact setting up our truststore.

Related

SpringBoot MVC - Warning: org.apache.tomcat.util.net.SSLUtilBase : The JSSE TLS 1.3 implementation does not support authentication

A question about Spring Boot MVC with Tomcat and TLSv1.3
I used to have a Spring Boot MVC, Tomcat based web app, with very simple business logic, over ssl HTTPS.
Per security team review, I had to bump the TLS version from TLSv1.2 to TLSv1.3.
Thought is was very simple and could easily complete this task, I went to change my property:
server.ssl.enabled-protocols=TLSv1.2
to
server.ssl.enabled-protocols=TLSv1.3
However, since then, I am getting this on each application start up:
org.apache.tomcat.util.net.SSLUtilBase : The JSSE TLS 1.3 implementation does not support authentication after the initial handshake and is therefore incompatible with optional client authentication
What does it mean please?
Is it "dangerous"?
How to fix it please?
Thank you
Post-Handshake Client Authentication is a TLSv1.3 extension defined in RFC8446. But OpenJDK doesn't implement it and will not implement it. The corresponding issue is marked as "Won't fix".
The warning is emitted by Tomcat in SSLUtilBase.java
if (enabledProtocols.contains(Constants.SSL_PROTO_TLSv1_3) &&
sslHostConfig.getCertificateVerification() == CertificateVerification.OPTIONAL &&
!isTls13RenegAuthAvailable() && warnTls13) {
log.warn(sm.getString("sslUtilBase.tls13.auth"));
}
The isTls13RenegAuthAvailable() method is defined in JSSEUtil.java
#Override
protected boolean isTls13RenegAuthAvailable() {
// TLS 1.3 does not support authentication after the initial handshake
return false;
}
To remove this warning you can either set CertificateVerification in Tomcat's SSLHostConfig to NONE or to REQUIRED. You can do it through the Spring Boot property server.ssl.client-auth which take the values NONE, WANT and NEED.
If you don't use client certificates, set it to NONE. If you use client certificates, check that each client can authenticate itself correctly with the NEED value. If you leave it as it, the only risk is that client that use post-handshake authentication will not be able to authenticate.
If you really need post-handshake client authentication, you will have to use another TLS implementation than JSSE. You can either use a reverse proxy such as Apache, NGINX, Traefik or use Tomcat’s native bindings for APR/OpenSSL. There is an interesting article you can read about this: Tomcat Native / OpenSSL in Spring Boot 2.0

Spring security client PKCE with Keycloak

I have a Java application using Spring Security 5.2.1 and secured by Keycloak.
The client in Keycloak is a public openid-connect client.
It works fine.
I have now a requirement to use PKCE (Proof Key for Code Exchange).
As Client Support for PKCE has been added to Spring Security 5.2.0.M2 and as I use Spring Security 5.2.1, I can use Spring Security to implement it.
That's the good news.
The 'bad' news is that I found nearly nothing on the Web or in the Spring Security documentation on how I must implement it, practically.
Adding "enable-pkce": true in keycloak.json doesn't work, and I don't find any clear example of what to do.
Is there some documentation, website or whatever else, describing what to do to implementsthis ?
Thank you very much !
From the Spring Security reference documentation https://docs.spring.io/spring-security/site/docs/5.3.1.RELEASE/reference/html5/#initiating-the-authorization-request
PKCE will automatically be used when the following conditions are true:
client-secret is omitted (or empty)
client-authentication-method is set to "none" (ClientAuthenticationMethod.NONE)

Configure SSL on Camel rest-component using Spring-Boot with embedded Jetty

Been bashing my head in for a few days trying to get SSL working with an existing rest endpoint. Currently using self-signed certificates in a jks.
We have a rest route (not this route, but very similar):
#Override
public void configure() {
restConfiguration()
.component("jetty")
.scheme("https")
.bindingMode(RestBindingMode.off)
.dataFormatProperty("prettyPrint", "true")
.port(8443);
rest("/post")
.post()
.consumes("application/json")
.produces("application/json")
.to( // next endpoint // );
This works perfectly over HTTP. When we set the scheme as HTTPS, Jetty throws a SSL cypher mismatch error when a request is sent to it; I'd imagine this is because no SSL configuration is being picked up.
I've tried some of the examples on the internet (such as configuring Jetty from the application.properties), but that doesn't actually seem to do anything at all.
Any help appreciated, all of the routes in the project are written using the Camel Java DSL, not the XML equivalent.
This has since been fixed. We discovered that the correct method of doing this was by configuring the embedded Jetty itself, rather than modifying the camel route.
This was achievable by instantiating a JettyHttpComponent, and setting the SSLContextParameters on it.

How to call a web security with message security and client certificate authentication?

I need to call a web service with a java client.
This service authenticates clients through certificates at the message level (Ws-Security, not SSL).
It should be possible since, I can generate web services with JAX-WS with mutual certificate security in this dialog.
But I don't manage to create a client. Does anyone has an idea ?
I did not tried it myself, but from http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/ :
Configuring Message Security Using XWSS
The Application Server contains all of the JAR files necessary to use XWS-Security for securing JAX-WS applications, however, in order to view the sample applications, you must download and install the standalone Java WSDP bundle. You can download the Java WSDP from http://java.sun.com/webservices/downloads/webservicespack.html.
To add message security to an existing JAX-WS application using XWSS, follow these steps on the client side:
Create a client security configuration. The client security configuration file specifies the order and type of message security operations that will be used for the client application. For example, a simple security configuration to perform a digital signature operation looks like this:
<xwss:Sign id="s" includeTimestamp="true">
<xwss:X509Token encodingType="http://docs.oasis-
open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
valueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-
x509-token-profile-1.0#X509SubjectKeyIdentifier"
certificateAlias="xws-security-client"
keyReferenceType="Identifier"/>
</xwss:Sign>
</xwss:SecurityConfiguration>
</xwss:Service>
<xwss:SecurityEnvironmentHandler>
simple.client.SecurityEnvironmentHandler
</xwss:SecurityEnvironmentHandler>
For more information on writing and understanding security configurations and setting up SecurityEnvironmentHandlers, please see the Java Web Services Developer Pack 1.6 Tutorial at http://java.sun.com/webservices/docs/1.6/tutorial/doc/index.html.
In your client code, create an XWSSecurityConfiguration object initialized with the security configuration generated. Here is an example of the code that you would use in your client file. For an example of a complete file that uses this code, look at the example client in the \jaxws2.0\simple-doclit\src\simple\client\ directory.
FileInputStream f = new FileInputStream("./etc/client_security_config.xml");
XWSSecurityConfiguration config = SecurityConfigurationFactory.newXWSSecurityConfiguration(f);
Set security configuration information on the RequestContext by using the XWSSecurityConfiguration.MESSAGE_SECURITY_CONFIGURATION property. For an example of a complete file that uses this code, look at the example client in the \jaxws2.0\simple-doclit\src\simple\client\ directory.
// put the security config info
((BindingProvider)stub).getRequestContext().put(
XWSSecurityConfiguration.MESSAGE_SECURITY_CONFIGURATION,
config);
Invoke the method on the stub as you would if you were writing the client without regard to adding XWS-Security. The example for the application from the \jaxws2.0\simple-doclit\src\simple\client\ directory is as shown below:
Holder<String> hold = new Holder("Hello !");
stub.ping(ticket, hold);

SSL Connection problem to web service from Java bean

I have written an application that connects to a SSL web service (including client certificate) through jaxws. For this to work I have a wstrust.jks that contains trusted root certificate for ws, and client.p12 that is the client certificate to use when connecting to ws. I have then created a custom SSLSocketFactory to be able to use my wstrust.jks and client.12 during the connection to ws. I tell jaxws to use my implementation by:
[javax.xml.ws.BindingProvider].getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, customSSLSocketFactory);
Everything works like a charm if i run it as a standalone java-application. However when i use the same technique inside a Java bean (JSF) deployed as a war-file running under Tomcat,
i get a "PKIX path building failed"-error.
BUT If i configure SSL through JAVA_OPTS when i start my Tomcat (through the -Djavax.net.ssl.* parameters) I get it to work.
So my question:
How do i (or is it possible) to get my custom-SSLSocketFactory-technique to work inside the Java bean?
I guess as tomcat wraps itself around my application, when running as a bean, it is working differently and my wish to use a custom SSLSocketFactory isnt respected...
Thanks for any input on this!
/Tobbe
Solved it. If anyone have the same issue here is how. Instead of setting my custom factory through:
[javax.xml.ws.BindingProvider].getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, customSSLSocketFactory);
I had to set it through:
HttpsURLConnection.setDefaultSSLSocketFactory(customSSLSocketFactory);
otherwise it seems to get ignored.
/Tobbe

Categories