SSL, TLS and HTTPS

This chapter 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 for creating and maintaining truststores.

We have examples for both tools in Quarkus CXF source tree:

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 the following properties:

Here is an example:

application.properties
keystore.type = jks (1)
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
quarkus.cxf.client.hello.trust-store-type = ${keystore.type}
(2)
quarkus.cxf.client.hello.trust-store = client-truststore.${keystore.type}
quarkus.cxf.client.hello.trust-store-password = client-truststore-password
1 pkcs12 trust store type is a common alternative to jks.
2 The referenced client-truststore.jks file has to be available in src/main/resources directory.

Server SSL configuration

To make your services available over the HTTPS protocol, you need to setup server keystore in the first place. The server SSL configuration is driven by Vert.x, the HTTP layer of Quarkus. Quarkus HTTP guide provides the information about the configuration options.

Here is a basic example:

application.properties
(1)
quarkus.http.ssl.certificate.key-store-file = localhost-keystore.${keystore.type}
quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password
quarkus.http.ssl.certificate.key-store-key-alias = localhost
quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password
1 The referenced localhost.jks file has to be available in src/main/resources directory.

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 are created with openssl (or alternatively with Java Java keytool)

Here is the application.properties file:

application.properties
# The store type could also be jks
keystore.type = pkcs12

# Server keystore for Simple TLS
quarkus.http.ssl.certificate.key-store-file = localhost-keystore.${keystore.type}
quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password
quarkus.http.ssl.certificate.key-store-key-alias = localhost
quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password
# Server truststore for Mutual TLS
quarkus.http.ssl.certificate.trust-store-file = localhost-truststore.${keystore.type}
quarkus.http.ssl.certificate.trust-store-password = localhost-truststore-password
# 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
quarkus.cxf.client.mTls.key-store = target/classes/client-keystore.${keystore.type}
quarkus.cxf.client.mTls.key-store-type = ${keystore.type}
quarkus.cxf.client.mTls.key-store-password = client-keystore-password
quarkus.cxf.client.mTls.key-password = client-keystore-password
quarkus.cxf.client.mTls.trust-store = target/classes/client-truststore.${keystore.type}
quarkus.cxf.client.mTls.trust-store-type = ${keystore.type}
quarkus.cxf.client.mTls.trust-store-password = client-truststore-password

# 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
 ...