SSL, TLS and HTTPS
This section documents various use cases related to SSL, TLS and HTTPS.
The sample code snippets used in this section come from the WS-SecurityPolicy integration test in the source tree of Quarkus CXF |
Client SSL configuration
If your client is going to communicate with a server whose SSL certificate is not trusted by the client’s operating system, then you need to set up a custom trust store for your client.
Tools like openssl
or Java keytool
are commonly used for creating and maintaining truststores.
We have examples for both tools in the Quarkus CXF source tree:
There is a fast and easy way to generate certificates for testing. |
Once you have prepared the trust store, you need to configure your client to use it.
Set the client trust store in application.properties
This is the easiest way to set the client trust store.
The key role is played by named TLS configurations delivered by Quarkus TLS registry
an by the quarkus.cxf.client."client-name".tls-configuration-name
property.
Quarkus TLS registry is an extension that centralizes TLS configuration,
making it easier to manage and maintain secure connections across your application.
|
Here is an example:
# Define a TLS configuration with name "hello-tls" (1)
quarkus.tls.hello-tls.trust-store.p12.path = client-truststore.pkcs12
quarkus.tls.hello-tls.trust-store.p12.password = client-truststore-password
# Basic client settings
quarkus.cxf.client.hello.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/hello
quarkus.cxf.client.hello.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService
# Use "hello-tls" defined above for this client
quarkus.cxf.client.hello.tls-configuration-name = hello-tls
1 | The referenced client-truststore.pkcs12 file has to be available either in the classpath
or in the file system. |
The new way of configuring TLS is optimized for the new Vert.x based CXF clients (default since Quarkus CXF 3.16.0). For those, all client-related options provided by Quarkus TLS registry are supported.
The named TLS configurations provided by TLS registry can be also used for CXF clients having http-conduit-factory
set to
URLConnectionHTTPConduitFactory
, HttpClientHTTPConduitFactory
or with
Async CXF clients on top of Apache HttpClient 5.
However, in those cases, the following TLS options are not supported and using them will lead to an exception at runtime:
The older way of configuring the client trust store is still supported, but deprecated since Quarkus CXF 3.16.0: application.properties
|
Server SSL configuration
To make your services available over the HTTPS protocol, you need to set up server keystore in the first place. The server SSL configuration is driven by Vert.x, the HTTP layer of Quarkus. Quarkus HTTP guide and Quarkus TLS registry reference provide the information about the configuration options.
Here is a basic example:
# Server side SSL
quarkus.tls.key-store.p12.path = localhost-keystore.pkcs12
quarkus.tls.key-store.p12.password = localhost-keystore-password
quarkus.tls.key-store.p12.alias = localhost
quarkus.tls.key-store.p12.alias-password = localhost-keystore-password
Mutual TLS (mTLS) authentication
So far, we have explained the simple or single-sided case where only the server proves its identity through an SSL certificate and the client has to be set up to trust that certificate. Mutual TLS authentication goes by letting also the client prove its identity using the same means of public key cryptography.
Hence, for the Mutual TLS (mTLS) authentication, in addition to setting up the server keystore and client truststore as described above, you need to setup the keystore on the client side and the truststore on the server side.
The tools for creating and maintaining the stores are the same and the configuration properties to use are pretty much analogous to the ones used in the Simple TLS case.
The mTLS integration test in the Quarkus CXF source tree can serve as a good starting point.
The keystores and truststores can be created with
openssl
(or alternatively with Java Java keytool
)
Here is the application.properties
file:
# Server keystore for Simple TLS
quarkus.tls.localhost-pkcs12.key-store.p12.path = localhost-keystore.p12
quarkus.tls.localhost-pkcs12.key-store.p12.password = secret
quarkus.tls.localhost-pkcs12.key-store.p12.alias = localhost
quarkus.tls.localhost-pkcs12.key-store.p12.alias-password = secret
# Server truststore for Mutual TLS
quarkus.tls.localhost-pkcs12.trust-store.p12.path = localhost-server-truststore.p12
quarkus.tls.localhost-pkcs12.trust-store.p12.password = secret
# Select localhost-pkcs12 as the TLS configuration for the HTTP server
quarkus.http.tls-configuration-name = localhost-pkcs12
# Do not allow any clients which do not prove their indentity through an SSL certificate
quarkus.http.ssl.client-auth = required
# CXF service
quarkus.cxf.endpoint."/mTls".implementor = io.quarkiverse.cxf.it.auth.mtls.MTlsHelloServiceImpl
# CXF client with a properly set certificate for mTLS
quarkus.cxf.client.mTls.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/mTls
quarkus.cxf.client.mTls.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService
# Set client-pkcs12 as the TLS configuration for the this client
quarkus.cxf.client.mTls.tls-configuration-name = client-pkcs12
# Named TLS configuration for the client
quarkus.tls.client-pkcs12.key-store.p12.path = target/classes/localhost-client-keystore.p12
quarkus.tls.client-pkcs12.key-store.p12.password = secret
quarkus.tls.client-pkcs12.key-store.p12.alias = client
quarkus.tls.client-pkcs12.key-store.p12.alias-password = secret
quarkus.tls.client-pkcs12.trust-store.p12.path = target/classes/localhost-truststore.p12
quarkus.tls.client-pkcs12.trust-store.p12.password = secret
# Include the keystores in the native executable
quarkus.native.resources.includes = *.pkcs12,*.jks
Enforce SSL through WS-SecurityPolicy
The requirement for the clients to connect through HTTPS can be defined in a policy.
The functionality is provided by quarkus-cxf-rt-ws-security
extension.
Here is an example of a policy file:
<?xml version="1.0" encoding="UTF-8"?>
<wsp:Policy wsp:Id="HttpsSecurityServicePolicy"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<wsp:ExactlyOne>
<wsp:All>
<sp:TransportBinding>
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate="false" />
</wsp:Policy>
</sp:TransportToken>
<sp:IncludeTimestamp />
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic128 />
</wsp:Policy>
</sp:AlgorithmSuite>
</wsp:Policy>
</sp:TransportBinding>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
The policy has to be referenced from a service endpoint interface (SEI):
package io.quarkiverse.cxf.it.security.policy;
import jakarta.jws.WebMethod;
import jakarta.jws.WebService;
import org.apache.cxf.annotations.Policy;
/**
* A service implementation with a transport policy set
*/
@WebService(serviceName = "HttpsPolicyHelloService")
@Policy(placement = Policy.Placement.BINDING, uri = "https-policy.xml")
public interface HttpsPolicyHelloService extends AbstractHelloService {
@WebMethod
@Override
public String hello(String text);
}
With this setup in place, any request delivered over HTTP will be rejected by the PolicyVerificationInInterceptor
:
ERROR [org.apa.cxf.ws.pol.PolicyVerificationInInterceptor] Inbound policy verification failed: These policy alternatives can not be satisfied:
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}TransportBinding: TLS is not enabled
...
SSL certificates for testing
SmallRye Certificate Generator project offers a Junit 5 extension to generate certificates in tests.
First, you need to add the appropriate dependency to your project:
<dependency>
<groupId>io.smallrye.certs</groupId>
<artifactId>smallrye-certificate-generator-junit5</artifactId>
<scope>test</scope>
</dependency>
Then you can use the @Certificates
annotation in your tests:
import io.smallrye.certs.Format;
import io.smallrye.certs.junit5.Alias;
import io.smallrye.certs.junit5.Certificate;
import io.smallrye.certs.junit5.Certificates;
@Certificates(baseDir = "target/classes", //
certificates = @Certificate( //
name = "localhost", //
password = "secret", //
aliases = @Alias(//
name = "client", //
password = "secret", //
client = true), //
formats = { Format.PKCS12, Format.JKS }))
@QuarkusTest
public class MutualTlsTest {
...
}
This will generate the following files (plus the same files in JKS format) suitable for testing mutual TLS authentication:
-
target/classes/localhost-client-keystore.p12
- the client key store -
target/classes/localhost-keystore.p12
- the server key store -
target/classes/localhost-server-truststore.p12
- the server trust store -
target/classes/localhost-truststore.p12
- the client trust store