diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf71fef017f..b1a8b89b26f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,50 @@ jobs: path: reports-unit-tests.zip if-no-files-found: ignore + crypto-tests: + name: Crypto Tests + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ env.DEFAULT_JDK_VERSION }} + cache: 'maven' + - name: Update maven settings + run: mkdir -p ~/.m2 ; cp .github/settings.xml ~/.m2/ + - name: Cleanup org.keycloak artifacts + run: rm -rf ~/.m2/repository/org/keycloak >/dev/null || true + - name: Download built keycloak + id: download-keycloak + uses: actions/download-artifact@v3 + with: + path: ~/.m2/repository/org/keycloak/ + name: keycloak-artifacts.zip + - name: Run crypto tests (BCFIPS non-approved mode) + run: | + if ! ./mvnw install -nsu -B -f crypto/pom.xml -Dcom.redhat.fips=true; then + find . -path 'crypto/target/surefire-reports/*.xml' | zip -q reports-crypto-tests.zip -@ + exit 1 + fi + + - name: Run crypto tests (BCFIPS approved mode) + run: | + if ! ./mvnw install -nsu -B -f crypto/pom.xml -Dcom.redhat.fips=true -Dorg.bouncycastle.fips.approved_only=true; then + find . -path 'crypto/target/surefire-reports/*.xml' | zip -q reports-crypto-tests.zip -@ + exit 1 + fi + + - name: Crypto test reports + uses: actions/upload-artifact@v3 + if: failure() + with: + name: reports-crypto-tests + retention-days: 14 + path: reports-crypto-tests.zip + if-no-files-found: ignore + model-tests: name: Model Tests runs-on: ubuntu-latest diff --git a/core/src/test/java/org/keycloak/KeyPairVerifierTest.java b/core/src/test/java/org/keycloak/KeyPairVerifierTest.java index 4108b632f94..adc35d5f541 100644 --- a/core/src/test/java/org/keycloak/KeyPairVerifierTest.java +++ b/core/src/test/java/org/keycloak/KeyPairVerifierTest.java @@ -57,12 +57,17 @@ public abstract class KeyPairVerifierTest { + "PEuQrsfWRXm9/dTEavbfNkv5E53zWXjWyf93ezkVhBX0YoXmf6UO7PAlvsrjno3T\n" + "uwIDAQAB\n" + "-----END PUBLIC KEY-----"; @Test - public void verifyWithPrivateKeysInTraditionalRSAFormat() throws Exception { - verifyImpl(this.privateKey1, this.privateKey2048); + public void verifyWith1024PrivateKeyInTraditionalRSAFormat() throws Exception { + verifyImplRsa1024Key(this.privateKey1); } @Test - public void verifyWithPrivateKeysInPKCS8Format() throws Exception { + public void verifyWith2048PrivateKeyInTraditionalRSAFormat() throws Exception { + verifyImplRsa2048Key(this.privateKey2048); + } + + @Test + public void verifyWith1024PrivateKeyInPKCS8Format() throws Exception { String privateKey1 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtWsK5O0CtuBpnM" + "vWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfd" + "Q2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5" + @@ -78,6 +83,11 @@ public abstract class KeyPairVerifierTest { "7KQ6+vVqJlQwVPvYdTSOeZB7YVV6S4b4slS3ZObsa0yNMWgal/QnCtW5k3f185gC" + "Wj6dOLGB5btfxg=="; + verifyImplRsa1024Key(privateKey1); + } + + @Test + public void verifyWith2048PrivateKeyInPKCS8Format() throws Exception { String privateKey2048 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDhXcyk6e4qx0Ft" + "HVTM2Mr2jmZ4QxDizlWnKSG/UOmpOKUo6IQftVD9e2M3HDTKOcUGKUKekrrI32YM" + "QdsETNpGO12uBWGQh6OJpcUE/kwGFRDmX27wTchkLcTynAONUXRn27RHiUZ5SDaT" + @@ -105,21 +115,22 @@ public abstract class KeyPairVerifierTest { "Zer7cRS4Vsn4uNvxhYGB4+NIcOhL/r7/7OoHVvm5Cn+NgVthCXnRQ9E9MX66XV5C" + "jLsXjc2CPf/lwNFqsVl7dlPNmg=="; - verifyImpl(privateKey1, privateKey2048); + verifyImplRsa2048Key(privateKey2048); } - protected void verifyImpl(String privateKey1, String privateKey2048) throws Exception { - KeyPairVerifier.verify(privateKey1, publicKey1); - KeyPairVerifier.verify(privateKey2048, publicKey2048); - + protected void verifyImplRsa1024Key(String rsaPrivateKey1024) throws Exception { + KeyPairVerifier.verify(rsaPrivateKey1024, publicKey1); try { - KeyPairVerifier.verify(privateKey1, publicKey2048); + KeyPairVerifier.verify(rsaPrivateKey1024, publicKey2048); Assert.fail("Expected VerificationException"); } catch (VerificationException e) { } + } + protected void verifyImplRsa2048Key(String rsaPrivateKey2048) throws Exception { + KeyPairVerifier.verify(rsaPrivateKey2048, publicKey2048); try { - KeyPairVerifier.verify(privateKey2048, publicKey1); + KeyPairVerifier.verify(rsaPrivateKey2048, publicKey1); Assert.fail("Expected VerificationException"); } catch (VerificationException e) { } diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index 30e91ca31ea..71ed91fbc9a 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -85,4 +85,19 @@ + + + + + maven-surefire-plugin + + + ${basedir}/target/test-classes/kc.java.security + + + + + + + \ No newline at end of file diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java index e80a51fe57e..bd64a9374de 100644 --- a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402KeyPairVerifierTest.java @@ -1,7 +1,11 @@ package org.keycloak.crypto.fips.test; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.fips.FipsUnapprovedOperationError; +import org.junit.Assert; import org.junit.Before; import org.junit.Assume; +import org.junit.Test; import org.keycloak.KeyPairVerifierTest; import org.keycloak.common.util.Environment; @@ -17,4 +21,42 @@ public class FIPS1402KeyPairVerifierTest extends KeyPairVerifierTest { // Run this test just if java is in FIPS mode Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); } + + @Test + public void verifyWith1024PrivateKeyInTraditionalRSAFormat() throws Exception { + // Signature generation with RSA 1024 key works just in non-approved mode + Assume.assumeFalse(CryptoServicesRegistrar.isInApprovedOnlyMode()); + super.verifyWith1024PrivateKeyInTraditionalRSAFormat(); + } + + @Test + public void verifyWith1024PrivateKeyInPKCS8Format() throws Exception { + // Signature generation with RSA 1024 key works just in non-approved mode + Assume.assumeFalse(CryptoServicesRegistrar.isInApprovedOnlyMode()); + super.verifyWith1024PrivateKeyInPKCS8Format(); + } + + @Test + public void verifyWith1024PrivateKeyInTraditionalRSAFormatShouldFail() throws Exception { + // Signature generation with RSA 1024 key works just in non-approved mode + Assume.assumeTrue(CryptoServicesRegistrar.isInApprovedOnlyMode()); + try { + super.verifyWith1024PrivateKeyInTraditionalRSAFormat(); + Assert.fail("Should not successfully generate signature with RSA 1024 key in BC approved mode"); + } catch (FipsUnapprovedOperationError fipsError) { + // expected + } + } + + @Test + public void verifyWith1024PrivateKeyInPKCS8FormatShouldFail() throws Exception { + // Signature generation with RSA 1024 key works just in non-approved mode + Assume.assumeTrue(CryptoServicesRegistrar.isInApprovedOnlyMode()); + try { + super.verifyWith1024PrivateKeyInPKCS8Format(); + Assert.fail("Should not successfully generate signature with RSA 1024 key in BC approved mode"); + } catch (FipsUnapprovedOperationError fipsError) { + // expected + } + } } diff --git a/crypto/fips1402/src/test/resources/kc.java.security b/crypto/fips1402/src/test/resources/kc.java.security new file mode 100644 index 00000000000..2e5f292caf2 --- /dev/null +++ b/crypto/fips1402/src/test/resources/kc.java.security @@ -0,0 +1,29 @@ +# Configuration file just with the security properties, which are supposed to be overriden. The properties, which are not mentioned in this file, +# are inherited from the default java.security file bundled within the distribution. +# +# NOTE: Each property is specified 2 times. This is so the same file can be used on both FIPS based RHEL host (which uses "fips" prefixed properties by default) +# and the non-fips based (EG. when running the tests on GH actions) + +# +# List of providers and their preference orders (see above). Used on the host without FIPS (EG. when running the tests on GH actions) +# NOTE: List is empty for now, so we test just with BCFIPS provider, which is registered programatically +# +security.provider.1= + +# +# Security providers used when global crypto-policies are set to FIPS. +# NOTE: List is empty for now, so we test just with BCFIPS provider, which is registered programatically +# +fips.provider.1= +#fips.provider.1=SunPKCS11 ${java.home}/conf/security/nss.fips.cfg +#fips.provider.2=SunEC +#fips.provider.3=com.sun.net.ssl.internal.ssl.Provider SunPKCS11-NSS-FIPS + +# Commented this provider for now 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 +# fips.provider.2=SUN + +# +# Default keystore type. +# +keystore.type=PKCS11 +fips.keystore.type=PKCS11 \ No newline at end of file