As always the steps followed to perform the full configuration are presented one by one.
The last openjdk 11 is installed in the system. The path for the java installation will be ${JAVA_HOME} from now on.
wget https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11%2B9/OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz
tar zxvf OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz
Next wildfly 24.0.1 is installed. The directory where the server is unzipped will be called ${WILDFLY_HOME}.
wget https://download.jboss.org/wildfly/24.0.1.Final/wildfly-24.0.1.Final.zip
unzip wildfly-24.0.1.Final.zip
cd wildfly-24.0.1.Final/bin/
./add-user.sh -u admin -p admin
export JAVA_HOME=/path/to/jdk-11.0.11+9
./standalone
Now from the Bouncy Castle page download the FIPS (bc-fips-1.0.2.1.jar) and the JSSE (bctls-fips-1.0.12.1.jar) provider files. This try the BCJSSE implementation from Bouncy Castle will be used instead of the default SunJSSE used in the previous entry. The jars are downloaded to ${FIPS_HOME} folder.
Time to create the stores with the server certificate and the trusted CAs. The BCFKS custom format is used to show that the BCFIPS provider is really in place.
cd ${WILDFLY_HOME}/standalone/configuration/
${JAVA_HOME}/bin/keytool -providername BCFIPS -providerpath ${FIPS_HOME}/bc-fips-1.0.2.1.jar -providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider -genkeypair -alias localhost -keyalg RSA -keysize 2048 -validity 365 -keystore localhost.bcfks -storetype bcfks -dname "CN=localhost" -storepass secret -keypass secret -ext "SAN=DNS:localhost,IP:127.0.0.1"
cp ${JAVA_HOME}/lib/security/cacerts .
${JAVA_HOME}/bin/keytool -providername BCFIPS -providerpath ${FIPS_HOME}/bc-fips-1.0.2.1.jar -providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider -importkeystore -srckeystore cacerts -destkeystore cacerts.bcfks -srcstoretype JKS -deststoretype bcfks -srcstorepass changeit -deststorepass changeit
The elytron magic starts here. Both jar files are registered as modules inside the server using CLI. Please note that the JSSE module has the main one as a dependency.
module add --name=org.bouncycastle.fips --resources=${FIPS_HOME}/bc-fips-1.0.2.1.jar
module add --name=org.bouncycastle.tls.fips --resources=${FIPS_HOME}/bctls-fips-1.0.12.1.jar --dependencies=org.bouncycastle.fips
The BC documentation (which only explains direct JVM configuration using the java.security file) says that the providers should be configured like this:
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
So the main FIPS provider is defined first and then the JSSE one needs the argument fips:BCFIPS. This means that the latter is in fips mode and it will use the main BCFIPS provider defined previously to perform the security operations. But, how do we do this inside elytron? This is the only tricky point needed for the configuration. Passing one provider to another provider cannot be configured in wildfly. But the BCJSSE also understands a fully qualified class name. Therefore this setup works:
/subsystem=elytron/provider-loader=bc-fips:add(module=org.bouncycastle.fips)
/subsystem=elytron/provider-loader=bc-fips-tls:add(module=org.bouncycastle.tls.fips, class-names=[org.bouncycastle.jsse.provider.BouncyCastleJsseProvider], argument="fips:org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider")
It is extremely important that the BCJSSE provider is registered passing the argument parameter. The value should be fips:org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider, fips mode and the BCFIPS provider but using the class name.
The common configuration for stores and ssl context is performed now, but using the appropriate provider for each step. First the key store and manager:
/subsystem=elytron/key-store=fipsKS:add(path=localhost.bcfks, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=bcfks, providers=bc-fips)
/subsystem=elytron/key-manager=fipsKM:add(key-store=fipsKS, algorithm="PKIX", credential-reference={clear-text=secret}, providers=bc-fips-tls)
Then the same thing for the trust store and manager:
/subsystem=elytron/key-store=fipsCAKS:add(type=bcfks, relative-to=jboss.server.config.dir, path=cacerts.bcfks, credential-reference={clear-text=changeit}, providers=bc-fips)
/subsystem=elytron/trust-manager=fipsTM:add(key-store=fipsCAKS, providers=bc-fips-tls)
The server ssl context is created with the previous elements. Please note that only TLSv1.2 is configured. TLSv1.3 was tried first but it seems that currently the BCFIPS implementation has no ciphers for this version.
/subsystem=elytron/server-ssl-context=fipsSSC:add(key-manager=fipsKM, trust-manager=fipsTM, protocols=["TLSv1.2"], providers=bc-fips-tls)
Finally the ssl context is assigned to the https listener.
batch
/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context, value=fipsSSC)
run-batch
And that is all. The Bouncy Castle FIPS is configured and used for the default 8443 secure port. Using for example curl you can see that the server is working and our certificate is displayed.
curl -k -v -I https://localhost:8443/
Trying 127.0.0.1:8443...
Connected to localhost (127.0.0.1) port 8443 (#0)
ALPN, offering h2
ALPN, offering http/1.1
successfully set certificate verify locations:
CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
TLSv1.3 (OUT), TLS handshake, Client hello (1):
TLSv1.3 (IN), TLS handshake, Server hello (2):
TLSv1.2 (IN), TLS handshake, Certificate (11):
TLSv1.2 (IN), TLS handshake, Server key exchange (12):
TLSv1.2 (IN), TLS handshake, Server finished (14):
TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
TLSv1.2 (OUT), TLS handshake, Finished (20):
TLSv1.2 (IN), TLS handshake, Finished (20):
SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
ALPN, server accepted to use h2
Server certificate:
subject: CN=localhost
start date: Jul 31 10:30:19 2021 GMT
expire date: Jul 31 10:30:19 2022 GMT
issuer: CN=localhost
SSL certificate verify result: self signed certificate (18), continuing anyway.
Using HTTP2, server supports multi-use
Connection state changed (HTTP/2 confirmed)
Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
Using Stream ID: 1 (easy handle 0x55e78bb10aa0)
> HEAD / HTTP/2
> Host: localhost:8443
> user-agent: curl/7.71.1
> accept: /
>
Connection state changed (MAX_CONCURRENT_STREAMS == 4294967295)!
< HTTP/2 200
HTTP/2 200
< last-modified: Tue, 27 Jul 2021 08:03:32 GMT
last-modified: Tue, 27 Jul 2021 08:03:32 GMT
< content-length: 1504
content-length: 1504
< content-type: text/html
content-type: text/html
< accept-ranges: bytes
accept-ranges: bytes
< date: Sat, 31 Jul 2021 11:13:42 GMT
date: Sat, 31 Jul 2021 11:13:42 GMT
At this point there are more options you can be interested in configuring. For example elytron allows to include the JCE providers into the JVM. This way the applications can use the provider as usual (getProvider("BCFIPS") for example). Check the final-providers and the default combined-providers to see how elytron and openssl providers are exposed by default. Another interesting feature is setting the default-ssl-context at JVM level, allowing to configure a BCFIPS ssl context as the default one for applications.
Comments