Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
import java.util.Enumeration;
import java.util.Map;

import static java.util.Collections.emptyMap;
Expand Down Expand Up @@ -231,11 +232,9 @@ private KeyStore loadTrustStore(TruststoreSettings truststoreSettings) throws Ge
}
logger.debug("Loaded custom truststore from path: {} with type: {}", truststorePath, truststoreType);
} else if (Security.getProvider("BCFIPS") != null) {
throw new IllegalStateException(
"FIPS mode is active but no custom truststore is configured. "
+ "Please configure gcs.client.<client-name>.truststore.path and "
+ "gcs.client.<client-name>.truststore.secure_password settings."
);
// FIPS mode is active: create FIPS-compliant (BCFKS) truststore with Google default certs
certTrustStore = createFipsCompliantGoogleTrustStore();
logger.debug("Using FIPS-compliant Google default certificate trust store");
} else {
// requires java.lang.RuntimePermission "setFactory"
// Pin the TLS trust certificates.
Expand Down Expand Up @@ -302,4 +301,25 @@ static Integer toTimeout(final TimeValue timeout) {
}
return Math.toIntExact(timeout.getMillis());
}

private KeyStore createFipsCompliantGoogleTrustStore() throws GeneralSecurityException, IOException {
// Use BCFKS KeyStore type which is required for FIPS mode
KeyStore trustStore = KeyStore.getInstance("BCFIPS", "BCFIPS");
// Initialize empty BCFKS trust store
trustStore.load(null, null);

// Load Google's built-in default trust store
KeyStore googleDefaultStore = GoogleUtils.getCertificateTrustStore();
Enumeration<String> aliases = googleDefaultStore.aliases();

// Copy all certificate entries into the FIPS-compliant BCFKS trust store
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (googleDefaultStore.isCertificateEntry(alias)) {
trustStore.setCertificateEntry(alias, googleDefaultStore.getCertificate(alias));
}
}

return trustStore;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.opensearch.common.settings.Settings;
import org.opensearch.common.settings.SettingsException;

import java.io.IOException;
import java.nio.file.Path;

import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -68,17 +67,18 @@ public void testCustomTruststoreConfiguration() throws Exception {
assertNotNull(storage);
}

public void testFipsModeEnforcesTruststoreConfiguration() throws Exception {
public void testFipsModeTruststoreConfiguration() throws Exception {
final var service = new GoogleCloudStorageService();
final var statsCollector = new GoogleCloudStorageOperationsStats("bucket");

{ // Scenario 1: No truststore configuration -> should throw exception
{ // Scenario 1: No truststore configuration -> auto-create FIPS truststore
final var secureSettings = new MockSecureSettings();
final var settings = Settings.builder().setSecureSettings(secureSettings).build();
service.refreshAndClearCache(GoogleCloudStorageClientSettings.load(settings));

final var exception = expectThrows(IOException.class, () -> service.client("default", "repo", statsCollector));
assertThat(exception.getMessage(), containsString("FIPS mode is active but no custom truststore is configured"));
// Should create client successfully without exception
final var storage = service.client("default", "repo", statsCollector);
assertNotNull(storage);
}

{ // Scenario 2: Path + password configured (missing type) -> should throw exception
Expand All @@ -105,5 +105,4 @@ public void testFipsModeEnforcesTruststoreConfiguration() throws Exception {
assertNotNull("Storage client should be created successfully with full truststore configuration", storage);
}
}

}
Loading