Keycloak is a very popular Java based open source Identity Access Management product. The recent versions (17.x onward) have gone server-less using Quarkus. When dealing with government, your SSO/ IAM system required to be FedRAMP and FIPS compliant. Keycloak version 21.x+ is FIPS-2 certified, but additional configurations are needed to make the default Keycloak FIPS 140-2 enabled.
Enabling FIPs mode in Keycloak is documented here. The documentation covers the steps in a greater detail. However, I have not seen any IAM deployed out of box, so the documented steps are not enough. I went thru series of hair pulling hours to make it work in my environment. I am jotting down those gotchas here hoping this will help someone.
The FIPS standard revolves around the use of approved crypto module. Keycloak uses only FIPS approved crypto algorithm for it's functionalities when fips-mode is enabled.
Keycloak recommends to use FIPs validated BouncyCastle library when strict fips-mode is enabled. So the starting step is downloading BouncyCastle-FIPS bits and adding them to the Keycloak distribution as a crypto Service Provider, so BCFIPS bits will be used instead of the default bits.
(1) Download BouncyCastle jars
The Keycloak documentation instructs to download the following BouncyCastle jars and add them to KEYCPLAO_HOME/providers directory.
bc-fips-1.0.2.3.jar, bctls-fips-1.0.14.jar, bcpkix-fips-1.0.7.jar
:
ENV KC_FEATURES=fips
ENV KC_FIPS_MODE=strict
ENV KC_HTTPS_PORT=8443
ENV KC_HTTPS_KEY_STORE_TYPE=BCFKS
ENV KC_HOSTNAME="sso.poc.abc.com"
:
ARG BOUNCYCASTLE_FIPS_JAR_LOC='https://downloads.bouncycastle.org/fips-java'
RUN curl -qsSL --retry 3 --retry-delay 0 ${BOUNCYCASTLE_FIPS_JAR_LOC}/bc-fips-1.0.2.3.jar -O --output-dir /opt/keycloak/providers &&\
curl -qsSL --retry 3 --retry-delay 0 ${BOUNCYCASTLE_FIPS_JAR_LOC}/bctls-fips-1.0.14.jar -O --output-dir /opt/keycloak/providers &&\
curl -qsSL --retry 3 --retry-delay 0 ${BOUNCYCASTLE_FIPS_JAR_LOC}/bcpkix-fips-1.0.7.jar -O --output-dir /opt/keycloak/providers &&\
chmod +x /opt/keycloak/providers/*.jar
RUN /opt/keycloak/bin/kc.sh -v build
(2) Use of BouncyCastle Keystore :
By default, Keycloak uses "PKCS12" keystore and the same is also supported for BCFIPS non-approved mode. However "BCFKS" (BouncyCastle key store) is a must when keycloak runs in strict fips mode. BCFKS can be generated various way (from scratch or from existing PKCS12 etc) depending on your use case.
$ echo "securerandom.strongAlgorithms=PKCS11:SunPKCS11-NSS-FIPS" > kc.keystore-create.java.security
$ keytool -genkeypair -noprompt\
-sigalg SHA512withRSA \
-keyalg RSA \
-alias sso.poc.server \
-storetype bcfks \
-dname "CN=sso.poc.volterra.us, OU=f5xc, O=f5, L=San Jose, S=CA, C=US" \
-keystore server.keystore.bcfks \
-storepass ChangeMe! \
-keypass ChangeMe! \
-providername BCFIPS \
-providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-providerpath ./lib/bc-fips-*.jar \
-J-Djava.security.properties=kc.keystore-create.java.security
$ cp server.keystore.bcfks /opt/keycloak/conf/server.keystore
This is a typical use case when you already have a PKCS12 keystore delivered by your PKI team ot some other means. The PKCS12 may have been generated using opnenssl or java keytool.
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -node -subj "/C=US/ST=CA/L=SanJose/O=security/OU=sso/CN=sso.poc.abc.com"
$ openssl pkcs12 -export -nodes -password pass:"" \
-inkey key.pem -in cert.pem \
-name "server" \
-out server.keystore.pkcs12
$ keytool -genkey -noprompt \
-keyalg RSA \
-alias server \
-storetype PKCS12 -validity 3650 -keysize 2048 \
-dname "CN=sso.poc.abc.com, OU=sso, O=security, L=San Jose, S=CA, C=US" \
-keystore server.keystore.pkcs12 \
-storepass ChangeMe! \
-keypass ChangeMe!
$ keytool -importkeystore -v \
-srckeystore server.keystore.pkcs12 \
-srcstorepass "" \
-srcstoretype PKCS12 \
-srcalias "server" \
-srckeypass "" \
-destkeystore server.keystore \
-deststoretype bcfks \
-deststorepass "password" \
-destalias "server" \
-destkeypass "password" \
-providername BCFIPS \
-providerpath ./lib/bc-fips-*.jar \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-J-Djava.security.properties=kc.keystore-create.java.security
# test the BCFKS Keystore that is just created by listing it"
$ keytool -list -v -keystore server.keystore \
-storepass "password" -storetype bcfks \
-providerpath ./lib/bc-fips-*.jar -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
$ cp server.keystore.bcfks /opt/keycloak/conf/server.keystore
My use case was similar to above, but little twisted. My PKCS12 file was delivered by another centralized PKI/ Keystore service through a side car, which runs in my container. So my BCFKS keystore was build from this sidecar provided PKCS12 during my Keycloak container building process. In this scenario, the above code did not work. I get this error while building BCFKS using keytool.
:
RUN microdnf install -y java-11-openjdk-headless ....
:
RUN /opt/keycloak/bin/kc.sh -v build
:
Thanks for stopping by my blog!