Initial integration tests with BCFIPS distribution (#14895)
Closes #14886
This commit is contained in:
parent
19ee00ff54
commit
0756ef9a75
28 changed files with 743 additions and 96 deletions
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
|
@ -274,6 +274,77 @@ jobs:
|
||||||
path: reports-${{ matrix.server }}-base-tests-${{ matrix.tests }}.zip
|
path: reports-${{ matrix.server }}-base-tests-${{ matrix.tests }}.zip
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
test-fips:
|
||||||
|
name: Base testsuite (fips)
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
server: ['bcfips-nonapproved-pkcs12']
|
||||||
|
tests: ['group1']
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Cache Maven packages
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: cache-2-${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: cache-1-${{ runner.os }}-m2
|
||||||
|
|
||||||
|
- name: Download built keycloak
|
||||||
|
id: download-keycloak
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository/org/keycloak/
|
||||||
|
name: keycloak-artifacts.zip
|
||||||
|
|
||||||
|
# - name: List M2 repo
|
||||||
|
# run: |
|
||||||
|
# find ~ -name *dist*.zip
|
||||||
|
# ls -lR ~/.m2/repository
|
||||||
|
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: ${{ env.DEFAULT_JDK_VERSION }}
|
||||||
|
- name: Update maven settings
|
||||||
|
run: mkdir -p ~/.m2 ; cp .github/settings.xml ~/.m2/
|
||||||
|
- name: Prepare quarkus distribution with BCFIPS
|
||||||
|
run: ./mvnw clean install -nsu -B -e -f testsuite/integration-arquillian/servers/auth-server/quarkus -Pauth-server-quarkus,auth-server-fips140-2
|
||||||
|
- name: Run base tests
|
||||||
|
run: |
|
||||||
|
declare -A PARAMS TESTGROUP
|
||||||
|
PARAMS["bcfips-nonapproved-pkcs12"]="-Pauth-server-quarkus,auth-server-fips140-2"
|
||||||
|
TESTGROUP["group1"]="-Dtest=org.keycloak.testsuite.forms.**" # Tests in the package "forms"
|
||||||
|
|
||||||
|
./mvnw clean install -nsu -B ${PARAMS["${{ matrix.server }}"]} ${TESTGROUP["${{ matrix.tests }}"]} -f testsuite/integration-arquillian/tests/base/pom.xml | misc/log/trimmer.sh
|
||||||
|
|
||||||
|
TEST_RESULT=${PIPESTATUS[0]}
|
||||||
|
find . -path '*/target/surefire-reports/*.xml' | zip -q reports-${{ matrix.server }}-base-tests-${{ matrix.tests }}.zip -@
|
||||||
|
exit $TEST_RESULT
|
||||||
|
|
||||||
|
- name: Analyze Test and/or Coverage Results
|
||||||
|
uses: runforesight/foresight-test-kit-action@v1
|
||||||
|
if: always() && github.repository == 'keycloak/keycloak'
|
||||||
|
with:
|
||||||
|
api_key: ${{ secrets.FORESIGHT_API_KEY }}
|
||||||
|
test_format: JUNIT
|
||||||
|
test_framework: JUNIT
|
||||||
|
test_path: 'testsuite/integration-arquillian/tests/base/target/surefire-reports/*.xml'
|
||||||
|
|
||||||
|
- name: Base test reports
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: reports-${{ matrix.server }}-base-tests-${{ matrix.tests }}
|
||||||
|
retention-days: 14
|
||||||
|
path: reports-${{ matrix.server }}-base-tests-${{ matrix.tests }}.zip
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
test-posgres:
|
test-posgres:
|
||||||
name: Base testsuite (postgres)
|
name: Base testsuite (postgres)
|
||||||
needs: build
|
needs: build
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package org.keycloak.common.crypto;
|
package org.keycloak.common.crypto;
|
||||||
|
|
||||||
|
import java.security.KeyStore;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
@ -33,6 +35,7 @@ public class CryptoIntegration {
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.tracef(dumpJavaSecurityProviders());
|
logger.tracef(dumpJavaSecurityProviders());
|
||||||
|
logger.tracef(dumpSecurityProperties());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +71,19 @@ public class CryptoIntegration {
|
||||||
return builder.append("]").toString();
|
return builder.append("]").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String dumpSecurityProperties() {
|
||||||
|
StringBuilder builder = new StringBuilder("Security properties: [ \n")
|
||||||
|
.append(" Java security properties file: " + System.getProperty("java.security.properties") + "\n")
|
||||||
|
.append(" Default keystore type: " + KeyStore.getDefaultType() + "\n")
|
||||||
|
.append(" keystore.type.compat: " + Security.getProperty("keystore.type.compat") + "\n");
|
||||||
|
Stream.of("javax.net.ssl.trustStoreType", "javax.net.ssl.trustStore", "javax.net.ssl.trustStoreProvider",
|
||||||
|
"javax.net.ssl.keyStoreType", "javax.net.ssl.keyStore", "javax.net.ssl.keyStoreProvider")
|
||||||
|
.forEach(propertyName -> builder.append(" " + propertyName + ": " + System.getProperty(propertyName) + "\n"));
|
||||||
|
return builder.append("]").toString();
|
||||||
|
}
|
||||||
|
|
||||||
public static void setProvider(CryptoProvider provider) {
|
public static void setProvider(CryptoProvider provider) {
|
||||||
|
logger.debugf("Using the crypto provider: %s", provider.getClass().getName());
|
||||||
cryptoProvider = provider;
|
cryptoProvider = provider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.keycloak.common.crypto;
|
||||||
|
|
||||||
|
public enum FipsMode {
|
||||||
|
enabled("org.keycloak.crypto.fips.FIPS1402Provider"),
|
||||||
|
strict("org.keycloak.crypto.fips.Fips1402StrictCryptoProvider"),
|
||||||
|
disabled("org.keycloak.crypto.def.DefaultCryptoProvider");
|
||||||
|
|
||||||
|
private String providerClassName;
|
||||||
|
|
||||||
|
FipsMode(String providerClassName) {
|
||||||
|
this.providerClassName = providerClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFipsEnabled() {
|
||||||
|
return this.equals(enabled) || this.equals(strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProviderClassName() {
|
||||||
|
return providerClassName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.common.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.common.crypto.CryptoIntegration;
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
|
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
|
@ -29,8 +28,6 @@ import java.security.Security;
|
||||||
*/
|
*/
|
||||||
public class BouncyIntegration {
|
public class BouncyIntegration {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(BouncyIntegration.class);
|
|
||||||
|
|
||||||
public static final String PROVIDER = loadProvider();
|
public static final String PROVIDER = loadProvider();
|
||||||
|
|
||||||
private static String loadProvider() {
|
private static String loadProvider() {
|
||||||
|
@ -39,12 +36,6 @@ public class BouncyIntegration {
|
||||||
return Security.getProviders()[0].getName();
|
return Security.getProviders()[0].getName();
|
||||||
// throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider");
|
// throw new RuntimeException("Failed to load required security provider: BouncyCastleProvider or BouncyCastleFipsProvider");
|
||||||
}
|
}
|
||||||
if (Security.getProvider(provider.getName()) == null) {
|
|
||||||
Security.addProvider(provider);
|
|
||||||
log.debugv("Loaded {0} security provider", provider.getClass().getName());
|
|
||||||
} else {
|
|
||||||
log.debugv("Security provider {0} already loaded", provider.getClass().getName());
|
|
||||||
}
|
|
||||||
return provider.getName();
|
return provider.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.bouncycastle.jce.ECNamedCurveTable;
|
||||||
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
|
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
|
||||||
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.crypto.CryptoProvider;
|
import org.keycloak.common.crypto.CryptoProvider;
|
||||||
import org.keycloak.common.crypto.CryptoConstants;
|
import org.keycloak.common.crypto.CryptoConstants;
|
||||||
import org.keycloak.common.crypto.ECDSACryptoProvider;
|
import org.keycloak.common.crypto.ECDSACryptoProvider;
|
||||||
|
@ -42,6 +43,8 @@ import org.keycloak.crypto.JavaAlgorithm;
|
||||||
*/
|
*/
|
||||||
public class DefaultCryptoProvider implements CryptoProvider {
|
public class DefaultCryptoProvider implements CryptoProvider {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(DefaultCryptoProvider.class);
|
||||||
|
|
||||||
private final Provider bcProvider;
|
private final Provider bcProvider;
|
||||||
|
|
||||||
private Map<String, Object> providers = new ConcurrentHashMap<>();
|
private Map<String, Object> providers = new ConcurrentHashMap<>();
|
||||||
|
@ -55,6 +58,13 @@ public class DefaultCryptoProvider implements CryptoProvider {
|
||||||
providers.put(CryptoConstants.RSA1_5, new DefaultRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/PKCS1Padding"));
|
providers.put(CryptoConstants.RSA1_5, new DefaultRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/PKCS1Padding"));
|
||||||
providers.put(CryptoConstants.RSA_OAEP, new DefaultRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"));
|
providers.put(CryptoConstants.RSA_OAEP, new DefaultRsaKeyEncryptionJWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"));
|
||||||
providers.put(CryptoConstants.RSA_OAEP_256, new DefaultRsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"));
|
providers.put(CryptoConstants.RSA_OAEP_256, new DefaultRsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"));
|
||||||
|
|
||||||
|
if (existingBc == null) {
|
||||||
|
Security.addProvider(this.bcProvider);
|
||||||
|
log.debugv("Loaded {0} security provider", this.bcProvider.getClass().getName());
|
||||||
|
} else {
|
||||||
|
log.debugv("Security provider {0} already loaded", this.bcProvider.getClass().getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.security.cert.CertStore;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.CollectionCertStoreParameters;
|
import java.security.cert.CollectionCertStoreParameters;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -33,7 +34,9 @@ import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
import org.bouncycastle.crypto.fips.FipsRSA;
|
import org.bouncycastle.crypto.fips.FipsRSA;
|
||||||
import org.bouncycastle.crypto.fips.FipsSHS;
|
import org.bouncycastle.crypto.fips.FipsSHS;
|
||||||
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
|
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
|
||||||
|
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
|
||||||
import org.bouncycastle.math.ec.ECCurve;
|
import org.bouncycastle.math.ec.ECCurve;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.crypto.CryptoProvider;
|
import org.keycloak.common.crypto.CryptoProvider;
|
||||||
import org.keycloak.common.crypto.ECDSACryptoProvider;
|
import org.keycloak.common.crypto.ECDSACryptoProvider;
|
||||||
import org.keycloak.common.crypto.CryptoConstants;
|
import org.keycloak.common.crypto.CryptoConstants;
|
||||||
|
@ -52,6 +55,8 @@ import org.keycloak.crypto.JavaAlgorithm;
|
||||||
*/
|
*/
|
||||||
public class FIPS1402Provider implements CryptoProvider {
|
public class FIPS1402Provider implements CryptoProvider {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(FIPS1402Provider.class);
|
||||||
|
|
||||||
private final BouncyCastleFipsProvider bcFipsProvider;
|
private final BouncyCastleFipsProvider bcFipsProvider;
|
||||||
private final Map<String, Object> providers = new ConcurrentHashMap<>();
|
private final Map<String, Object> providers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@ -66,6 +71,14 @@ public class FIPS1402Provider implements CryptoProvider {
|
||||||
providers.put(CryptoConstants.RSA_OAEP_256, new FIPSRsaKeyEncryptionJWEAlgorithmProvider(FipsRSA.WRAP_OAEP.withDigest(FipsSHS.Algorithm.SHA256)));
|
providers.put(CryptoConstants.RSA_OAEP_256, new FIPSRsaKeyEncryptionJWEAlgorithmProvider(FipsRSA.WRAP_OAEP.withDigest(FipsSHS.Algorithm.SHA256)));
|
||||||
|
|
||||||
Security.insertProviderAt(new KeycloakFipsSecurityProvider(bcFipsProvider), 1);
|
Security.insertProviderAt(new KeycloakFipsSecurityProvider(bcFipsProvider), 1);
|
||||||
|
if (existingBcFipsProvider == null) {
|
||||||
|
Security.insertProviderAt(this.bcFipsProvider, 2);
|
||||||
|
Provider bcJsseProvider = new BouncyCastleJsseProvider("fips:BCFIPS");
|
||||||
|
Security.insertProviderAt(bcJsseProvider, 3);
|
||||||
|
log.debugf("Inserted security providers: %s", Arrays.asList(this.bcFipsProvider.getName(),bcJsseProvider.getName()));
|
||||||
|
} else {
|
||||||
|
log.debugf("Security provider %s already loaded", existingBcFipsProvider.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
package org.keycloak.crypto.fips.test;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLSessionContext;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.CryptoServicesRegistrar;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Assume;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
|
import org.keycloak.common.util.Environment;
|
||||||
|
import org.keycloak.rule.CryptoInitRule;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class FIPS1402SslTest {
|
||||||
|
|
||||||
|
protected static final Logger logger = Logger.getLogger(FIPS1402SslTest.class);
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static CryptoInitRule cryptoInitRule = new CryptoInitRule();
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void dumpSecurityProviders() {
|
||||||
|
logger.info(CryptoIntegration.dumpJavaSecurityProviders());
|
||||||
|
logger.info(CryptoIntegration.dumpSecurityProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
// 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 testPkcs12KeyStoreWithPKIXKeyMgrFactory() throws Exception {
|
||||||
|
// PKCS12 keystore works just in non-approved mode
|
||||||
|
Assume.assumeFalse(CryptoServicesRegistrar.isInApprovedOnlyMode());
|
||||||
|
String type = "PKCS12";
|
||||||
|
String password = "passwordpassword";
|
||||||
|
|
||||||
|
KeyStore keystore = loadKeystore(type, password);
|
||||||
|
String keyMgrDefaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
||||||
|
KeyManagerFactory keyMgrFact = getKeyMgrFactory(password, keystore, keyMgrDefaultAlgorithm);
|
||||||
|
testSSLContext(keyMgrFact);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This works with BCFIPS, but requires addition of security provider "com.sun.net.ssl.internal.ssl.Provider BCFIPS" to Java Security providers
|
||||||
|
@Test
|
||||||
|
@Ignore("Skip for now and keep it just for the reference. We can check if we want to test this path with SunX509 algorithm withadditional security provider")
|
||||||
|
public void testPkcs12KeyStoreWithSunX509KeyMgrFactory() throws Exception {
|
||||||
|
// PKCS12 keystore works just in non-approved mode
|
||||||
|
Assume.assumeFalse(CryptoServicesRegistrar.isInApprovedOnlyMode());
|
||||||
|
String type = "PKCS12";
|
||||||
|
String password = "passwordpassword";
|
||||||
|
|
||||||
|
KeyStore keystore = loadKeystore(type, password);
|
||||||
|
String keyMgrDefaultAlgorithm = "SunX509";
|
||||||
|
KeyManagerFactory keyMgrFact = getKeyMgrFactory(password, keystore, keyMgrDefaultAlgorithm);
|
||||||
|
testSSLContext(keyMgrFact);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBcfksKeyStoreWithPKIXKeyMgrFactory() throws Exception {
|
||||||
|
String type = "BCFKS";
|
||||||
|
String password = "passwordpassword";
|
||||||
|
|
||||||
|
KeyStore keystore = loadKeystore(type, password);
|
||||||
|
String keyMgrDefaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
||||||
|
KeyManagerFactory keyMgrFact = getKeyMgrFactory(password, keystore, keyMgrDefaultAlgorithm);
|
||||||
|
testSSLContext(keyMgrFact);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This works with BCFIPS, but requires addition of security provider "com.sun.net.ssl.internal.ssl.Provider BCFIPS" to Java Security providers
|
||||||
|
@Test
|
||||||
|
@Ignore("Skip for now and keep it just for the reference. We can check if we want to test this path with SunX509 algorithm withadditional security provider")
|
||||||
|
public void testBcfksKeyStoreWithSunX509KeyMgrFactory() throws Exception {
|
||||||
|
String type = "BCFKS";
|
||||||
|
String password = "passwordpassword";
|
||||||
|
|
||||||
|
KeyStore keystore = loadKeystore(type, password);
|
||||||
|
String keyMgrDefaultAlgorithm = "SunX509";
|
||||||
|
KeyManagerFactory keyMgrFact = getKeyMgrFactory(password, keystore, keyMgrDefaultAlgorithm);
|
||||||
|
testSSLContext(keyMgrFact);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyStore loadKeystore(String type, String password) throws Exception {
|
||||||
|
KeyStore keystore = KeyStore.getInstance(type);
|
||||||
|
InputStream in = FIPS1402SslTest.class.getClassLoader().getResourceAsStream("bcfips-keystore." + type.toLowerCase());
|
||||||
|
keystore.load(in, password != null ? password.toCharArray() : null);
|
||||||
|
logger.infof("Keystore loaded successfully. Type: %s, provider: %s", keystore.getProvider().getName());
|
||||||
|
return keystore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyManagerFactory getKeyMgrFactory(String password, KeyStore keystore, String keyMgrAlgorithm) throws Exception {
|
||||||
|
KeyManagerFactory keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgorithm);
|
||||||
|
char[] keyPassword = password.toCharArray();
|
||||||
|
keyMgrFact.init(keystore, keyPassword);
|
||||||
|
logger.infof("KeyManagerFactory loaded for algorithm: %s", keyMgrAlgorithm);
|
||||||
|
return keyMgrFact;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void testSSLContext(KeyManagerFactory keyMgrFact) throws Exception {
|
||||||
|
SSLContext context = SSLContext.getInstance("TLS");
|
||||||
|
context.init(keyMgrFact.getKeyManagers(), null, null);
|
||||||
|
SSLEngine engine = context.createSSLEngine();
|
||||||
|
|
||||||
|
List<String> enabledCipherSuites = Arrays.asList(engine.getEnabledCipherSuites());
|
||||||
|
List<String> supportedProtocols = Arrays.asList(context.getDefaultSSLParameters().getProtocols());
|
||||||
|
List<String> supportedCiphers = Arrays.asList(engine.getSupportedCipherSuites());
|
||||||
|
|
||||||
|
logger.infof("Enabled ciphersuites: %s", enabledCipherSuites.size());
|
||||||
|
logger.infof("Supported protocols: %s", supportedProtocols);
|
||||||
|
logger.infof("Supported ciphers size: %d", supportedCiphers.size());
|
||||||
|
Assert.assertThat(enabledCipherSuites.size(), greaterThan(0));
|
||||||
|
Assert.assertThat(supportedProtocols.size(), greaterThan(0));
|
||||||
|
Assert.assertThat(supportedCiphers.size(), greaterThan(0));
|
||||||
|
|
||||||
|
SSLSessionContext sslServerCtx = context.getServerSessionContext();
|
||||||
|
Assert.assertNotNull(sslServerCtx);
|
||||||
|
}
|
||||||
|
}
|
BIN
crypto/fips1402/src/test/resources/bcfips-keystore.bcfks
Normal file
BIN
crypto/fips1402/src/test/resources/bcfips-keystore.bcfks
Normal file
Binary file not shown.
BIN
crypto/fips1402/src/test/resources/bcfips-keystore.pkcs12
Normal file
BIN
crypto/fips1402/src/test/resources/bcfips-keystore.pkcs12
Normal file
Binary file not shown.
|
@ -1,29 +1,51 @@
|
||||||
# Configuration file just with the security properties, which are supposed to be overriden. The properties, which are not mentioned in this file,
|
# 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.
|
# are inherited from the default java.security file bundled within the Java 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)
|
# 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)
|
# 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)
|
# 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
|
# Uses only BouncyCastle FIPS providers to make sure to use only FIPS compliant cryptography.
|
||||||
#
|
#
|
||||||
security.provider.1=
|
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
|
||||||
|
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
|
||||||
|
security.provider.3=
|
||||||
|
|
||||||
#
|
#
|
||||||
# Security providers used when global crypto-policies are set to FIPS.
|
# Security providers used when global crypto-policies are set to FIPS (Usually it is used when FIPS enabled on system/JVM level)
|
||||||
# NOTE: List is empty for now, so we test just with BCFIPS provider, which is registered programatically
|
|
||||||
#
|
#
|
||||||
fips.provider.1=
|
fips.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
|
||||||
#fips.provider.1=SunPKCS11 ${java.home}/conf/security/nss.fips.cfg
|
fips.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
|
||||||
#fips.provider.2=SunEC
|
fips.provider.3=
|
||||||
#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
|
# 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
|
||||||
# fips.provider.2=SUN
|
# fips.provider.2=SUN
|
||||||
|
|
||||||
#
|
#
|
||||||
# Default keystore type.
|
# Default keystore type.
|
||||||
#
|
#
|
||||||
keystore.type=PKCS11
|
keystore.type=PKCS12
|
||||||
fips.keystore.type=PKCS11
|
fips.keystore.type=PKCS12
|
||||||
|
|
||||||
|
# This is needed especially if we cannot add security provider "com.sun.net.ssl.internal.ssl.Provider BCFIPS" as a security provider.
|
||||||
|
# OpenJDK has "SunX509" as default algorithm, but that one is not supported by BCJSSE. So adding the Sun provider delegating to BCFIPS is needed (as above)
|
||||||
|
# or changing default algorithm as described here
|
||||||
|
ssl.KeyManagerFactory.algorithm=PKIX
|
||||||
|
fips.ssl.KeyManagerFactory.algorithm=PKIX
|
||||||
|
|
||||||
|
ssl.TrustManagerFactory.algorithm=PKIX
|
||||||
|
fips.ssl.TrustManagerFactory.algorithm=PKIX
|
||||||
|
|
||||||
|
#
|
||||||
|
# Controls compatibility mode for JKS and PKCS12 keystore types.
|
||||||
|
#
|
||||||
|
# When set to 'true', both JKS and PKCS12 keystore types support loading
|
||||||
|
# keystore files in either JKS or PKCS12 format. When set to 'false' the
|
||||||
|
# JKS keystore type supports loading only JKS keystore files and the PKCS12
|
||||||
|
# keystore type supports loading only PKCS12 keystore files.
|
||||||
|
#
|
||||||
|
# This is set to false as BCFIPS providers don't support JKS
|
||||||
|
keystore.type.compat=false
|
||||||
|
fips.keystore.type.compat=false
|
||||||
|
|
145
docs/fips.md
145
docs/fips.md
|
@ -1,57 +1,140 @@
|
||||||
FIPS 140-2 Integration
|
FIPS 140-2 Integration
|
||||||
======================
|
======================
|
||||||
|
|
||||||
Build with FIPS
|
Run the server with FIPS
|
||||||
---------------
|
------------------------
|
||||||
|
|
||||||
With OpenJDK 11 on the classpath, run this from the project root directory:
|
To run Keycloak quarkus distribution, on the FIPS enabled host and FIPS enabled OpenJDK, you need to:
|
||||||
|
- Make sure that Keycloak will use the BouncyCastle FIPS dependencies instead of the normal BouncyCastle dependencies
|
||||||
|
- Make sure to start the server with the FIPS mode.
|
||||||
|
|
||||||
|
1) Copy BCFIPS dependencies to your Keycloak distribution.
|
||||||
|
You can either download them from BouncyCastle page and add it manually to the directory `KEYCLOAK_HOME/providers`(make sure to
|
||||||
|
use proper versions compatible with BouncyCastle Keycloak dependencies).
|
||||||
|
|
||||||
|
Or you can use for example commands like this to copy the appropriate BCFIPS jars to the Keycloak distribution. Again, replace
|
||||||
|
the BCFIPS versions with the appropriate versions from pom.xml. Assumption is that you have already these BCFIPS in
|
||||||
|
your local maven repository, which can be achieved for example by building `crypto/fips1402` module (See the section for
|
||||||
|
running the unit tests below):
|
||||||
|
|
||||||
```
|
```
|
||||||
mvn clean install -DskipTests=true -Dfips140-2 -Pquarkus
|
cd $KEYCLOAK_HOME/bin
|
||||||
```
|
export MAVEN_REPO_HOME=$HOME/.m2/repository
|
||||||
The property `fips140-2` is used to trigger maven profile to build keycloak+quarkus distribution with `bouncycastle-fips` dependencies instead of plain `bouncycastle`
|
cp $MAVEN_REPO_HOME/org/bouncycastle/bc-fips/1.0.2.3/bc-fips-1.0.2.3.jar ../providers/
|
||||||
and also with `keycloak-crypto-fips1402` module containing some security code dependent on bouncycastle-fips APIs.
|
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/
|
||||||
Note, that if you ommit the `fips140-2` property from the command above, then the quarkus distribution will be built
|
|
||||||
with the plain non-fips bouncycastle dependencies and with `keycloak-crypto-default` module.
|
|
||||||
|
|
||||||
Then unzip and check only bouncycastle-fips libraries are inside "lib" directory:
|
|
||||||
```
|
|
||||||
tar xf $KEYCLOAK_SOURCES/quarkus/dist/target/keycloak-999-SNAPSHOT.tar.gz
|
|
||||||
ls keycloak-999-SNAPSHOT/lib/lib/main/org.bouncycastle.bc*
|
|
||||||
```
|
|
||||||
Output should be something like:
|
|
||||||
```
|
|
||||||
keycloak-999-SNAPSHOT/lib/lib/main/org.bouncycastle.bc-fips-1.0.2.jar keycloak-999-SNAPSHOT/lib/lib/main/org.bouncycastle.bctls-fips-1.0.11.jar
|
|
||||||
keycloak-999-SNAPSHOT/lib/lib/main/org.bouncycastle.bcpkix-fips-1.0.3.jar
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly the JAR keycloak-fips-integration should be available:
|
2) Now create either pkcs12 or bcfks keystore. The pkcs12 works just in BCFIPS non-approved mode.
|
||||||
|
|
||||||
|
Please choose either `bcfips` or `pkcs12` and use the appropriate value of `KEYSTORE_FILE` variable according to your choice:
|
||||||
|
|
||||||
|
Also make sure to set `KEYCLOAK_SOURCES` to the location with your Keycloak codebase.
|
||||||
|
|
||||||
|
Note that for keystore generation, it is needed to use the BouncyCastle FIPS libraries and use custom security file, which
|
||||||
|
will remove default SUN and SunPKCS11 providers as it doesn't work to create keystore with them on FIPS enabled OpenJDK11 due
|
||||||
|
the limitation described here https://access.redhat.com/solutions/6954451 and in the related bugzilla https://bugzilla.redhat.com/show_bug.cgi?id=2048582.
|
||||||
```
|
```
|
||||||
ls keycloak-999-SNAPSHOT/lib/lib/main/org.keycloak.keycloak-fips-integration-999-SNAPSHOT.jar
|
export KEYSTORE_FILE=keycloak-server.pkcs12
|
||||||
|
#export KEYSTORE_FILE=keycloak-server.bcfks
|
||||||
|
export KEYCLOAK_SOURCES=$HOME/IdeaProjects/keycloak
|
||||||
|
|
||||||
|
export KEYSTORE_FORMAT=$(echo $KEYSTORE_FILE | cut -d. -f2)
|
||||||
|
|
||||||
|
# Removing old keystore file to start from fresh
|
||||||
|
rm keycloak-server.pkcs12
|
||||||
|
rm keycloak-server.bcfks
|
||||||
|
|
||||||
|
keytool -keystore $KEYSTORE_FILE \
|
||||||
|
-storetype $KEYSTORE_FORMAT \
|
||||||
|
-providername BCFIPS \
|
||||||
|
-providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
|
||||||
|
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
|
||||||
|
-providerpath $MAVEN_REPO_HOME/org/bouncycastle/bc-fips/1.0.2.3/bc-fips-1.0.2.3.jar \
|
||||||
|
-alias localhost \
|
||||||
|
-genkeypair -sigalg SHA512withRSA -keyalg RSA -storepass passwordpassword \
|
||||||
|
-dname CN=localhost -keypass passwordpassword \
|
||||||
|
-J-Djava.security.properties=$KEYCLOAK_SOURCES/crypto/fips1402/src/test/resources/kc.java.security
|
||||||
```
|
```
|
||||||
|
|
||||||
Now run the server on the FIPS enabled machine with FIPS-enabled OpenJDK (Tested on RHEL 8.6):
|
3) Run "build" to re-augment with `enabled` fips mode and start the server.
|
||||||
|
|
||||||
|
For the `fips-mode`, he alternative is to use `--fips-mode=strict` in which case BouncyCastle FIPS will use "approved mode",
|
||||||
|
which means even stricter security algorithms. As mentioned above, strict mode won't work with `pkcs12` keystore:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd keycloak-999-SNAPSHOT/bin
|
./kc.sh build --fips-mode=enabled
|
||||||
./kc.sh start-dev
|
./kc.sh start --optimized --hostname=localhost \
|
||||||
|
--https-key-store-file=$PWD/$KEYSTORE_FILE \
|
||||||
|
--https-key-store-type=$KEYSTORE_FORMAT \
|
||||||
|
--https-key-store-password=passwordpassword \
|
||||||
|
--log-level=INFO,org.keycloak.common.crypto:TRACE,org.keycloak.crypto:TRACE
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: Right now, server should start, and you should be able to use `http://localhost:8080` and login to admin console etc.
|
4) The approach above will run the Keycloak JVM with all the default java security providers and will add also
|
||||||
Keycloak will now use bouncycastle-fips libraries and the `CryptoIntegration` will use `FIPS1402Provider`.
|
BouncyCastle FIPS security providers on top of that in runtime. This works fine, however it may not be guaranteed that
|
||||||
|
all the crypto algorithms are used in the FIPS compliant way as the default providers like "Sun" potentially allow non-FIPS
|
||||||
|
usage in the Java. Some more details here: 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
|
||||||
|
|
||||||
Run the tests in the FIPS environment
|
To ensure that Java strictly allows to use only FIPS-compliant crypto, it can be good to rely solely just on the BCFIPS.
|
||||||
-------------------------------------
|
This is possible by using custom java security file, which adds just the BouncyCastle FIPS security providers. This requires
|
||||||
|
BouncyCastle FIPS dependencies to be available in the bootstrap classpath instead of adding them in runtime.
|
||||||
|
|
||||||
|
So for this approach, it is needed to move the BCFIPS jars from the `providers` directory to bootstrap classpath.
|
||||||
|
```
|
||||||
|
mkdir ../lib/bootstrap
|
||||||
|
mv ../providers/bc*.jar ../lib/bootstrap/
|
||||||
|
```
|
||||||
|
Then run `build` and `start` commands as above, but with additional property for the alternative security file like
|
||||||
|
```
|
||||||
|
-Djava.security.properties=$KEYCLOAK_SOURCES/crypto/fips1402/src/test/resources/kc.java.security
|
||||||
|
```
|
||||||
|
At the server startup, you should see the message like this in the log and you can check if correct providers are present and not any others:
|
||||||
|
```
|
||||||
|
2022-10-10 08:23:07,097 TRACE [org.keycloak.common.crypto.CryptoIntegration] (main) Java security providers: [
|
||||||
|
KC(BCFIPS version 1.000203) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider,
|
||||||
|
BCFIPS version 1.000203 - class org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider,
|
||||||
|
BCJSSE version 1.001202 - class org.bouncycastle.jsse.provider.BouncyCastleJsseProvider,
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: If you want to use BouncyCastle approved mode, then it is recommended to change/add these properties into the `kc.java.security`
|
||||||
|
file:
|
||||||
|
```
|
||||||
|
keystore.type=BCFKS
|
||||||
|
fips.keystore.type=BCFKS
|
||||||
|
org.bouncycastle.fips.approved_only=true
|
||||||
|
```
|
||||||
|
and then check that startup log contains `KC` provider contains KC provider with the note about `Approved Mode` like this:
|
||||||
|
```
|
||||||
|
KC(BCFIPS version 1.000203 Approved Mode) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider,
|
||||||
|
```
|
||||||
|
Note that in approved mode, there are few limitations at the moment like for example:
|
||||||
|
- User passwords must be at least 14 characters long
|
||||||
|
- 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 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.
|
This instruction is about running automated tests on the FIPS enabled RHEL 8.6 system with the FIPS enabled OpenJDK 11.
|
||||||
|
|
||||||
So far only the unit tests inside the `crypto` module are supported. More effort is needed to have whole testsuite passing.
|
So far only the unit tests inside the `crypto` module are supported. More effort is needed to have whole testsuite passing.
|
||||||
|
|
||||||
First it is needed to build the project (See above). Then run the tests in the `crypto` module.
|
First it is needed to build the project (See above). Then run the tests in the `crypto` module.
|
||||||
```
|
```
|
||||||
mvn clean install -f crypto
|
mvn clean install -f common -DskipTests=true
|
||||||
|
mvn clean install -f core -DskipTests=true
|
||||||
|
mvn clean install -f server-spi -DskipTests=true
|
||||||
|
mvn clean install -f server-spi-private -DskipTests=true
|
||||||
|
mvn clean install -f crypto/fips1402
|
||||||
```
|
```
|
||||||
|
|
||||||
The tests should work also with the BouncyCastle approved mode, which is more strict in the used crypto algorithms
|
The tests should work also with the BouncyCastle approved mode, which is more strict in the used crypto algorithms
|
||||||
```
|
```
|
||||||
mvn clean install -f crypto -Dorg.bouncycastle.fips.approved_only=true
|
mvn clean install -f crypto/fips1402 -Dorg.bouncycastle.fips.approved_only=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run the integration tests in the FIPS environment
|
||||||
|
-------------------------------------------------
|
||||||
|
See the FIPS section in the [MySQL docker image](../testsuite/integration-arquillian/HOW-TO-RUN.md)
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,9 @@
|
||||||
package org.keycloak.config;
|
package org.keycloak.config;
|
||||||
|
|
||||||
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
|
|
||||||
public class SecurityOptions {
|
public class SecurityOptions {
|
||||||
|
|
||||||
public enum FipsMode {
|
|
||||||
enabled("org.keycloak.crypto.fips.FIPS1402Provider"),
|
|
||||||
strict("org.keycloak.crypto.fips.Fips1402StrictCryptoProvider"),
|
|
||||||
disabled("org.keycloak.crypto.def.DefaultCryptoProvider");
|
|
||||||
|
|
||||||
private String providerClassName;
|
|
||||||
|
|
||||||
FipsMode(String providerClassName) {
|
|
||||||
this.providerClassName = providerClassName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFipsEnabled() {
|
|
||||||
return this.equals(enabled) || this.equals(strict);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProviderClassName() {
|
|
||||||
return providerClassName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Option<FipsMode> FIPS_MODE = new OptionBuilder<>("fips-mode", FipsMode.class)
|
public static final Option<FipsMode> FIPS_MODE = new OptionBuilder<>("fips-mode", FipsMode.class)
|
||||||
.category(OptionCategory.SECURITY)
|
.category(OptionCategory.SECURITY)
|
||||||
.buildTime(true)
|
.buildTime(true)
|
||||||
|
|
|
@ -89,6 +89,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
|
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
|
||||||
import org.jboss.resteasy.spi.ResteasyDeployment;
|
import org.jboss.resteasy.spi.ResteasyDeployment;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.config.SecurityOptions;
|
import org.keycloak.config.SecurityOptions;
|
||||||
import org.keycloak.config.StorageOptions;
|
import org.keycloak.config.StorageOptions;
|
||||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||||
|
@ -305,10 +306,9 @@ class KeycloakProcessor {
|
||||||
*
|
*
|
||||||
* @param recorder
|
* @param recorder
|
||||||
*/
|
*/
|
||||||
@Consume(BootstrapConfigSetupCompleteBuildItem.class)
|
|
||||||
@Record(ExecutionTime.STATIC_INIT)
|
@Record(ExecutionTime.STATIC_INIT)
|
||||||
@BuildStep
|
@BuildStep
|
||||||
KeycloakSessionFactoryPreInitBuildItem configureProviders(KeycloakRecorder recorder, List<PersistenceXmlDescriptorBuildItem> descriptors) {
|
KeycloakSessionFactoryPreInitBuildItem configureKeycloakSessionFactory(KeycloakRecorder recorder, List<PersistenceXmlDescriptorBuildItem> descriptors) {
|
||||||
Profile.setInstance(new QuarkusProfile());
|
Profile.setInstance(new QuarkusProfile());
|
||||||
Map<Spi, Map<Class<? extends Provider>, Map<String, Class<? extends ProviderFactory>>>> factories = new HashMap<>();
|
Map<Spi, Map<Class<? extends Provider>, Map<String, Class<? extends ProviderFactory>>>> factories = new HashMap<>();
|
||||||
Map<Class<? extends Provider>, String> defaultProviders = new HashMap<>();
|
Map<Class<? extends Provider>, String> defaultProviders = new HashMap<>();
|
||||||
|
@ -572,13 +572,13 @@ class KeycloakProcessor {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Consume(KeycloakSessionFactoryPreInitBuildItem.class)
|
@Consume(BootstrapConfigSetupCompleteBuildItem.class)
|
||||||
@BuildStep
|
@BuildStep
|
||||||
@Record(ExecutionTime.STATIC_INIT)
|
@Record(ExecutionTime.STATIC_INIT)
|
||||||
void setCryptoProvider(KeycloakRecorder recorder) {
|
void setCryptoProvider(KeycloakRecorder recorder) {
|
||||||
SecurityOptions.FipsMode fipsMode = Configuration.getOptionalValue(
|
FipsMode fipsMode = Configuration.getOptionalValue(
|
||||||
MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + SecurityOptions.FIPS_MODE.getKey()).map(
|
MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + SecurityOptions.FIPS_MODE.getKey()).map(
|
||||||
SecurityOptions.FipsMode::valueOf).orElse(SecurityOptions.FipsMode.disabled);
|
FipsMode::valueOf).orElse(FipsMode.disabled);
|
||||||
|
|
||||||
recorder.setCryptoProvider(fipsMode);
|
recorder.setCryptoProvider(fipsMode);
|
||||||
}
|
}
|
||||||
|
|
2
quarkus/dist/src/main/content/bin/kc.bat
vendored
2
quarkus/dist/src/main/content/bin/kc.bat
vendored
|
@ -124,7 +124,7 @@ if "x%JAVA_HOME%" == "x" (
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
set "CLASSPATH_OPTS=%DIRNAME%..\lib\quarkus-run.jar"
|
set "CLASSPATH_OPTS=%DIRNAME%..\lib\quarkus-run.jar:%DIRNAME%..\lib\bootstrap\*"
|
||||||
|
|
||||||
set "JAVA_RUN_OPTS=%JAVA_OPTS% -Dkc.home.dir="%DIRNAME%.." -Djboss.server.config.dir="%DIRNAME%..\conf" -Dkeycloak.theme.dir="%DIRNAME%..\themes" %SERVER_OPTS% -cp "%CLASSPATH_OPTS%" io.quarkus.bootstrap.runner.QuarkusEntryPoint %CONFIG_ARGS%"
|
set "JAVA_RUN_OPTS=%JAVA_OPTS% -Dkc.home.dir="%DIRNAME%.." -Djboss.server.config.dir="%DIRNAME%..\conf" -Dkeycloak.theme.dir="%DIRNAME%..\themes" %SERVER_OPTS% -cp "%CLASSPATH_OPTS%" io.quarkus.bootstrap.runner.QuarkusEntryPoint %CONFIG_ARGS%"
|
||||||
|
|
||||||
|
|
2
quarkus/dist/src/main/content/bin/kc.sh
vendored
2
quarkus/dist/src/main/content/bin/kc.sh
vendored
|
@ -36,7 +36,7 @@ SERVER_OPTS="-Dkc.home.dir='$(abs_path '..')'"
|
||||||
SERVER_OPTS="$SERVER_OPTS -Djboss.server.config.dir='$(abs_path '../conf')'"
|
SERVER_OPTS="$SERVER_OPTS -Djboss.server.config.dir='$(abs_path '../conf')'"
|
||||||
SERVER_OPTS="$SERVER_OPTS -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
SERVER_OPTS="$SERVER_OPTS -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
SERVER_OPTS="$SERVER_OPTS -Dquarkus-log-max-startup-records=10000"
|
SERVER_OPTS="$SERVER_OPTS -Dquarkus-log-max-startup-records=10000"
|
||||||
CLASSPATH_OPTS="'$(abs_path "../lib/quarkus-run.jar")'"
|
CLASSPATH_OPTS="'$(abs_path "../lib/quarkus-run.jar"):$(abs_path "../lib/bootstrap/*")'"
|
||||||
|
|
||||||
DEBUG_MODE="${DEBUG:-false}"
|
DEBUG_MODE="${DEBUG:-false}"
|
||||||
DEBUG_PORT="${DEBUG_PORT:-8787}"
|
DEBUG_PORT="${DEBUG_PORT:-8787}"
|
||||||
|
|
|
@ -40,7 +40,7 @@ import org.keycloak.Config;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.crypto.CryptoIntegration;
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
import org.keycloak.common.crypto.CryptoProvider;
|
import org.keycloak.common.crypto.CryptoProvider;
|
||||||
import org.keycloak.config.SecurityOptions;
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||||
|
@ -166,7 +166,7 @@ public class KeycloakRecorder {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCryptoProvider(SecurityOptions.FipsMode fipsMode) {
|
public void setCryptoProvider(FipsMode fipsMode) {
|
||||||
String cryptoProvider = fipsMode.getProviderClassName();
|
String cryptoProvider = fipsMode.getProviderClassName();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.config.ClassLoaderOptions;
|
import org.keycloak.config.ClassLoaderOptions;
|
||||||
import org.keycloak.config.SecurityOptions;
|
import org.keycloak.config.SecurityOptions;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
|
@ -30,7 +32,7 @@ final class ClassLoaderPropertyMappers {
|
||||||
ConfigValue fipsEnabled = Configuration.getConfigValue(
|
ConfigValue fipsEnabled = Configuration.getConfigValue(
|
||||||
MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + SecurityOptions.FIPS_MODE.getKey());
|
MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + SecurityOptions.FIPS_MODE.getKey());
|
||||||
|
|
||||||
if (fipsEnabled != null && SecurityOptions.FipsMode.valueOf(fipsEnabled.getValue()).isFipsEnabled()) {
|
if (fipsEnabled != null && FipsMode.valueOf(fipsEnabled.getValue()).isFipsEnabled()) {
|
||||||
return Optional.of(
|
return Optional.of(
|
||||||
"org.bouncycastle:bcprov-jdk15on,org.bouncycastle:bcpkix-jdk15on,org.keycloak:keycloak-crypto-default");
|
"org.bouncycastle:bcprov-jdk15on,org.bouncycastle:bcpkix-jdk15on,org.keycloak:keycloak-crypto-default");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import static java.util.Optional.of;
|
||||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.config.SecurityOptions;
|
import org.keycloak.config.SecurityOptions;
|
||||||
|
|
||||||
import io.smallrye.config.ConfigSourceInterceptorContext;
|
import io.smallrye.config.ConfigSourceInterceptorContext;
|
||||||
|
@ -23,16 +25,16 @@ final class SecurityPropertyMappers {
|
||||||
|
|
||||||
private static Optional<String> resolveFipsMode(Optional<String> value, ConfigSourceInterceptorContext context) {
|
private static Optional<String> resolveFipsMode(Optional<String> value, ConfigSourceInterceptorContext context) {
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
return of(SecurityOptions.FipsMode.disabled.toString());
|
return of(FipsMode.disabled.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return of(SecurityOptions.FipsMode.valueOf(value.get()).toString());
|
return of(FipsMode.valueOf(value.get()).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optional<String> resolveSecurityProvider(Optional<String> value,
|
private static Optional<String> resolveSecurityProvider(Optional<String> value,
|
||||||
ConfigSourceInterceptorContext configSourceInterceptorContext) {
|
ConfigSourceInterceptorContext configSourceInterceptorContext) {
|
||||||
SecurityOptions.FipsMode fipsMode = value.map(SecurityOptions.FipsMode::valueOf)
|
FipsMode fipsMode = value.map(FipsMode::valueOf)
|
||||||
.orElse(SecurityOptions.FipsMode.disabled);
|
.orElse(FipsMode.disabled);
|
||||||
|
|
||||||
if (fipsMode.isFipsEnabled()) {
|
if (fipsMode.isFipsEnabled()) {
|
||||||
return of("BCFIPS");
|
return of("BCFIPS");
|
||||||
|
|
|
@ -38,8 +38,6 @@ import javax.mail.internet.MimeMultipart;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -49,9 +47,8 @@ import java.util.Properties;
|
||||||
*/
|
*/
|
||||||
public class DefaultEmailSenderProvider implements EmailSenderProvider {
|
public class DefaultEmailSenderProvider implements EmailSenderProvider {
|
||||||
|
|
||||||
private static final String SUPPORTED_SSL_PROTOCOLS = getSupportedSslProtocols();
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(DefaultEmailSenderProvider.class);
|
private static final Logger logger = Logger.getLogger(DefaultEmailSenderProvider.class);
|
||||||
|
private static final String SUPPORTED_SSL_PROTOCOLS = getSupportedSslProtocols();
|
||||||
|
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.truststore;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
@ -44,6 +45,8 @@ import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,6 +69,7 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
String storepath = config.get("file");
|
String storepath = config.get("file");
|
||||||
String pass = config.get("password");
|
String pass = config.get("password");
|
||||||
String policy = config.get("hostname-verification-policy");
|
String policy = config.get("hostname-verification-policy");
|
||||||
|
String configuredType = config.get("type");
|
||||||
|
|
||||||
// if "truststore" . "file" is not configured then it is disabled
|
// if "truststore" . "file" is not configured then it is disabled
|
||||||
if (storepath == null && pass == null && policy == null) {
|
if (storepath == null && pass == null && policy == null) {
|
||||||
|
@ -82,10 +86,11 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
throw new RuntimeException("Attribute 'password' missing in 'truststore':'file' configuration");
|
throw new RuntimeException("Attribute 'password' missing in 'truststore':'file' configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String type = getTruststoreType(storepath, configuredType);
|
||||||
try {
|
try {
|
||||||
truststore = loadStore(storepath, pass == null ? null :pass.toCharArray());
|
truststore = loadStore(storepath, type, pass == null ? null :pass.toCharArray());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to initialize TruststoreProviderFactory: " + new File(storepath).getAbsolutePath(), e);
|
throw new RuntimeException("Failed to initialize TruststoreProviderFactory: " + new File(storepath).getAbsolutePath() + ", truststore type: " + type, e);
|
||||||
}
|
}
|
||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
verificationPolicy = HostnameVerificationPolicy.WILDCARD;
|
verificationPolicy = HostnameVerificationPolicy.WILDCARD;
|
||||||
|
@ -101,11 +106,11 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
provider = new FileTruststoreProvider(truststore, verificationPolicy, Collections.unmodifiableMap(certsLoader.trustedRootCerts)
|
provider = new FileTruststoreProvider(truststore, verificationPolicy, Collections.unmodifiableMap(certsLoader.trustedRootCerts)
|
||||||
, Collections.unmodifiableMap(certsLoader.intermediateCerts));
|
, Collections.unmodifiableMap(certsLoader.intermediateCerts));
|
||||||
TruststoreProviderSingleton.set(provider);
|
TruststoreProviderSingleton.set(provider);
|
||||||
log.debug("File truststore provider initialized: " + new File(storepath).getAbsolutePath());
|
log.debugf("File truststore provider initialized: %s, Truststore type: %s", new File(storepath).getAbsolutePath(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeyStore loadStore(String path, char[] password) throws Exception {
|
private KeyStore loadStore(String path, String type, char[] password) throws Exception {
|
||||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore ks = KeyStore.getInstance(type);
|
||||||
InputStream is = new FileInputStream(path);
|
InputStream is = new FileInputStream(path);
|
||||||
try {
|
try {
|
||||||
ks.load(is, password);
|
ks.load(is, password);
|
||||||
|
@ -154,6 +159,25 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getTruststoreType(String path, String configuredType) {
|
||||||
|
// Configured type has precedence
|
||||||
|
if (configuredType != null) return configuredType;
|
||||||
|
|
||||||
|
// Fallback to detected tyoe from the file format (EG. my-keystore.pkcs12 will return "pkcs12")
|
||||||
|
int lastDotIndex = path.lastIndexOf('.');
|
||||||
|
if (lastDotIndex > -1) {
|
||||||
|
String ext = path.substring(lastDotIndex).toUpperCase();
|
||||||
|
Optional<String> detectedType = Arrays.stream(KeystoreUtil.KeystoreFormat.values())
|
||||||
|
.map(KeystoreUtil.KeystoreFormat::toString)
|
||||||
|
.filter(ksFormat -> ksFormat.equals(ext))
|
||||||
|
.findFirst();
|
||||||
|
if (detectedType.isPresent()) return detectedType.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to default JVM
|
||||||
|
return KeyStore.getDefaultType();
|
||||||
|
}
|
||||||
|
|
||||||
private static class TruststoreCertificatesLoader {
|
private static class TruststoreCertificatesLoader {
|
||||||
|
|
||||||
private Map<X500Principal, X509Certificate> trustedRootCerts = new HashMap<>();
|
private Map<X500Principal, X509Certificate> trustedRootCerts = new HashMap<>();
|
||||||
|
|
|
@ -923,3 +923,25 @@ DefaultHostnameTest.java:226)
|
||||||
when running these tests on your local machine. This happens when something on your machine or network is blocking DNS queries to [nip.io](https://nip.io)
|
when running these tests on your local machine. This happens when something on your machine or network is blocking DNS queries to [nip.io](https://nip.io)
|
||||||
One possible workaround is to add a commonly used public dns server (e.g. 8.8.8.8 for google dns server) to your local
|
One possible workaround is to add a commonly used public dns server (e.g. 8.8.8.8 for google dns server) to your local
|
||||||
networks dns configuration and run the tests.
|
networks dns configuration and run the tests.
|
||||||
|
|
||||||
|
## FIPS 140-2 testing
|
||||||
|
|
||||||
|
On the FIPS enabled platform with FIPS enabled OpenJDK 11, you can run this to test against Keycloak server on Quarkus
|
||||||
|
with FIPS 140.2 integration enabled
|
||||||
|
```
|
||||||
|
mvn -B -f testsuite/integration-arquillian/pom.xml \
|
||||||
|
clean install \
|
||||||
|
-Pauth-server-quarkus,auth-server-fips140-2 \
|
||||||
|
-Dcom.redhat.fips=false
|
||||||
|
```
|
||||||
|
NOTE 1: The property `com.redhat.fips` is needed so that testsuite itself is executed in the JVM with FIPS disabled. However
|
||||||
|
most important part is that Keycloak itself is running on the JVM with FIPS enabled. You can check log from server startup and
|
||||||
|
there should be messages similar to those:
|
||||||
|
```
|
||||||
|
2022-10-11 19:34:29,521 DEBUG [org.keycloak.common.crypto.CryptoIntegration] (main) Using the crypto provider: org.keycloak.crypto.fips.FIPS1402Provider
|
||||||
|
2022-10-11 19:34:31,072 TRACE [org.keycloak.common.crypto.CryptoIntegration] (main) Java security providers: [
|
||||||
|
KC(BCFIPS version 1.000203) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider,
|
||||||
|
BCFIPS version 1.000203 - class org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider,
|
||||||
|
BCJSSE version 1.001202 - class org.bouncycastle.jsse.provider.BouncyCastleJsseProvider,
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
# 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 Java 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)
|
||||||
|
# Uses only BouncyCastle FIPS providers to make sure to use only FIPS compliant cryptography.
|
||||||
|
#
|
||||||
|
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
|
||||||
|
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
|
||||||
|
security.provider.3=
|
||||||
|
|
||||||
|
#
|
||||||
|
# Security providers used when global crypto-policies are set to FIPS (Usually it is used when FIPS enabled on system/JVM level)
|
||||||
|
#
|
||||||
|
fips.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
|
||||||
|
fips.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
|
||||||
|
fips.provider.3=
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# fips.provider.2=SUN
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default keystore type.
|
||||||
|
#
|
||||||
|
keystore.type=PKCS12
|
||||||
|
fips.keystore.type=PKCS12
|
||||||
|
|
||||||
|
# This is needed especially if we cannot add security provider "com.sun.net.ssl.internal.ssl.Provider BCFIPS" as a security provider.
|
||||||
|
# OpenJDK has "SunX509" as default algorithm, but that one is not supported by BCJSSE. So adding the Sun provider delegating to BCFIPS is needed (as above)
|
||||||
|
# or changing default algorithm as described here
|
||||||
|
ssl.KeyManagerFactory.algorithm=PKIX
|
||||||
|
fips.ssl.KeyManagerFactory.algorithm=PKIX
|
||||||
|
|
||||||
|
ssl.TrustManagerFactory.algorithm=PKIX
|
||||||
|
fips.ssl.TrustManagerFactory.algorithm=PKIX
|
||||||
|
|
||||||
|
#
|
||||||
|
# Controls compatibility mode for JKS and PKCS12 keystore types.
|
||||||
|
#
|
||||||
|
# When set to 'true', both JKS and PKCS12 keystore types support loading
|
||||||
|
# keystore files in either JKS or PKCS12 format. When set to 'false' the
|
||||||
|
# JKS keystore type supports loading only JKS keystore files and the PKCS12
|
||||||
|
# keystore type supports loading only PKCS12 keystore files.
|
||||||
|
#
|
||||||
|
# This is set to false as BCFIPS providers don't support JKS
|
||||||
|
keystore.type.compat=false
|
||||||
|
fips.keystore.type.compat=false
|
Binary file not shown.
Binary file not shown.
|
@ -21,6 +21,7 @@
|
||||||
<auth.server.quarkus.cluster.config>local</auth.server.quarkus.cluster.config>
|
<auth.server.quarkus.cluster.config>local</auth.server.quarkus.cluster.config>
|
||||||
<!-- Path to testsuite/test resources : Themes-->
|
<!-- Path to testsuite/test resources : Themes-->
|
||||||
<keycloak.base.test.resource>${project.parent.basedir}/../../tests/base/src/test/resources</keycloak.base.test.resource>
|
<keycloak.base.test.resource>${project.parent.basedir}/../../tests/base/src/test/resources</keycloak.base.test.resource>
|
||||||
|
<auth.server.fips.mode>disabled</auth.server.fips.mode>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -300,5 +301,68 @@
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>auth-server-fips140-2</id>
|
||||||
|
<properties>
|
||||||
|
<auth.server.fips.mode>enabled</auth.server.fips.mode>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-crypto-fips1402</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-fips-pki-conf</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${auth.server.home}/conf</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${common.resources}/fips</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<overwrite>true</overwrite>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-bcfips-deps</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${auth.server.home}/lib/bootstrap</outputDirectory>
|
||||||
|
<includeArtifactIds>bc-fips,bctls-fips,bcpkix-fips</includeArtifactIds>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</profile>
|
||||||
|
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import org.jboss.arquillian.container.spi.ConfigurationException;
|
import org.jboss.arquillian.container.spi.ConfigurationException;
|
||||||
import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration;
|
import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -23,6 +24,20 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
||||||
private int bindHttpPort = 8080;
|
private int bindHttpPort = 8080;
|
||||||
private int bindHttpsPortOffset = 0;
|
private int bindHttpsPortOffset = 0;
|
||||||
private int bindHttpsPort = Integer.getInteger("auth.server.https.port", 8543);
|
private int bindHttpsPort = Integer.getInteger("auth.server.https.port", 8543);
|
||||||
|
|
||||||
|
private String keystoreFile = System.getProperty("auth.server.keystore");
|
||||||
|
|
||||||
|
private String keystorePassword = System.getProperty("auth.server.keystore.password");
|
||||||
|
|
||||||
|
private String keystoreType = System.getProperty("auth.server.keystore.type");
|
||||||
|
|
||||||
|
|
||||||
|
private String truststoreFile = System.getProperty("auth.server.truststore");
|
||||||
|
|
||||||
|
private String truststorePassword = System.getProperty("auth.server.truststore.password");
|
||||||
|
|
||||||
|
private String truststoreType = System.getProperty("auth.server.truststore.type");
|
||||||
|
|
||||||
private int debugPort = -1;
|
private int debugPort = -1;
|
||||||
private Path providersPath = Paths.get(System.getProperty("auth.server.home"));
|
private Path providersPath = Paths.get(System.getProperty("auth.server.home"));
|
||||||
private int startupTimeoutInSeconds = 300;
|
private int startupTimeoutInSeconds = 300;
|
||||||
|
@ -34,6 +49,8 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
||||||
private boolean reaugmentBeforeStart;
|
private boolean reaugmentBeforeStart;
|
||||||
private String importFile = System.getProperty("migration.import.file.name");
|
private String importFile = System.getProperty("migration.import.file.name");
|
||||||
|
|
||||||
|
private FipsMode fipsMode = FipsMode.valueOf(System.getProperty("auth.server.fips.mode"));
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate() throws ConfigurationException {
|
public void validate() throws ConfigurationException {
|
||||||
int basePort = getBindHttpPort();
|
int basePort = getBindHttpPort();
|
||||||
|
@ -88,6 +105,54 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
||||||
this.bindHttpPort = bindHttpPort;
|
this.bindHttpPort = bindHttpPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getKeystoreFile() {
|
||||||
|
return keystoreFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeystoreFile(String keystoreFile) {
|
||||||
|
this.keystoreFile = keystoreFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeystorePassword() {
|
||||||
|
return keystorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeystorePassword(String keystorePassword) {
|
||||||
|
this.keystorePassword = keystorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeystoreType() {
|
||||||
|
return keystoreType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeystoreType(String keystoreType) {
|
||||||
|
this.keystoreType = keystoreType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTruststoreFile() {
|
||||||
|
return truststoreFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTruststoreFile(String truststoreFile) {
|
||||||
|
this.truststoreFile = truststoreFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTruststorePassword() {
|
||||||
|
return truststorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTruststorePassword(String truststorePassword) {
|
||||||
|
this.truststorePassword = truststorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTruststoreType() {
|
||||||
|
return truststoreType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTruststoreType(String truststoreType) {
|
||||||
|
this.truststoreType = truststoreType;
|
||||||
|
}
|
||||||
|
|
||||||
public Path getProvidersPath() {
|
public Path getProvidersPath() {
|
||||||
return providersPath;
|
return providersPath;
|
||||||
}
|
}
|
||||||
|
@ -140,6 +205,14 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
||||||
return javaOpts;
|
return javaOpts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void appendJavaOpts(String javaOpts) {
|
||||||
|
if (javaOpts == null) {
|
||||||
|
setJavaOpts(javaOpts);
|
||||||
|
} else {
|
||||||
|
setJavaOpts(this.javaOpts + " " + javaOpts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isReaugmentBeforeStart() {
|
public boolean isReaugmentBeforeStart() {
|
||||||
return reaugmentBeforeStart;
|
return reaugmentBeforeStart;
|
||||||
}
|
}
|
||||||
|
@ -163,4 +236,12 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
||||||
public void setImportFile(String importFile) {
|
public void setImportFile(String importFile) {
|
||||||
this.importFile = importFile;
|
this.importFile = importFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FipsMode getFipsMode() {
|
||||||
|
return fipsMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFipsMode(FipsMode fipsMode) {
|
||||||
|
this.fipsMode = fipsMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.jboss.shrinkwrap.api.Archive;
|
import org.jboss.shrinkwrap.api.Archive;
|
||||||
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
|
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
|
||||||
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
|
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
|
||||||
|
import org.keycloak.common.crypto.FipsMode;
|
||||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||||
import org.keycloak.testsuite.model.StoreProvider;
|
import org.keycloak.testsuite.model.StoreProvider;
|
||||||
|
|
||||||
|
@ -227,8 +228,10 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
||||||
final Supplier<Boolean> shouldSetUpDb = () -> !restart.get() && !storeProvider.equals(StoreProvider.DEFAULT);
|
final Supplier<Boolean> shouldSetUpDb = () -> !restart.get() && !storeProvider.equals(StoreProvider.DEFAULT);
|
||||||
final Supplier<String> getClusterConfig = () -> System.getProperty("auth.server.quarkus.cluster.config", "local");
|
final Supplier<String> getClusterConfig = () -> System.getProperty("auth.server.quarkus.cluster.config", "local");
|
||||||
|
|
||||||
|
log.debugf("FIPS Mode: %s", configuration.getFipsMode());
|
||||||
|
|
||||||
// only run build during first execution of the server (if the DB is specified), restarts or when running cluster tests
|
// only run build during first execution of the server (if the DB is specified), restarts or when running cluster tests
|
||||||
if (restart.get() || shouldSetUpDb.get() || "ha".equals(getClusterConfig.get())) {
|
if (restart.get() || shouldSetUpDb.get() || "ha".equals(getClusterConfig.get()) || configuration.getFipsMode() != FipsMode.disabled) {
|
||||||
commands.removeIf("--optimized"::equals);
|
commands.removeIf("--optimized"::equals);
|
||||||
commands.add("--http-relative-path=/auth");
|
commands.add("--http-relative-path=/auth");
|
||||||
|
|
||||||
|
@ -241,6 +244,10 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
||||||
commands.add("--cache-config-file=cluster-" + cacheMode + ".xml");
|
commands.add("--cache-config-file=cluster-" + cacheMode + ".xml");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configuration.getFipsMode() != FipsMode.disabled) {
|
||||||
|
addFipsOptions(commands);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addStorageOptions(storeProvider, commands);
|
addStorageOptions(storeProvider, commands);
|
||||||
|
@ -257,6 +264,26 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
||||||
storeProvider.addStoreOptions(commands);
|
storeProvider.addStoreOptions(commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addFipsOptions(List<String> commands) {
|
||||||
|
commands.add("--fips-mode=" + configuration.getFipsMode().toString());
|
||||||
|
|
||||||
|
log.debugf("Keystore file: %s, keystore type: %s, truststore file: %s, truststore type: %s",
|
||||||
|
configuration.getKeystoreFile(), configuration.getKeystoreType(),
|
||||||
|
configuration.getTruststoreFile(), configuration.getTruststoreType());
|
||||||
|
commands.add("--https-key-store-file=" + configuration.getKeystoreFile());
|
||||||
|
commands.add("--https-key-store-type=" + configuration.getKeystoreType());
|
||||||
|
commands.add("--https-key-store-password=" + configuration.getKeystorePassword());
|
||||||
|
commands.add("--https-trust-store-file=" + configuration.getTruststoreFile());
|
||||||
|
commands.add("--https-trust-store-type=" + configuration.getTruststoreType());
|
||||||
|
commands.add("--https-trust-store-password=" + configuration.getTruststorePassword());
|
||||||
|
commands.add("--spi-truststore-file-file=" + configuration.getTruststoreFile());
|
||||||
|
commands.add("--spi-truststore-file-password=" + configuration.getTruststorePassword());
|
||||||
|
commands.add("--spi-truststore-file-type=" + configuration.getTruststoreType());
|
||||||
|
commands.add("--log-level=INFO,org.keycloak.common.crypto:TRACE,org.keycloak.crypto:TRACE,org.keycloak.truststore:TRACE");
|
||||||
|
|
||||||
|
configuration.appendJavaOpts("-Djava.security.properties=" + System.getProperty("auth.server.java.security.file"));
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForReadiness() throws MalformedURLException, LifecycleException {
|
private void waitForReadiness() throws MalformedURLException, LifecycleException {
|
||||||
SuiteContext suiteContext = this.suiteContext.get();
|
SuiteContext suiteContext = this.suiteContext.get();
|
||||||
//TODO: not sure if the best endpoint but it makes sure that everything is properly initialized. Once we have
|
//TODO: not sure if the best endpoint but it makes sure that everything is properly initialized. Once we have
|
||||||
|
|
|
@ -73,8 +73,10 @@
|
||||||
<auth.server.adapter.impl.class>org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</auth.server.adapter.impl.class>
|
<auth.server.adapter.impl.class>org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</auth.server.adapter.impl.class>
|
||||||
<auth.server.truststore>${auth.server.config.dir}/keycloak.truststore</auth.server.truststore>
|
<auth.server.truststore>${auth.server.config.dir}/keycloak.truststore</auth.server.truststore>
|
||||||
<auth.server.truststore.password>secret</auth.server.truststore.password>
|
<auth.server.truststore.password>secret</auth.server.truststore.password>
|
||||||
|
<auth.server.truststore.type>jks</auth.server.truststore.type>
|
||||||
<auth.server.keystore>${auth.server.config.dir}/keycloak.jks</auth.server.keystore>
|
<auth.server.keystore>${auth.server.config.dir}/keycloak.jks</auth.server.keystore>
|
||||||
<auth.server.keystore.password>secret</auth.server.keystore.password>
|
<auth.server.keystore.password>secret</auth.server.keystore.password>
|
||||||
|
<auth.server.keystore.type>jks</auth.server.keystore.type>
|
||||||
<auth.server.jvm.args.extra/>
|
<auth.server.jvm.args.extra/>
|
||||||
|
|
||||||
<auth.server.jboss.artifactId>integration-arquillian-servers-auth-server-${auth.server}</auth.server.jboss.artifactId>
|
<auth.server.jboss.artifactId>integration-arquillian-servers-auth-server-${auth.server}</auth.server.jboss.artifactId>
|
||||||
|
@ -260,6 +262,7 @@
|
||||||
<auth.server.ocsp.responder.enabled>false</auth.server.ocsp.responder.enabled>
|
<auth.server.ocsp.responder.enabled>false</auth.server.ocsp.responder.enabled>
|
||||||
<keycloak.x509cert.lookup.provider>default</keycloak.x509cert.lookup.provider>
|
<keycloak.x509cert.lookup.provider>default</keycloak.x509cert.lookup.provider>
|
||||||
<auth.server.quarkus.cluster.config>local</auth.server.quarkus.cluster.config>
|
<auth.server.quarkus.cluster.config>local</auth.server.quarkus.cluster.config>
|
||||||
|
<auth.server.fips.mode>disabled</auth.server.fips.mode>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -522,8 +525,11 @@
|
||||||
<auth.server.jboss.jvm.debug.args>${auth.server.jboss.jvm.debug.args}</auth.server.jboss.jvm.debug.args>
|
<auth.server.jboss.jvm.debug.args>${auth.server.jboss.jvm.debug.args}</auth.server.jboss.jvm.debug.args>
|
||||||
<auth.server.truststore>${auth.server.truststore}</auth.server.truststore>
|
<auth.server.truststore>${auth.server.truststore}</auth.server.truststore>
|
||||||
<auth.server.truststore.password>${auth.server.truststore.password}</auth.server.truststore.password>
|
<auth.server.truststore.password>${auth.server.truststore.password}</auth.server.truststore.password>
|
||||||
|
<auth.server.truststore.type>${auth.server.truststore.type}</auth.server.truststore.type>
|
||||||
<auth.server.keystore>${auth.server.keystore}</auth.server.keystore>
|
<auth.server.keystore>${auth.server.keystore}</auth.server.keystore>
|
||||||
<auth.server.keystore.password>${auth.server.keystore.password}</auth.server.keystore.password>
|
<auth.server.keystore.password>${auth.server.keystore.password}</auth.server.keystore.password>
|
||||||
|
<auth.server.keystore.type>${auth.server.keystore.type}</auth.server.keystore.type>
|
||||||
|
<auth.server.java.security.file>${auth.server.java.security.file}</auth.server.java.security.file>
|
||||||
<auth.server.jvm.args.extra>${auth.server.jvm.args.extra}</auth.server.jvm.args.extra>
|
<auth.server.jvm.args.extra>${auth.server.jvm.args.extra}</auth.server.jvm.args.extra>
|
||||||
|
|
||||||
<auth.server.profile>${auth.server.profile}</auth.server.profile>
|
<auth.server.profile>${auth.server.profile}</auth.server.profile>
|
||||||
|
@ -676,6 +682,10 @@
|
||||||
<keycloak.connectionsJpa.user>${keycloak.connectionsJpa.user}</keycloak.connectionsJpa.user>
|
<keycloak.connectionsJpa.user>${keycloak.connectionsJpa.user}</keycloak.connectionsJpa.user>
|
||||||
<keycloak.connectionsJpa.password>${keycloak.connectionsJpa.password}</keycloak.connectionsJpa.password>
|
<keycloak.connectionsJpa.password>${keycloak.connectionsJpa.password}</keycloak.connectionsJpa.password>
|
||||||
|
|
||||||
|
<!-- FIPS 140-2 -->
|
||||||
|
<auth.server.fips.mode>${auth.server.fips.mode}</auth.server.fips.mode>
|
||||||
|
<auth.server.fips.keystore.type>${auth.server.fips.keystore.type}</auth.server.fips.keystore.type>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
~ Used for Wildfly Elytron 1.13.0.CR3+ RESTEasy client SSL truststore configuration.
|
~ Used for Wildfly Elytron 1.13.0.CR3+ RESTEasy client SSL truststore configuration.
|
||||||
~ See KEYCLOAK-15692, ELY-1891 issues & PRs of EAP7-1219 issue for details.
|
~ See KEYCLOAK-15692, ELY-1891 issues & PRs of EAP7-1219 issue for details.
|
||||||
|
@ -1572,6 +1582,23 @@
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>auth-server-fips140-2</id>
|
||||||
|
<properties>
|
||||||
|
<auth.server.fips.mode>enabled</auth.server.fips.mode>
|
||||||
|
|
||||||
|
<auth.server.keystore.type>pkcs12</auth.server.keystore.type>
|
||||||
|
<auth.server.keystore>${auth.server.config.dir}/keycloak-fips.keystore.${auth.server.keystore.type}</auth.server.keystore>
|
||||||
|
<auth.server.keystore.password>passwordpassword</auth.server.keystore.password>
|
||||||
|
|
||||||
|
<auth.server.truststore.type>${auth.server.keystore.type}</auth.server.truststore.type>
|
||||||
|
<auth.server.truststore>${auth.server.config.dir}/keycloak-fips.truststore.${auth.server.truststore.type}</auth.server.truststore>
|
||||||
|
<auth.server.truststore.password>passwordpassword</auth.server.truststore.password>
|
||||||
|
|
||||||
|
<auth.server.java.security.file>${auth.server.config.dir}/kc.java.security</auth.server.java.security.file>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>common-test-dependencies</id>
|
<id>common-test-dependencies</id>
|
||||||
<activation>
|
<activation>
|
||||||
|
|
Loading…
Reference in a new issue