From 264c5a6cdb2fb86e20536ea4302d20160ca01919 Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 1 Dec 2022 14:30:48 +0100 Subject: [PATCH] Support for KcReg and KcAdm CLI to use BCFIPS instead of BC on FIPS platforms Closes #14968 --- .github/workflows/ci.yml | 2 +- .../crypto/fips/test/FIPS1402SslTest.java | 1 + docs/fips.md | 20 ++++++- integration/client-cli/admin-cli/pom.xml | 4 -- .../admin-cli/src/main/bin/kcadm.bat | 2 +- .../admin-cli/src/main/bin/kcadm.sh | 2 +- .../keycloak/client/admin/cli/KcAdmMain.java | 9 ++- .../admin/cli/util/ClassLoaderUtil.java | 60 +++++++++++++++++++ .../client-cli/client-cli-dist/assembly.xml | 8 +++ .../client-cli/client-cli-dist/pom.xml | 30 ++++++++++ .../client-registration-cli/pom.xml | 16 ----- .../src/main/bin/kcreg.bat | 2 +- .../src/main/bin/kcreg.sh | 2 +- .../client/registration/cli/KcRegMain.java | 9 ++- .../cli/util/ClassLoaderUtil.java | 60 +++++++++++++++++++ .../auth-server/common/fips/kc.java.security | 3 + .../servers/auth-server/quarkus/pom.xml | 11 ++++ .../integration-arquillian/tests/base/pom.xml | 34 +++++++++++ .../arquillian/AuthServerTestEnricher.java | 5 ++ .../testsuite/cli/AbstractCliTest.java | 10 +++- .../cli/admin/AbstractAdmCliTest.java | 4 +- .../testsuite/cli/admin/KcAdmCreateTest.java | 2 +- .../testsuite/cli/admin/KcAdmSessionTest.java | 4 +- .../testsuite/cli/admin/KcAdmTest.java | 12 ++-- .../cli/registration/AbstractRegCliTest.java | 2 +- .../cli/registration/KcRegCreateTest.java | 32 +++++----- .../testsuite/cli/registration/KcRegTest.java | 12 ++-- .../cli/registration/KcRegUpdateTest.java | 2 +- .../integration-arquillian/tests/pom.xml | 6 ++ 29 files changed, 302 insertions(+), 64 deletions(-) create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java create mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cefa0743a..25fc14bf8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -340,7 +340,7 @@ jobs: PARAMS["bcfips-nonapproved-pkcs12"]="-Pauth-server-quarkus,auth-server-fips140-2" # Tests in the package "forms" and some keystore related tests TESTGROUP["group1"]="-Dtest=org.keycloak.testsuite.forms.**,ClientAuthSignedJWTTest,CredentialsTest,JavaKeystoreKeyProviderTest,ServerInfoTest,UserFederationLdapConnectionTest,LDAPUserLoginTest" - TESTGROUP["group2"]="-Dtest=org.keycloak.testsuite.x509.**,MutualTLSClientTest,FAPI1Test,FAPICIBATest" # Tests for X.509 authentication with users and clients + TESTGROUP["group2"]="-Dtest=org.keycloak.testsuite.x509.**,MutualTLSClientTest,FAPI1Test,FAPICIBATest,KcRegTest,KcRegCreateTest,KcAdmTest,KcAdmCreateTest" # Tests for X.509 authentication with users and clients and CLI tests ./mvnw clean install -nsu -B ${PARAMS["${{ matrix.server }}"]} ${TESTGROUP["${{ matrix.tests }}"]} -f testsuite/integration-arquillian/tests/base/pom.xml | misc/log/trimmer.sh diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SslTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SslTest.java index 085f542dca..9f560b07f0 100644 --- a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SslTest.java +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402SslTest.java @@ -129,6 +129,7 @@ public class FIPS1402SslTest { List supportedProtocols = Arrays.asList(context.getDefaultSSLParameters().getProtocols()); List supportedCiphers = Arrays.asList(engine.getSupportedCipherSuites()); + logger.infof("SSLContext provider: %s, SSLContext class: %s", context.getProvider().getName(), context.getClass().getName()); logger.infof("Enabled ciphersuites: %s", enabledCipherSuites.size()); logger.infof("Supported protocols: %s", supportedProtocols); logger.infof("Supported ciphers size: %d", supportedCiphers.size()); diff --git a/docs/fips.md b/docs/fips.md index f3012532db..e41c09d1d7 100644 --- a/docs/fips.md +++ b/docs/fips.md @@ -20,9 +20,12 @@ running the unit tests below): ``` cd $KEYCLOAK_HOME/bin export MAVEN_REPO_HOME=$HOME/.m2/repository -cp $MAVEN_REPO_HOME/org/bouncycastle/bc-fips/1.0.2.3/bc-fips-1.0.2.3.jar ../providers/ -cp $MAVEN_REPO_HOME/org/bouncycastle/bctls-fips/1.0.12.2/bctls-fips-1.0.12.2.jar ../providers/ -cp $MAVEN_REPO_HOME/org/bouncycastle/bcpkix-fips/1.0.5/bcpkix-fips-1.0.5.jar ../providers/ +export BCFIPS_VERSION=1.0.2.3 +export BCTLSFIPS_VERSION=1.0.12.2 +export BCPKIXFIPS_VERSION=1.0.5 +cp $MAVEN_REPO_HOME/org/bouncycastle/bc-fips/$BCFIPS_VERSION/bc-fips-$BCFIPS_VERSION.jar ../providers/ +cp $MAVEN_REPO_HOME/org/bouncycastle/bctls-fips/$BCTLSFIPS_VERSION/bctls-fips-$BCTLSFIPS_VERSION.jar ../providers/ +cp $MAVEN_REPO_HOME/org/bouncycastle/bcpkix-fips/$BCPKIXFIPS_VERSION/bcpkix-fips-$BCPKIXFIPS_VERSION.jar ../providers/ ``` 2) Now create either pkcs12 or bcfks keystore. The pkcs12 works just in BCFIPS non-approved mode. @@ -114,6 +117,17 @@ Note that in approved mode, there are few limitations at the moment like for exa - Keystore/truststore must be of type bcfks due the both of `jks` and `pkcs12` don't work - Some warnings in the server.log at startup +Run the CLI on the FIPS host +---------------------------- +In case you want to run Client Registration CLI (`kcreg.sh/bat` script) or Admin CLI (`kcadm.sh/bat` script), it is needed +that CLI will also use the BouncyCastle FIPS dependencies instead of plain BouncyCastle dependencies. To achieve this, you may copy the +jars to the CLI library folder and that is enough. CLI tool will automatically use BCFIPS dependencies instead of plain BC when +it detects that corresponding BCFIPS jars are present (see above for the versions used): +``` +cp $MAVEN_REPO_HOME/org/bouncycastle/bc-fips/$BCFIPS_VERSION/bc-fips-$BCFIPS_VERSION.jar ../bin/client/lib/ +cp $MAVEN_REPO_HOME/org/bouncycastle/bctls-fips/$BCTLSFIPS_VERSION/bctls-fips-$BCTLSFIPS_VERSION.jar ../bin/client/lib/ +``` + Run the unit tests in the FIPS environment ------------------------------------------ This instruction is about running automated tests on the FIPS enabled RHEL 8.6 system with the FIPS enabled OpenJDK 11. diff --git a/integration/client-cli/admin-cli/pom.xml b/integration/client-cli/admin-cli/pom.xml index b4777ac227..6692be8031 100755 --- a/integration/client-cli/admin-cli/pom.xml +++ b/integration/client-cli/admin-cli/pom.xml @@ -38,10 +38,6 @@ org.keycloak keycloak-core - - org.keycloak - ${keycloak.crypto.artifactId} - org.apache.httpcomponents httpclient diff --git a/integration/client-cli/admin-cli/src/main/bin/kcadm.bat b/integration/client-cli/admin-cli/src/main/bin/kcadm.bat index 992b4f7cae..b8ea360774 100644 --- a/integration/client-cli/admin-cli/src/main/bin/kcadm.bat +++ b/integration/client-cli/admin-cli/src/main/bin/kcadm.bat @@ -5,4 +5,4 @@ if "%OS%" == "Windows_NT" ( ) else ( set DIRNAME=.\ ) -java %KC_OPTS% -cp "%DIRNAME%\client\keycloak-admin-cli-${project.version}.jar" org.keycloak.client.admin.cli.KcAdmMain %* +java %KC_OPTS% -cp "%DIRNAME%\client\keycloak-admin-cli-${project.version}.jar" -Dkc.lib.dir="%DIRNAME%\client\lib" org.keycloak.client.admin.cli.KcAdmMain %* diff --git a/integration/client-cli/admin-cli/src/main/bin/kcadm.sh b/integration/client-cli/admin-cli/src/main/bin/kcadm.sh index 60a9e05f5c..49db302a18 100755 --- a/integration/client-cli/admin-cli/src/main/bin/kcadm.sh +++ b/integration/client-cli/admin-cli/src/main/bin/kcadm.sh @@ -29,4 +29,4 @@ if [ "x$JAVA" = "x" ]; then fi fi -"$JAVA" $KC_OPTS -cp $DIRNAME/client/keycloak-admin-cli-${project.version}.jar org.keycloak.client.admin.cli.KcAdmMain "$@" +"$JAVA" $KC_OPTS -cp $DIRNAME/client/keycloak-admin-cli-${project.version}.jar -Dkc.lib.dir=$DIRNAME/client/lib org.keycloak.client.admin.cli.KcAdmMain "$@" diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java index 4afa122b57..107276e635 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java @@ -27,6 +27,7 @@ import org.keycloak.client.admin.cli.aesh.AeshEnhancer; import org.keycloak.client.admin.cli.aesh.Globals; import org.keycloak.client.admin.cli.aesh.ValveInputStream; import org.keycloak.client.admin.cli.commands.KcAdmCmd; +import org.keycloak.client.admin.cli.util.ClassLoaderUtil; import org.keycloak.common.crypto.CryptoIntegration; import java.util.ArrayList; @@ -38,8 +39,14 @@ import java.util.Arrays; public class KcAdmMain { public static void main(String [] args) { + String libDir = System.getProperty("kc.lib.dir"); + if (libDir == null) { + throw new RuntimeException("System property kc.lib.dir needs to be set"); + } + ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir); + Thread.currentThread().setContextClassLoader(cl); - CryptoIntegration.init(KcAdmMain.class.getClassLoader()); + CryptoIntegration.init(cl); Globals.stdin = new ValveInputStream(); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java new file mode 100644 index 0000000000..44d66072f3 --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java @@ -0,0 +1,60 @@ +/* + * Copyright 2022 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.keycloak.client.admin.cli.util; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.stream.Stream; + +/** + * @author Marek Posolda + */ +public class ClassLoaderUtil { + + /** + * Detect if BC FIPS jars are present in the given directory. Return classloader with appropriate JARS based on that + */ + public static ClassLoader resolveClassLoader(String libDir) { + File[] jarsInDir = new File(libDir).listFiles(file -> file.getName().endsWith(".jar")); + + // Detect if BC FIPS jars are present in the "client/lib" directory + boolean bcFipsJarPresent = Stream.of(jarsInDir).anyMatch(file -> file.getName().startsWith("bc-fips")); + String[] validJarPrefixes = bcFipsJarPresent ? new String[] {"keycloak-crypto-fips1402", "bc-fips", "bctls-fips"} : new String[] {"keycloak-crypto-default", "bcprov-jdk15on"}; + URL[] usedJars = Stream.of(jarsInDir) + .filter(file -> { + for (String prefix : validJarPrefixes) { + if (file.getName().startsWith(prefix + "-")) return true; + } + return false; + }) + .map(file -> { + try { + return file.toURI().toURL(); + } catch (MalformedURLException ex) { + throw new IllegalStateException("Error when converting file into URL. Please check the files in the directory " + jarsInDir, ex); + } + }).toArray(URL[]::new); + + return new URLClassLoader(usedJars, ClassLoaderUtil.class.getClassLoader()); + } + +} diff --git a/integration/client-cli/client-cli-dist/assembly.xml b/integration/client-cli/client-cli-dist/assembly.xml index fc74f20770..42cb0f2d7f 100755 --- a/integration/client-cli/client-cli-dist/assembly.xml +++ b/integration/client-cli/client-cli-dist/assembly.xml @@ -56,6 +56,14 @@ keycloak-client-tools/bin/client + + + org.keycloak:keycloak-crypto-default + org.keycloak:keycloak-crypto-fips1402 + org.bouncycastle:bcprov-jdk15on + + keycloak-client-tools/bin/client/lib + diff --git a/integration/client-cli/client-cli-dist/pom.xml b/integration/client-cli/client-cli-dist/pom.xml index 49187f93e2..71694e0bd9 100755 --- a/integration/client-cli/client-cli-dist/pom.xml +++ b/integration/client-cli/client-cli-dist/pom.xml @@ -38,6 +38,36 @@ org.keycloak keycloak-admin-cli + + org.keycloak + keycloak-crypto-default + + + * + * + + + + + org.keycloak + keycloak-crypto-fips1402 + + + * + * + + + + + org.bouncycastle + bcprov-jdk15on + + + * + * + + + diff --git a/integration/client-cli/client-registration-cli/pom.xml b/integration/client-cli/client-registration-cli/pom.xml index c0299337f3..836a223a80 100755 --- a/integration/client-cli/client-registration-cli/pom.xml +++ b/integration/client-cli/client-registration-cli/pom.xml @@ -38,10 +38,6 @@ org.keycloak keycloak-core - - org.keycloak - ${keycloak.crypto.artifactId} - org.jboss.logging jboss-logging @@ -95,18 +91,6 @@ org/keycloak/common/crypto/** - - org.bouncycastle:bcprov-jdk15on - - **/** - - - - org.bouncycastle:bcpkix-jdk15on - - **/** - - com.fasterxml.jackson.core:jackson-core diff --git a/integration/client-cli/client-registration-cli/src/main/bin/kcreg.bat b/integration/client-cli/client-registration-cli/src/main/bin/kcreg.bat index 74241a4c2d..35bda5919c 100644 --- a/integration/client-cli/client-registration-cli/src/main/bin/kcreg.bat +++ b/integration/client-cli/client-registration-cli/src/main/bin/kcreg.bat @@ -5,4 +5,4 @@ if "%OS%" == "Windows_NT" ( ) else ( set DIRNAME=.\ ) -java %KC_OPTS% -cp "%DIRNAME%\client\keycloak-client-registration-cli-${project.version}.jar" org.keycloak.client.registration.cli.KcRegMain %* +java %KC_OPTS% -cp "%DIRNAME%\client\keycloak-client-registration-cli-${project.version}.jar" -Dkc.lib.dir="%DIRNAME%\client\lib" org.keycloak.client.registration.cli.KcRegMain %* diff --git a/integration/client-cli/client-registration-cli/src/main/bin/kcreg.sh b/integration/client-cli/client-registration-cli/src/main/bin/kcreg.sh index ce85fb5710..ada4580006 100755 --- a/integration/client-cli/client-registration-cli/src/main/bin/kcreg.sh +++ b/integration/client-cli/client-registration-cli/src/main/bin/kcreg.sh @@ -28,4 +28,4 @@ if [ "x$JAVA" = "x" ]; then fi DIRNAME=`dirname "$RESOLVED_NAME"` -"$JAVA" $KC_OPTS -cp $DIRNAME/client/keycloak-client-registration-cli-${project.version}.jar org.keycloak.client.registration.cli.KcRegMain "$@" +"$JAVA" $KC_OPTS -cp $DIRNAME/client/keycloak-client-registration-cli-${project.version}.jar -Dkc.lib.dir=$DIRNAME/client/lib org.keycloak.client.registration.cli.KcRegMain "$@" diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java index 7036d3dbac..5e42799144 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java @@ -11,6 +11,7 @@ import org.keycloak.client.registration.cli.aesh.AeshEnhancer; import org.keycloak.client.registration.cli.aesh.ValveInputStream; import org.keycloak.client.registration.cli.aesh.Globals; import org.keycloak.client.registration.cli.commands.KcRegCmd; +import org.keycloak.client.registration.cli.util.ClassLoaderUtil; import org.keycloak.common.crypto.CryptoIntegration; import java.util.ArrayList; @@ -22,8 +23,14 @@ import java.util.Arrays; public class KcRegMain { public static void main(String [] args) { + String libDir = System.getProperty("kc.lib.dir"); + if (libDir == null) { + throw new RuntimeException("System property kc.lib.dir needs to be set"); + } + ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir); + Thread.currentThread().setContextClassLoader(cl); - CryptoIntegration.init(KcRegMain.class.getClassLoader()); + CryptoIntegration.init(cl); Globals.stdin = new ValveInputStream(); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java new file mode 100644 index 0000000000..ffaeab8877 --- /dev/null +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java @@ -0,0 +1,60 @@ +/* + * Copyright 2022 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.keycloak.client.registration.cli.util; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.stream.Stream; + +/** + * @author Marek Posolda + */ +public class ClassLoaderUtil { + + /** + * Detect if BC FIPS jars are present in the given directory. Return classloader with appropriate JARS based on that + */ + public static ClassLoader resolveClassLoader(String libDir) { + File[] jarsInDir = new File(libDir).listFiles(file -> file.getName().endsWith(".jar")); + + // Detect if BC FIPS jars are present in the "client/lib" directory + boolean bcFipsJarPresent = Stream.of(jarsInDir).anyMatch(file -> file.getName().startsWith("bc-fips")); + String[] validJarPrefixes = bcFipsJarPresent ? new String[] {"keycloak-crypto-fips1402", "bc-fips", "bctls-fips"} : new String[] {"keycloak-crypto-default", "bcprov-jdk15on"}; + URL[] usedJars = Stream.of(jarsInDir) + .filter(file -> { + for (String prefix : validJarPrefixes) { + if (file.getName().startsWith(prefix + "-")) return true; + } + return false; + }) + .map(file -> { + try { + return file.toURI().toURL(); + } catch (MalformedURLException ex) { + throw new IllegalStateException("Error when converting file into URL. Please check the files in the directory " + jarsInDir, ex); + } + }).toArray(URL[]::new); + + return new URLClassLoader(usedJars, ClassLoaderUtil.class.getClassLoader()); + } + +} diff --git a/testsuite/integration-arquillian/servers/auth-server/common/fips/kc.java.security b/testsuite/integration-arquillian/servers/auth-server/common/fips/kc.java.security index 9267176d29..3828ad81c5 100644 --- a/testsuite/integration-arquillian/servers/auth-server/common/fips/kc.java.security +++ b/testsuite/integration-arquillian/servers/auth-server/common/fips/kc.java.security @@ -18,6 +18,9 @@ security.provider.3= fips.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider fips.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS fips.provider.3= +#fips.provider.3=SunJGSS +#fips.provider.4=XMLDSig +#fips.provider.5= # Commented this provider for now (and also other providers) as it uses lots of non-FIPS services. # See https://access.redhat.com/documentation/en-us/openjdk/11/html-single/configuring_openjdk_11_on_rhel_with_fips/index#ref_openjdk-default-fips-configuration_openjdk diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index b761948438..041692949c 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -356,6 +356,17 @@ bc-fips,bctls-fips,bcpkix-fips + + copy-bcfips-deps-client + generate-resources + + copy-dependencies + + + ${auth.server.home}/bin/client/lib + bc-fips,bctls-fips + + diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 4bc211e251..7a623bb304 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -865,6 +865,40 @@ + + + auth-server-fips140-2 + + + + + maven-resources-plugin + + + copy-bcfips-deps-client-tools + process-test-resources + + copy-resources + + + ${containers.home}/keycloak-client-tools/bin/client/lib + + + ${containers.home}/auth-server-quarkus/bin/client/lib + + bc-fips-* + bctls-fips-* + + + + + + + + + + + diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java index 572002beb7..6b55c9b756 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java @@ -40,6 +40,7 @@ import org.jboss.arquillian.test.spi.event.suite.BeforeClass; import org.jboss.arquillian.test.spi.event.suite.BeforeSuite; import org.jboss.logging.Logger; import org.keycloak.admin.client.Keycloak; +import org.keycloak.common.crypto.FipsMode; import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.error.KeycloakErrorHandler; @@ -128,6 +129,10 @@ public class AuthServerTestEnricher { public static final String AUTH_SERVER_HOME_PROPERTY = "auth.server.home"; + public static final String AUTH_SERVER_FIPS_MODE_PROPERTY = "auth.server.fips.mode"; + + public static final FipsMode AUTH_SERVER_FIPS_MODE = FipsMode.valueOf(System.getProperty(AUTH_SERVER_FIPS_MODE_PROPERTY, FipsMode.disabled.toString())); + public static final String CACHE_SERVER_LIFECYCLE_SKIP_PROPERTY = "cache.server.lifecycle.skip"; public static final boolean CACHE_SERVER_LIFECYCLE_SKIP = Boolean.parseBoolean(System.getProperty(CACHE_SERVER_LIFECYCLE_SKIP_PROPERTY, "false")); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/AbstractCliTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/AbstractCliTest.java index 73fedaf944..effcbd73e4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/AbstractCliTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/AbstractCliTest.java @@ -1,8 +1,10 @@ package org.keycloak.testsuite.cli; import org.junit.Assert; +import org.keycloak.common.crypto.FipsMode; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import org.keycloak.testsuite.cli.exec.AbstractExec; import java.io.File; @@ -48,7 +50,9 @@ public abstract class AbstractCliTest extends AbstractKeycloakTest { throw new AssertionError("STDOUT: " + exe.stdoutString(), e); } } - if (stdErrLineCount != -1) { + // There is additional logging in case that BC FIPS libraries are used, so the count of logged lines don't match with the case with plain BC used + // Hence we test count of lines just with FIPS disabled + if (stdErrLineCount != -1 && isFipsDisabled()) { try { assertLineCount("stderr output", exe.stderrLines(), stdErrLineCount); } catch (Throwable e) { @@ -70,4 +74,8 @@ public abstract class AbstractCliTest extends AbstractKeycloakTest { Assert.assertTrue(label + " has " + lines.size() + " lines (expected: " + count + ")", lines.size() == count); } + private boolean isFipsDisabled() { + return AuthServerTestEnricher.AUTH_SERVER_FIPS_MODE == FipsMode.disabled; + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java index 930643c211..cdcbd53c14 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java @@ -254,7 +254,7 @@ public abstract class AbstractAdmCliTest extends AbstractCliTest { " --realm test " + credentials + " " + extraOptions + " -s clientId=test-client -o"); Assert.assertEquals("exitCode == 0", 0, exe.exitCode()); - Assert.assertEquals("login message", loginMessage, exe.stderrLines().get(0)); + Assert.assertTrue("login message expected. But the messages are: " + exe.stderrLines(), exe.stderrLines().stream().anyMatch(message -> message.equals(loginMessage))); ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class); Assert.assertEquals("clientId", "test-client", client.getClientId()); @@ -309,7 +309,7 @@ public abstract class AbstractAdmCliTest extends AbstractCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2 - linecountOffset); String resourceUri = serverUrl + "/admin/realms/test/clients/" + client.getId(); - Assert.assertEquals("error message", "Resource not found for url: " + resourceUri, exe.stderrLines().get(1 - linecountOffset)); + Assert.assertEquals("error message", "Resource not found for url: " + resourceUri, exe.stderrLines().get(exe.stderrLines().size() - 1)); lastModified2 = configFile.exists() ? configFile.lastModified() : 0; Assert.assertEquals("config file not modified", lastModified, lastModified2); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java index 40e7b7b3d3..983e6a094b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java @@ -145,7 +145,7 @@ public class KcAdmCreateTest extends AbstractAdmCliTest { exe = execute("create clients --config '" + configFile.getName() + "' -s clientId=my_client4"); assertExitCodeAndStreamSizes(exe, 0, 0, 1); - Assert.assertTrue("only id returned", exe.stderrLines().get(0).startsWith("Created new client with id '")); + Assert.assertTrue("only id returned", exe.stderrLines().get(exe.stderrLines().size() - 1).startsWith("Created new client with id '")); } } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java index fd63f1bcee..e267d3b1b1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java @@ -39,7 +39,7 @@ public class KcAdmSessionTest extends AbstractAdmCliTest { KcAdmExec exe = execute("create realms --config '" + configFile.getName() + "' -s realm=demorealm -s enabled=true"); assertExitCodeAndStreamSizes(exe, 0, 0, 1); - Assert.assertTrue(exe.stderrLines().get(0).startsWith("Created ")); + Assert.assertTrue(exe.stderrLines().get(exe.stderrLines().size() - 1).startsWith("Created ")); // create user exe = execute("create users --config '" + configFile.getName() + "' -r demorealm -s username=testuser -s enabled=true -i"); @@ -95,7 +95,7 @@ public class KcAdmSessionTest extends AbstractAdmCliTest { exe = execute("create clients/" + idOfClient + "/roles --config '" + configFile.getName() + "' -s name=clientrole -s 'description=Test client role'"); assertExitCodeAndStreamSizes(exe, 0, 0, 1); - Assert.assertTrue(exe.stderrLines().get(0).startsWith("Created ")); + Assert.assertTrue(exe.stderrLines().get(exe.stderrLines().size() - 1).startsWith("Created ")); // make sure client role has been created exe = execute("get-roles --config '" + configFile.getName() + "' --cclientid testclient"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java index 988021e6b2..f202a3b0f3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java @@ -474,7 +474,7 @@ public class KcAdmTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong user password @@ -483,7 +483,7 @@ public class KcAdmTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong client secret @@ -492,7 +492,7 @@ public class KcAdmTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Invalid client or Invalid client credentials [unauthorized_client]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Invalid client or Invalid client credentials [unauthorized_client]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try whole CRUD @@ -516,7 +516,7 @@ public class KcAdmTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong user password @@ -526,7 +526,7 @@ public class KcAdmTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong storepass @@ -536,7 +536,7 @@ public class KcAdmTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Failed to load private key: Keystore was tampered with, or password was incorrect", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Failed to load private key: Keystore was tampered with, or password was incorrect", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try whole CRUD diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java index 32cdb6a083..03b92ce976 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java @@ -487,7 +487,7 @@ public abstract class AbstractRegCliTest extends AbstractCliTest { exe = execute("delete test-client --no-config --server " + serverUrl + " --realm test " + credentials + " " + extraOptions); assertExitCodeAndStreamSizes(exe, 1, 0, 2); - Assert.assertEquals("error message", "Client not found [invalid_request]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Client not found [invalid_request]", exe.stderrLines().get(exe.stderrLines().size() - 1)); lastModified2 = configFile.exists() ? configFile.lastModified() : 0; Assert.assertEquals("config file not modified", lastModified, lastModified2); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java index bc77795f28..5e1b940f96 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java @@ -13,6 +13,7 @@ import org.keycloak.client.registration.cli.config.ConfigData; import org.keycloak.client.registration.cli.config.FileConfigHandler; import org.keycloak.common.Profile; import org.keycloak.common.constants.ServiceAccountConstants; +import org.keycloak.common.crypto.FipsMode; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -20,6 +21,7 @@ import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import org.keycloak.testsuite.cli.KcRegExec; import org.keycloak.testsuite.util.TempFileResource; import org.keycloak.util.JsonSerialization; @@ -171,7 +173,7 @@ public class KcRegCreateTest extends AbstractRegCliTest { exe = execute("create --insecure --config '" + configFile.getName() + "' -s clientId=my_client4"); assertExitCodeAndStreamSizes(exe, 0, 0, 3); - Assert.assertEquals("only clientId returned", "Registered new client with client_id 'my_client4'", exe.stderrLines().get(2)); + Assert.assertEquals("only clientId returned", "Registered new client with client_id 'my_client4'", exe.stderrLines().get(exe.stderrLines().size() - 1)); @@ -210,23 +212,25 @@ public class KcRegCreateTest extends AbstractRegCliTest { Assert.assertEquals("Error message", "Attribute 'redirect_uris' not supported on document type 'default'", exe.stderrLines().get(0)); } + // TODO: SAML is not tested with FIPS enabled as it does not work. This needs to be revisited when SAML works with FIPS + if (AuthServerTestEnricher.AUTH_SERVER_FIPS_MODE == FipsMode.disabled) { - // test create saml formated xml - format autodetection - File samlSpMetaFile = new File(System.getProperty("user.dir") + "/src/test/resources/cli/kcreg/saml-sp-metadata.xml"); - Assert.assertTrue("saml-sp-metadata.xml exists", samlSpMetaFile.isFile()); + // test create saml formated xml - format autodetection + File samlSpMetaFile = new File(System.getProperty("user.dir") + "/src/test/resources/cli/kcreg/saml-sp-metadata.xml"); + Assert.assertTrue("saml-sp-metadata.xml exists", samlSpMetaFile.isFile()); - exe = execute("create --insecure --config '" + configFile.getName() + "' -o -f - < '" + samlSpMetaFile.getAbsolutePath() + "'"); + exe = execute("create --insecure --config '" + configFile.getName() + "' -o -f - < '" + samlSpMetaFile.getAbsolutePath() + "'"); - assertExitCodeAndStdErrSize(exe, 0, 2); - - ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class); - Assert.assertNotNull("id", client.getId()); - Assert.assertEquals("clientId", "http://localhost:8080/sales-post-enc/", client.getClientId()); - Assert.assertEquals("redirectUris", Arrays.asList("http://localhost:8081/sales-post-enc/saml"), client.getRedirectUris()); - Assert.assertEquals("attributes.saml_name_id_format", "username", client.getAttributes().get("saml_name_id_format")); - Assert.assertEquals("attributes.saml_assertion_consumer_url_post", "http://localhost:8081/sales-post-enc/saml", client.getAttributes().get("saml_assertion_consumer_url_post")); - Assert.assertEquals("attributes.saml.signature.algorithm", "RSA_SHA256", client.getAttributes().get("saml.signature.algorithm")); + assertExitCodeAndStdErrSize(exe, 0, 2); + ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class); + Assert.assertNotNull("id", client.getId()); + Assert.assertEquals("clientId", "http://localhost:8080/sales-post-enc/", client.getClientId()); + Assert.assertEquals("redirectUris", Arrays.asList("http://localhost:8081/sales-post-enc/saml"), client.getRedirectUris()); + Assert.assertEquals("attributes.saml_name_id_format", "username", client.getAttributes().get("saml_name_id_format")); + Assert.assertEquals("attributes.saml_assertion_consumer_url_post", "http://localhost:8081/sales-post-enc/saml", client.getAttributes().get("saml_assertion_consumer_url_post")); + Assert.assertEquals("attributes.saml.signature.algorithm", "RSA_SHA256", client.getAttributes().get("saml.signature.algorithm")); + } // delete initial token exe = execute("config initial-token --config '" + configFile.getName() + "' --insecure --server " + serverUrl + " --realm " + realm + " --delete"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java index 5160a7c298..83994d8f31 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java @@ -475,7 +475,7 @@ public class KcRegTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong user password @@ -484,7 +484,7 @@ public class KcRegTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong client secret @@ -493,7 +493,7 @@ public class KcRegTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Invalid client or Invalid client credentials [unauthorized_client]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Invalid client or Invalid client credentials [unauthorized_client]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try whole CRUD @@ -517,7 +517,7 @@ public class KcRegTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Client not allowed for direct access grants [unauthorized_client]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong user password @@ -527,7 +527,7 @@ public class KcRegTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Invalid user credentials [invalid_grant]", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try wrong storepass @@ -537,7 +537,7 @@ public class KcRegTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 1, 0, 2); Assert.assertEquals("login message", "Logging into " + serverUrl + " as user user1 of realm test", exe.stderrLines().get(0)); - Assert.assertEquals("error message", "Failed to load private key: Keystore was tampered with, or password was incorrect", exe.stderrLines().get(1)); + Assert.assertEquals("error message", "Failed to load private key: Keystore was tampered with, or password was incorrect", exe.stderrLines().get(exe.stderrLines().size() - 1)); // try whole CRUD diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java index 419d75469a..dd5bca3d54 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java @@ -100,7 +100,7 @@ public class KcRegUpdateTest extends AbstractRegCliTest { exe = execute("update my_client --config '" + configFile.getName() + "' -o -s enabled=true -e oidc"); assertExitCodeAndStreamSizes(exe, 1, 0, 1); - Assert.assertEquals("error message", "Failed to set attribute 'enabled' on document type 'oidc'", exe.stderrLines().get(0)); + Assert.assertEquals("error message", "Failed to set attribute 'enabled' on document type 'oidc'", exe.stderrLines().get(exe.stderrLines().size() - 1)); diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 55604ff1c0..9ae41d1243 100644 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -1864,6 +1864,12 @@ org.keycloak keycloak-client-cli-dist + + + org.keycloak + keycloak-crypto-fips1402 + + zip