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. io.quarkus:quarkus-tls-registry is a transitive dependency of io.quarkiverse.cxf:quarkus-cxf since Quarkus CXF 3.16.0, so you do not have to add it manually.

Here is an example:

application.properties
# 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
# Deprecated way of setting the client trust store
quarkus.cxf.client.hello.trust-store-type = pkcs12
quarkus.cxf.client.hello.trust-store = client-truststore.pkcs12
quarkus.cxf.client.hello.trust-store-password = client-truststore-password

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:

application.properties
# 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:

application.properties
# 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:

https-policy.xml
<?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):

HttpsPolicyHelloService.java
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:

pom.xml
<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:

MutualTlsTest.java
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