From 7a93e829ec93cd9104c2cadacb1c702c60ac0e5e Mon Sep 17 00:00:00 2001 From: Iwan Igonin Date: Tue, 19 Nov 2024 10:54:58 +0100 Subject: [PATCH] Kerberos, forbiddenApis, SecureRandom, SunJCE, AzureTests Summery: - replace unsecure kerberos crypto algorithms - add 'java.security.KeyStore' to forbidden-apis - instantiate and use SecureRandom from BCFIPS library - exclude SunJCE from security providers list at runtime, when running in FIPS JVM - exclude Azure tests when running in FIPS JVM Signed-off-by: Iwan Igonin --- .../gradle/http/WaitForHttpResource.java | 14 +- .../gradle/testclusters/OpenSearchNode.java | 2 +- .../resources/forbidden/jdk-signatures.txt | 6 + distribution/src/config/fips_java.security | 4 - .../common/settings/KeyStoreWrapperTests.java | 18 +- libs/common/build.gradle | 6 + .../licenses/bcpkix-fips-2.0.7.jar.sha1 | 1 + libs/common/licenses/bouncycastle-LICENSE.txt | 14 ++ libs/common/licenses/bouncycastle-NOTICE.txt | 1 + .../opensearch/common/SecureRandomHolder.java | 4 +- .../common/crypto/KeyStoreFactory.java | 5 + modules/transport-netty4/build.gradle | 11 -- .../index/analysis/IcuAnalyzerTests.java | 5 + plugins/repository-azure/build.gradle | 11 +- plugins/repository-s3/build.gradle | 11 -- .../s3/S3BlobStoreRepositoryTests.java | 2 +- .../opensearch/repositories/s3/S3Service.java | 5 +- .../s3/S3BlobContainerRetriesTests.java | 5 +- plugins/transport-nio/build.gradle | 11 -- .../SecureNioHttpServerTransportTests.java | 15 +- .../src/test/resources/README.txt | 14 -- .../src/test/resources/certificate.crt | 22 --- .../src/test/resources/certificate.key | 28 --- plugins/transport-reactor-netty4/build.gradle | 11 -- ...ReactorNetty4HttpServerTransportTests.java | 16 +- .../src/test/resources/README.txt | 14 -- .../src/test/resources/certificate.crt | 22 --- .../src/test/resources/certificate.key | 28 --- server/licenses/bcpkix-fips-2.0.7.jar.sha1 | 1 + .../org/opensearch/bootstrap/Bootstrap.java | 3 + .../bootstrap/SecureRandomInitializer.java | 46 +++++ .../bootstrap/SecurityProviderManager.java | 48 +++++ .../org/opensearch/common/Randomness.java | 17 -- .../common/settings/KeyStoreWrapper.java | 16 +- .../org/opensearch/bootstrap/security.policy | 6 +- .../SecureRandomInitializerTests.java | 62 +++++++ .../SecurityProviderManagerTests.java | 171 ++++++++++++++++++ .../org/opensearch/bootstrap/test.policy | 1 + .../resources/provision/kdc.conf.template | 7 +- .../resources/provision/krb5.conf.template | 13 +- .../bootstrap/BootstrapForTesting.java | 19 ++ .../org/opensearch/test/KeyStoreUtils.java | 70 +++++++ 42 files changed, 535 insertions(+), 251 deletions(-) create mode 100644 libs/common/licenses/bcpkix-fips-2.0.7.jar.sha1 create mode 100644 libs/common/licenses/bouncycastle-LICENSE.txt create mode 100644 libs/common/licenses/bouncycastle-NOTICE.txt delete mode 100644 plugins/transport-nio/src/test/resources/README.txt delete mode 100644 plugins/transport-nio/src/test/resources/certificate.crt delete mode 100644 plugins/transport-nio/src/test/resources/certificate.key delete mode 100644 plugins/transport-reactor-netty4/src/test/resources/README.txt delete mode 100644 plugins/transport-reactor-netty4/src/test/resources/certificate.crt delete mode 100644 plugins/transport-reactor-netty4/src/test/resources/certificate.key create mode 100644 server/licenses/bcpkix-fips-2.0.7.jar.sha1 create mode 100644 server/src/main/java/org/opensearch/bootstrap/SecureRandomInitializer.java create mode 100644 server/src/main/java/org/opensearch/bootstrap/SecurityProviderManager.java create mode 100644 server/src/test/java/org/opensearch/bootstrap/SecureRandomInitializerTests.java create mode 100644 server/src/test/java/org/opensearch/bootstrap/SecurityProviderManagerTests.java create mode 100644 test/framework/src/main/java/org/opensearch/test/KeyStoreUtils.java diff --git a/buildSrc/src/main/java/org/opensearch/gradle/http/WaitForHttpResource.java b/buildSrc/src/main/java/org/opensearch/gradle/http/WaitForHttpResource.java index 54c544a299b84..62f2621be7924 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/http/WaitForHttpResource.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/http/WaitForHttpResource.java @@ -32,8 +32,10 @@ package org.opensearch.gradle.http; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; +import org.gradle.internal.impldep.com.jcraft.jsch.annotations.SuppressForbiddenApi; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; @@ -51,7 +53,6 @@ import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; -import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.Arrays; @@ -216,7 +217,7 @@ KeyStore buildTrustStore() throws GeneralSecurityException, IOException { } private KeyStore buildTrustStoreFromFile() throws GeneralSecurityException, IOException { - KeyStore keyStore = KeyStore.getInstance(trustStoreFile.getName().endsWith(".jks") ? "JKS" : "PKCS12"); + var keyStore = getKeyStoreInstance(trustStoreFile.getName().endsWith(".jks") ? "JKS" : "PKCS12"); try (InputStream input = new FileInputStream(trustStoreFile)) { keyStore.load(input, trustStorePassword == null ? null : trustStorePassword.toCharArray()); } @@ -224,7 +225,7 @@ private KeyStore buildTrustStoreFromFile() throws GeneralSecurityException, IOEx } private KeyStore buildTrustStoreFromCA() throws GeneralSecurityException, IOException { - final KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType()); + var store = getKeyStoreInstance(KeyStore.getDefaultType()); store.load(null, null); final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); int counter = 0; @@ -239,12 +240,17 @@ private KeyStore buildTrustStoreFromCA() throws GeneralSecurityException, IOExce return store; } + @SuppressForbiddenApi("runs exclusively in test-context without KeyStoreFactory on classpath.") + private KeyStore getKeyStoreInstance(String type) throws KeyStoreException { + return KeyStore.getInstance(type); + } + private SSLContext createSslContext(KeyStore trustStore) throws GeneralSecurityException { checkForTrustEntry(trustStore); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(new KeyManager[0], tmf.getTrustManagers(), new SecureRandom()); + sslContext.init(new KeyManager[0], tmf.getTrustManagers(), CryptoServicesRegistrar.getSecureRandom()); return sslContext; } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java index 7d98785acf3b5..5c317682b764a 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java @@ -548,7 +548,7 @@ public synchronized void start() { logToProcessStdout("Creating opensearch keystore with password set to [" + keystorePassword + "]"); if (keystorePassword.length() > 0) { - runOpenSearchBinScriptWithInput(keystorePassword + "\n" + keystorePassword, "opensearch-keystore", "create", "-p"); + runOpenSearchBinScriptWithInput(keystorePassword + "\n" + keystorePassword + "\n", "opensearch-keystore", "create", "-p"); } else { runOpenSearchBinScript("opensearch-keystore", "-v", "create"); } diff --git a/buildSrc/src/main/resources/forbidden/jdk-signatures.txt b/buildSrc/src/main/resources/forbidden/jdk-signatures.txt index b2fd479dce5ff..646f9a474524a 100644 --- a/buildSrc/src/main/resources/forbidden/jdk-signatures.txt +++ b/buildSrc/src/main/resources/forbidden/jdk-signatures.txt @@ -37,6 +37,12 @@ java.nio.file.Path#toFile() java.nio.file.Files#createTempDirectory(java.lang.String,java.nio.file.attribute.FileAttribute[]) java.nio.file.Files#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute[]) +@defaultMessage Use org.opensearch.common.crypto.KeyStoreFactory instead of java.security.KeyStore +java.security.KeyStore#getInstance(java.lang.String) +java.security.KeyStore#getInstance(java.lang.String,java.lang.String) +java.security.KeyStore#getInstance(java.lang.String,java.security.Provider) +java.security.KeyStore#getInstance(java.io.File,char[]) + @defaultMessage Don't use java serialization - this can break BWC without noticing it java.io.ObjectOutputStream java.io.ObjectOutput diff --git a/distribution/src/config/fips_java.security b/distribution/src/config/fips_java.security index bf160a0055a59..2e5bf13cf8d0e 100644 --- a/distribution/src/config/fips_java.security +++ b/distribution/src/config/fips_java.security @@ -5,10 +5,6 @@ security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips security.provider.3=SUN security.provider.4=SunJGSS -securerandom.source=file:/dev/urandom -securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN -securerandom.drbg.config= - login.configuration.provider=sun.security.provider.ConfigFile policy.provider=sun.security.provider.PolicyFile policy.expandProperties=true diff --git a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java index 0224c9bba7b74..08ad09194442d 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java @@ -38,7 +38,7 @@ import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.NIOFSDirectory; -import org.opensearch.common.Randomness; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.opensearch.common.crypto.KeyStoreFactory; import org.opensearch.common.crypto.KeyStoreType; import org.opensearch.common.util.io.IOUtils; @@ -205,7 +205,7 @@ public void testFailWhenCannotConsumeSecretStream() throws Exception { try (IndexOutput indexOutput = directory.createOutput("opensearch.keystore", IOContext.DEFAULT)) { CodecUtil.writeHeader(indexOutput, "opensearch.keystore", 3); indexOutput.writeByte((byte) 0); // No password - SecureRandom random = Randomness.createSecure(); + SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); byte[] salt = new byte[64]; random.nextBytes(salt); byte[] iv = new byte[12]; @@ -233,7 +233,7 @@ public void testFailWhenCannotConsumeEncryptedBytesStream() throws Exception { try (IndexOutput indexOutput = directory.createOutput("opensearch.keystore", IOContext.DEFAULT)) { CodecUtil.writeHeader(indexOutput, "opensearch.keystore", 3); indexOutput.writeByte((byte) 0); // No password - SecureRandom random = Randomness.createSecure(); + SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); byte[] salt = new byte[64]; random.nextBytes(salt); byte[] iv = new byte[12]; @@ -262,7 +262,7 @@ public void testFailWhenSecretStreamNotConsumed() throws Exception { try (IndexOutput indexOutput = directory.createOutput("opensearch.keystore", IOContext.DEFAULT)) { CodecUtil.writeHeader(indexOutput, "opensearch.keystore", 3); indexOutput.writeByte((byte) 0); // No password - SecureRandom random = Randomness.createSecure(); + SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); byte[] salt = new byte[64]; random.nextBytes(salt); byte[] iv = new byte[12]; @@ -289,7 +289,7 @@ public void testFailWhenEncryptedBytesStreamIsNotConsumed() throws Exception { try (IndexOutput indexOutput = directory.createOutput("opensearch.keystore", IOContext.DEFAULT)) { CodecUtil.writeHeader(indexOutput, "opensearch.keystore", 3); indexOutput.writeByte((byte) 0); // No password - SecureRandom random = Randomness.createSecure(); + SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); byte[] salt = new byte[64]; random.nextBytes(salt); byte[] iv = new byte[12]; @@ -372,15 +372,15 @@ public void testIllegalSettingName() throws Exception { public void testFailLoadV1KeystoresInFipsJvm() throws Exception { assumeTrue("Test in FIPS JVM", inFipsJvm()); - Exception e = assertThrows(SecurityException.class, () -> generateV1()); - assertThat(e.getMessage(), containsString("Only PKCS_11, BCFKS keystores are allowed in FIPS JVM")); + Exception e = assertThrows(NoSuchProviderException.class, this::generateV1); + assertThat(e.getMessage(), containsString("no such provider: SunJCE")); } public void testFailLoadV2KeystoresInFipsJvm() throws Exception { assumeTrue("Test in FIPS JVM", inFipsJvm()); - Exception e = assertThrows(SecurityException.class, () -> generateV2()); - assertThat(e.getMessage(), containsString("Only PKCS_11, BCFKS keystores are allowed in FIPS JVM")); + Exception e = assertThrows(NoSuchProviderException.class, this::generateV2); + assertThat(e.getMessage(), containsString("no such provider: SunJCE")); } public void testBackcompatV1() throws Exception { diff --git a/libs/common/build.gradle b/libs/common/build.gradle index 6941298c1b9e8..d641b5916728c 100644 --- a/libs/common/build.gradle +++ b/libs/common/build.gradle @@ -20,8 +20,10 @@ base { dependencies { // This dependency is used only by :libs:core for null-checking interop with other tools compileOnly "com.google.code.findbugs:jsr305:3.0.2" + compileOnly "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}" compileOnly "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}" + api "org.bouncycastle:bcpkix-fips:${versions.bouncycastle_pkix}" /******* * !!!! NO THIRD PARTY DEPENDENCIES !!!! @@ -46,6 +48,10 @@ tasks.named('forbiddenApisMain').configure { replaceSignatureFiles 'jdk-signatures' } +tasks.named("dependencyLicenses").configure { + mapping from: /bc.*/, to: 'bouncycastle' +} + // Add support for incubator modules on supported Java versions. if (BuildParams.runtimeJavaVersion >= JavaVersion.VERSION_20) { sourceSets { diff --git a/libs/common/licenses/bcpkix-fips-2.0.7.jar.sha1 b/libs/common/licenses/bcpkix-fips-2.0.7.jar.sha1 new file mode 100644 index 0000000000000..ff463e602ad88 --- /dev/null +++ b/libs/common/licenses/bcpkix-fips-2.0.7.jar.sha1 @@ -0,0 +1 @@ +01eea0f325315ca6295b0a6926ff862d8001cdf9 diff --git a/libs/common/licenses/bouncycastle-LICENSE.txt b/libs/common/licenses/bouncycastle-LICENSE.txt new file mode 100644 index 0000000000000..5c7c14696849d --- /dev/null +++ b/libs/common/licenses/bouncycastle-LICENSE.txt @@ -0,0 +1,14 @@ +Copyright (c) 2000 - 2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libs/common/licenses/bouncycastle-NOTICE.txt b/libs/common/licenses/bouncycastle-NOTICE.txt new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/libs/common/licenses/bouncycastle-NOTICE.txt @@ -0,0 +1 @@ + diff --git a/libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java b/libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java index 14844293b3274..990f6968a57a2 100644 --- a/libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java +++ b/libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java @@ -32,6 +32,8 @@ package org.opensearch.common; +import org.bouncycastle.crypto.CryptoServicesRegistrar; + import java.security.SecureRandom; /** @@ -41,5 +43,5 @@ */ class SecureRandomHolder { // class loading is atomic - this is a lazy & safe singleton to be used by this package - public static final SecureRandom INSTANCE = new SecureRandom(); + public static final SecureRandom INSTANCE = CryptoServicesRegistrar.getSecureRandom(); } diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/KeyStoreFactory.java b/libs/common/src/main/java/org/opensearch/common/crypto/KeyStoreFactory.java index 4cd6e9922ab27..a07c4b64b1a36 100644 --- a/libs/common/src/main/java/org/opensearch/common/crypto/KeyStoreFactory.java +++ b/libs/common/src/main/java/org/opensearch/common/crypto/KeyStoreFactory.java @@ -9,6 +9,7 @@ package org.opensearch.common.crypto; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.opensearch.common.SuppressForbidden; import java.security.KeyStore; import java.security.KeyStoreException; @@ -55,7 +56,11 @@ public static KeyStore getInstance(KeyStoreType type, String provider) { } provider = FIPS_PROVIDER; } + return get(type, provider); + } + @SuppressForbidden(reason = "centralized instantiation of a KeyStore") + private static KeyStore get(KeyStoreType type, String provider) { try { if (provider == null) { return KeyStore.getInstance(type.getJcaName()); diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index cdaf8350055f0..3c10ac6ccb96b 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -147,17 +147,6 @@ thirdPartyAudit { 'io.netty.internal.tcnative.SSLContext', 'io.netty.internal.tcnative.SSLPrivateKeyMethod', - // from io.netty.handler.ssl.util.BouncyCastleSelfSignedCertGenerator (netty) - 'org.bouncycastle.cert.X509v3CertificateBuilder', - 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', - 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', - 'org.bouncycastle.openssl.PEMEncryptedKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', - 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', - 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', - // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', 'org.eclipse.jetty.npn.NextProtoNego$ServerProvider', diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java index c363bc6eb43f8..c74dbb22bc21e 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java @@ -35,6 +35,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase; import org.opensearch.Version; +import org.opensearch.bootstrap.SecureRandomInitializer; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; import org.opensearch.index.IndexSettings; @@ -47,6 +48,10 @@ public class IcuAnalyzerTests extends BaseTokenStreamTestCase { + static { + SecureRandomInitializer.init(); + } + public void testMixedAlphabetTokenization() throws IOException { Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT).build(); diff --git a/plugins/repository-azure/build.gradle b/plugins/repository-azure/build.gradle index a0e01aec9f654..dd12e0fd6ce73 100644 --- a/plugins/repository-azure/build.gradle +++ b/plugins/repository-azure/build.gradle @@ -214,13 +214,6 @@ thirdPartyAudit { // Worth nothing that, the latest dependency "net.shibboleth.utilities:java-support:8.0.0" has many vulnerabilities. // Hence ignored. 'net.shibboleth.utilities.java.support.xml.SerializeSupport', - 'org.bouncycastle.cert.X509CertificateHolder', - 'org.bouncycastle.cert.jcajce.JcaX509CertificateHolder', - 'org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder', - 'org.bouncycastle.openssl.PEMKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', 'org.cryptomator.siv.SivMode', 'org.opensaml.core.config.InitializationException', 'org.opensaml.core.config.InitializationService', @@ -311,6 +304,10 @@ Map expansions = [ 'base_path': azureBasePath + "_integration_tests" ] +tasks.withType(Test).configureEach { + onlyIf { BuildParams.inFipsJvm == false } +} + processYamlRestTestResources { inputs.properties(expansions) MavenFilteringHack.filter(it, expansions) diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index 9b23b17bea2e6..cdb9fe15e831c 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -486,17 +486,6 @@ thirdPartyAudit { 'net.jpountz.xxhash.XXHash32', 'net.jpountz.xxhash.XXHashFactory', - // from io.netty.handler.ssl.util.BouncyCastleSelfSignedCertGenerator (netty) - 'org.bouncycastle.cert.X509v3CertificateBuilder', - 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', - 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', - 'org.bouncycastle.openssl.PEMEncryptedKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', - 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', - 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', - 'org.conscrypt.AllocatedBuffer', 'org.conscrypt.BufferAllocator', 'org.conscrypt.Conscrypt', diff --git a/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java b/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java index 944de326d144c..b56aecafba1f8 100644 --- a/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java +++ b/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java @@ -143,7 +143,7 @@ protected HttpHandler createErroneousHttpHandler(final HttpHandler delegate) { protected Settings nodeSettings(int nodeOrdinal) { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString(S3ClientSettings.ACCESS_KEY_SETTING.getConcreteSettingForNamespace("test").getKey(), "access"); - secureSettings.setString(S3ClientSettings.SECRET_KEY_SETTING.getConcreteSettingForNamespace("test").getKey(), "secret"); + secureSettings.setString(S3ClientSettings.SECRET_KEY_SETTING.getConcreteSettingForNamespace("test").getKey(), "secret_password"); final Settings.Builder builder = Settings.builder() .put(ThreadPool.ESTIMATED_TIME_INTERVAL_SETTING.getKey(), 0) // We have tests that verify an exact wait time diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java index 3d5e121778ba9..e17234f6921e0 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java @@ -64,6 +64,7 @@ import org.apache.http.protocol.HttpContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.common.Nullable; import org.opensearch.common.SuppressForbidden; @@ -88,7 +89,6 @@ import java.nio.file.Path; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.time.Duration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -341,7 +341,8 @@ private static SSLConnectionSocketFactory createSocksSslConnectionSocketFactory( // This part was taken from AWS settings try { final SSLContext sslCtx = SSLContext.getInstance("TLS"); - sslCtx.init(SystemPropertyTlsKeyManagersProvider.create().keyManagers(), null, new SecureRandom()); + sslCtx.init(SystemPropertyTlsKeyManagersProvider.create().keyManagers(), null, CryptoServicesRegistrar.getSecureRandom()); + return new SdkTlsSocketFactory(sslCtx, new DefaultHostnameVerifier()) { @Override public Socket createSocket(final HttpContext ctx) throws IOException { diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java index 96ef28d24c14f..dbecc7c7eb417 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java @@ -222,7 +222,10 @@ protected AsyncMultiStreamBlobContainer createBlobContainer( final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString(S3ClientSettings.ACCESS_KEY_SETTING.getConcreteSettingForNamespace(clientName).getKey(), "access"); - secureSettings.setString(S3ClientSettings.SECRET_KEY_SETTING.getConcreteSettingForNamespace(clientName).getKey(), "secret"); + secureSettings.setString( + S3ClientSettings.SECRET_KEY_SETTING.getConcreteSettingForNamespace(clientName).getKey(), + "secret_password" + ); clientSettings.setSecureSettings(secureSettings); service.refreshAndClearCache(S3ClientSettings.load(clientSettings.build(), configPath())); asyncService.refreshAndClearCache(S3ClientSettings.load(clientSettings.build(), configPath())); diff --git a/plugins/transport-nio/build.gradle b/plugins/transport-nio/build.gradle index 7132c97864238..262818eb7d10b 100644 --- a/plugins/transport-nio/build.gradle +++ b/plugins/transport-nio/build.gradle @@ -74,17 +74,6 @@ thirdPartyAudit { 'org.apache.log4j.Level', 'org.apache.log4j.Logger', - // from io.netty.handler.ssl.util.BouncyCastleSelfSignedCertGenerator (netty) - 'org.bouncycastle.cert.X509v3CertificateBuilder', - 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', - 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', - 'org.bouncycastle.openssl.PEMEncryptedKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', - 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', - 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', - // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', 'org.eclipse.jetty.npn.NextProtoNego$ServerProvider', diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/ssl/SecureNioHttpServerTransportTests.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/ssl/SecureNioHttpServerTransportTests.java index 1adfe0370344c..636d8deaf7e07 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/ssl/SecureNioHttpServerTransportTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/ssl/SecureNioHttpServerTransportTests.java @@ -37,6 +37,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; import org.opensearch.telemetry.tracing.noop.NoopTracer; +import org.opensearch.test.KeyStoreUtils; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.rest.FakeRestRequest; import org.opensearch.threadpool.TestThreadPool; @@ -45,6 +46,7 @@ import org.junit.After; import org.junit.Before; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; @@ -77,6 +79,7 @@ import static org.opensearch.core.rest.RestStatus.OK; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_ORIGIN; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ENABLED; +import static org.opensearch.test.KeyStoreUtils.KEYSTORE_PASSWORD; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.Matchers.equalTo; @@ -111,12 +114,14 @@ public Optional buildHttpServerExceptionHandler(Setti @Override public Optional buildSecureHttpServerEngine(Settings settings, HttpServerTransport transport) throws SSLException { try { - SSLEngine engine = SslContextBuilder.forServer( - SecureNioHttpServerTransportTests.class.getResourceAsStream("/certificate.crt"), - SecureNioHttpServerTransportTests.class.getResourceAsStream("/certificate.key") - ).trustManager(InsecureTrustManagerFactory.INSTANCE).build().newEngine(UnpooledByteBufAllocator.DEFAULT); + var keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); + keyManagerFactory.init(KeyStoreUtils.createServerKeyStore(), KEYSTORE_PASSWORD); + SSLEngine engine = SslContextBuilder.forServer(keyManagerFactory) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build() + .newEngine(UnpooledByteBufAllocator.DEFAULT); return Optional.of(engine); - } catch (final IOException ex) { + } catch (Exception ex) { throw new SSLException(ex); } } diff --git a/plugins/transport-nio/src/test/resources/README.txt b/plugins/transport-nio/src/test/resources/README.txt deleted file mode 100644 index a4353cee45a97..0000000000000 --- a/plugins/transport-nio/src/test/resources/README.txt +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# -# This is README describes how the certificates in this directory were created. -# This file can also be executed as a script -# - -# 1. Create certificate key - -openssl req -x509 -sha256 -newkey rsa:2048 -keyout certificate.key -out certificate.crt -days 1024 -nodes - -# 2. Export the certificate in pkcs12 format - -openssl pkcs12 -export -in certificate.crt -inkey certificate.key -out server.p12 -name netty4-secure -password pass:password - diff --git a/plugins/transport-nio/src/test/resources/certificate.crt b/plugins/transport-nio/src/test/resources/certificate.crt deleted file mode 100644 index 54c78fdbcf6de..0000000000000 --- a/plugins/transport-nio/src/test/resources/certificate.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIUddAawr5zygcd+Dcn9WVDpO4BJ7YwDQYJKoZIhvcNAQEL -BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X -DTI0MDMxNDE5NDQzOVoXDTI3MDEwMjE5NDQzOVowWTELMAkGA1UEBhMCQVUxEzAR -BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 -IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAzjOKkg6Iba5zfZ8b/RYw+PGmGEfbdGuuF10Wz4Jmx/Nk4VfDLxdh -TW8VllUL2JD7uPkjABj7pW3awAbvIJ+VGbKqfBr1Nsz0mPPzhT8cfuMH/FDZgQs3 -4HuqDKr0LfC1Kw5E3WF0GVMBDNu0U+nKoeqySeYjGdxDnd3W4cqK5AnUxL0RnIny -Bw7ZuhcU55XndH/Xauro/2EpvJduDsWMdqt7ZfIf1TOmaiQHK+82yb/drVaJbczK -uTpn1Kv2bnzkQEckgq+z1dLNOOyvP2xf+nsziw5ilJe92e5GJOUJYFAlEgUAGpfD -dv6j/gTRYvdJCJItOQEQtektNCAZsoc0wwIDAQABo1MwUTAdBgNVHQ4EFgQUzHts -wIt+zhB/R4U4Do2P6rr0YhkwHwYDVR0jBBgwFoAUzHtswIt+zhB/R4U4Do2P6rr0 -YhkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAveh870jJX7vt -oLCrdugsyo79pR4f7Nr1kUy3jJrfoaoUmrjiiiHWgT22fGwp7j1GZF2mVfo8YVaK -63YNn5gB2NNZhguPOFC4AdvHRYOKRBOaOvWK8oq7BcJ//18JYI/pPnpgkYvJjqv4 -gFKaZX9qWtujHpAmKiVGs7pwYGNXfixPHRNV4owcfHMIH5dhbbqT49j94xVpjbXs -OymKtFl4kpCE/0LzKFrFcuu55Am1VLBHx2cPpHLOipgUcF5BHFlQ8AXiCMOwfPAw -d22mLB6Gt1oVEpyvQHYd3e04FetEXQ9E8T+NKWZx/8Ucf+IWBYmZBRxch6O83xgk -bAbGzqkbzQ== ------END CERTIFICATE----- diff --git a/plugins/transport-nio/src/test/resources/certificate.key b/plugins/transport-nio/src/test/resources/certificate.key deleted file mode 100644 index 228350180935d..0000000000000 --- a/plugins/transport-nio/src/test/resources/certificate.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOM4qSDohtrnN9 -nxv9FjD48aYYR9t0a64XXRbPgmbH82ThV8MvF2FNbxWWVQvYkPu4+SMAGPulbdrA -Bu8gn5UZsqp8GvU2zPSY8/OFPxx+4wf8UNmBCzfge6oMqvQt8LUrDkTdYXQZUwEM -27RT6cqh6rJJ5iMZ3EOd3dbhyorkCdTEvRGcifIHDtm6FxTnled0f9dq6uj/YSm8 -l24OxYx2q3tl8h/VM6ZqJAcr7zbJv92tVoltzMq5OmfUq/ZufORARySCr7PV0s04 -7K8/bF/6ezOLDmKUl73Z7kYk5QlgUCUSBQAal8N2/qP+BNFi90kIki05ARC16S00 -IBmyhzTDAgMBAAECggEAVOdiElvLjyX6xeoC00YU6hxOIMdNtHU2HMamwtDV01UD -38mMQ9KjrQelYt4n34drLrHe2IZw75/5J4JzagJrmUY47psHBwaDXItuZRokeJaw -zhLYTEs7OcKRtV+a5WOspUrdzi33aQoFb67zZG3qkpsZyFXrdBV+/fy/Iv+MCvLH -xR0jQ5mzE3cw20R7S4nddChBA/y8oKGOo6QRf2SznC1jL/+yolHvJPEn1v8AUxYm -BMPHxj1O0c4M4IxnJQ3Y5Jy9OaFMyMsFlF1hVhc/3LDDxDyOuBsVsFDicojyrRea -GKngIke0yezy7Wo4NUcp8YQhafonpWVsSJJdOUotcQKBgQD0rihFBXVtcG1d/Vy7 -FvLHrmccD56JNV744LSn2CDM7W1IulNbDUZINdCFqL91u5LpxozeE1FPY1nhwncJ -N7V7XYCaSLCuV1YJzRmUCjnzk2RyopGpzWog3f9uUFGgrk1HGbNAv99k/REya6Iu -IRSkuQhaJOj3bRXzonh0K4GjewKBgQDXvamtCioOUMSP8vq919YMkBw7F+z/fr0p -pamO8HL9eewAUg6N92JQ9kobSo/GptdmdHIjs8LqnS5C3H13GX5Qlf5GskOlCpla -V55ElaSp0gvKwWE168U7gQH4etPQAXXJrOGFaGbPj9W81hTUud7HVE88KYdfWTBo -I7TuE25tWQKBgBRjcr2Vn9xXsvVTCGgamG5lLPhcoNREGz7X0pXt34XT/vhBdnKu -331i5pZMom+YCrzqK5DRwUPBPpseTjb5amj2OKIijn5ojqXQbmI0m/GdBZC71TF2 -CXLlrMQvcy3VeGEFVjd+BYpvwAAYkfIQFZ1IQdbpHnSHpX2guzLK8UmDAoGBANUy -PIcf0EetUVHfkCIjNQfdMcjD8BTcLhsF9vWmcDxFTA9VB8ULf0D64mjt2f85yQsa -b+EQN8KZ6alxMxuLOeRxFYLPj0F9o+Y/R8wHBV48kCKhz2r1v0b6SfQ/jSm1B61x -BrxLW64qOdIOzS8bLyhUDKkrcPesr8V548aRtUKhAoGBAKlNJFd8BCGKD9Td+3dE -oP1iHTX5XZ+cQIqL0e+GMQlK4HnQP566DFZU5/GHNNAfmyxd5iSRwhTqPMHRAmOb -pqQwsyufx0dFeIBxeSO3Z6jW5h2sl4nBipZpw9bzv6EBL1xRr0SfMNZzdnf4JFzc -0htGo/VO93Z2pv8w7uGUz1nN ------END PRIVATE KEY----- diff --git a/plugins/transport-reactor-netty4/build.gradle b/plugins/transport-reactor-netty4/build.gradle index 1e76d1a29efc1..197c77615c7f1 100644 --- a/plugins/transport-reactor-netty4/build.gradle +++ b/plugins/transport-reactor-netty4/build.gradle @@ -110,17 +110,6 @@ thirdPartyAudit { 'io.netty.internal.tcnative.SSLContext', 'io.netty.internal.tcnative.SSLPrivateKeyMethod', - // from io.netty.handler.ssl.util.BouncyCastleSelfSignedCertGenerator (netty) - 'org.bouncycastle.cert.X509v3CertificateBuilder', - 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', - 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', - 'org.bouncycastle.openssl.PEMEncryptedKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', - 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', - 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', - // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', 'org.eclipse.jetty.npn.NextProtoNego$ServerProvider', diff --git a/plugins/transport-reactor-netty4/src/test/java/org/opensearch/http/reactor/netty4/ssl/SecureReactorNetty4HttpServerTransportTests.java b/plugins/transport-reactor-netty4/src/test/java/org/opensearch/http/reactor/netty4/ssl/SecureReactorNetty4HttpServerTransportTests.java index ac7687d551766..06d272e350a76 100644 --- a/plugins/transport-reactor-netty4/src/test/java/org/opensearch/http/reactor/netty4/ssl/SecureReactorNetty4HttpServerTransportTests.java +++ b/plugins/transport-reactor-netty4/src/test/java/org/opensearch/http/reactor/netty4/ssl/SecureReactorNetty4HttpServerTransportTests.java @@ -35,6 +35,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; import org.opensearch.telemetry.tracing.noop.NoopTracer; +import org.opensearch.test.KeyStoreUtils; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.rest.FakeRestRequest; import org.opensearch.threadpool.TestThreadPool; @@ -44,10 +45,10 @@ import org.junit.After; import org.junit.Before; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; -import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Optional; @@ -85,6 +86,7 @@ import static org.opensearch.core.rest.RestStatus.OK; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_ORIGIN; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ENABLED; +import static org.opensearch.test.KeyStoreUtils.KEYSTORE_PASSWORD; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -115,12 +117,14 @@ public Optional buildHttpServerExceptionHandler(Setti @Override public Optional buildSecureHttpServerEngine(Settings settings, HttpServerTransport transport) throws SSLException { try { - SSLEngine engine = SslContextBuilder.forServer( - SecureReactorNetty4HttpServerTransportTests.class.getResourceAsStream("/certificate.crt"), - SecureReactorNetty4HttpServerTransportTests.class.getResourceAsStream("/certificate.key") - ).trustManager(InsecureTrustManagerFactory.INSTANCE).build().newEngine(NettyAllocator.getAllocator()); + var keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); + keyManagerFactory.init(KeyStoreUtils.createServerKeyStore(), KEYSTORE_PASSWORD); + SSLEngine engine = SslContextBuilder.forServer(keyManagerFactory) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build() + .newEngine(NettyAllocator.getAllocator()); return Optional.of(engine); - } catch (final IOException ex) { + } catch (final Exception ex) { throw new SSLException(ex); } } diff --git a/plugins/transport-reactor-netty4/src/test/resources/README.txt b/plugins/transport-reactor-netty4/src/test/resources/README.txt deleted file mode 100644 index a4353cee45a97..0000000000000 --- a/plugins/transport-reactor-netty4/src/test/resources/README.txt +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# -# This is README describes how the certificates in this directory were created. -# This file can also be executed as a script -# - -# 1. Create certificate key - -openssl req -x509 -sha256 -newkey rsa:2048 -keyout certificate.key -out certificate.crt -days 1024 -nodes - -# 2. Export the certificate in pkcs12 format - -openssl pkcs12 -export -in certificate.crt -inkey certificate.key -out server.p12 -name netty4-secure -password pass:password - diff --git a/plugins/transport-reactor-netty4/src/test/resources/certificate.crt b/plugins/transport-reactor-netty4/src/test/resources/certificate.crt deleted file mode 100644 index 54c78fdbcf6de..0000000000000 --- a/plugins/transport-reactor-netty4/src/test/resources/certificate.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIUddAawr5zygcd+Dcn9WVDpO4BJ7YwDQYJKoZIhvcNAQEL -BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X -DTI0MDMxNDE5NDQzOVoXDTI3MDEwMjE5NDQzOVowWTELMAkGA1UEBhMCQVUxEzAR -BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 -IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAzjOKkg6Iba5zfZ8b/RYw+PGmGEfbdGuuF10Wz4Jmx/Nk4VfDLxdh -TW8VllUL2JD7uPkjABj7pW3awAbvIJ+VGbKqfBr1Nsz0mPPzhT8cfuMH/FDZgQs3 -4HuqDKr0LfC1Kw5E3WF0GVMBDNu0U+nKoeqySeYjGdxDnd3W4cqK5AnUxL0RnIny -Bw7ZuhcU55XndH/Xauro/2EpvJduDsWMdqt7ZfIf1TOmaiQHK+82yb/drVaJbczK -uTpn1Kv2bnzkQEckgq+z1dLNOOyvP2xf+nsziw5ilJe92e5GJOUJYFAlEgUAGpfD -dv6j/gTRYvdJCJItOQEQtektNCAZsoc0wwIDAQABo1MwUTAdBgNVHQ4EFgQUzHts -wIt+zhB/R4U4Do2P6rr0YhkwHwYDVR0jBBgwFoAUzHtswIt+zhB/R4U4Do2P6rr0 -YhkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAveh870jJX7vt -oLCrdugsyo79pR4f7Nr1kUy3jJrfoaoUmrjiiiHWgT22fGwp7j1GZF2mVfo8YVaK -63YNn5gB2NNZhguPOFC4AdvHRYOKRBOaOvWK8oq7BcJ//18JYI/pPnpgkYvJjqv4 -gFKaZX9qWtujHpAmKiVGs7pwYGNXfixPHRNV4owcfHMIH5dhbbqT49j94xVpjbXs -OymKtFl4kpCE/0LzKFrFcuu55Am1VLBHx2cPpHLOipgUcF5BHFlQ8AXiCMOwfPAw -d22mLB6Gt1oVEpyvQHYd3e04FetEXQ9E8T+NKWZx/8Ucf+IWBYmZBRxch6O83xgk -bAbGzqkbzQ== ------END CERTIFICATE----- diff --git a/plugins/transport-reactor-netty4/src/test/resources/certificate.key b/plugins/transport-reactor-netty4/src/test/resources/certificate.key deleted file mode 100644 index 228350180935d..0000000000000 --- a/plugins/transport-reactor-netty4/src/test/resources/certificate.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOM4qSDohtrnN9 -nxv9FjD48aYYR9t0a64XXRbPgmbH82ThV8MvF2FNbxWWVQvYkPu4+SMAGPulbdrA -Bu8gn5UZsqp8GvU2zPSY8/OFPxx+4wf8UNmBCzfge6oMqvQt8LUrDkTdYXQZUwEM -27RT6cqh6rJJ5iMZ3EOd3dbhyorkCdTEvRGcifIHDtm6FxTnled0f9dq6uj/YSm8 -l24OxYx2q3tl8h/VM6ZqJAcr7zbJv92tVoltzMq5OmfUq/ZufORARySCr7PV0s04 -7K8/bF/6ezOLDmKUl73Z7kYk5QlgUCUSBQAal8N2/qP+BNFi90kIki05ARC16S00 -IBmyhzTDAgMBAAECggEAVOdiElvLjyX6xeoC00YU6hxOIMdNtHU2HMamwtDV01UD -38mMQ9KjrQelYt4n34drLrHe2IZw75/5J4JzagJrmUY47psHBwaDXItuZRokeJaw -zhLYTEs7OcKRtV+a5WOspUrdzi33aQoFb67zZG3qkpsZyFXrdBV+/fy/Iv+MCvLH -xR0jQ5mzE3cw20R7S4nddChBA/y8oKGOo6QRf2SznC1jL/+yolHvJPEn1v8AUxYm -BMPHxj1O0c4M4IxnJQ3Y5Jy9OaFMyMsFlF1hVhc/3LDDxDyOuBsVsFDicojyrRea -GKngIke0yezy7Wo4NUcp8YQhafonpWVsSJJdOUotcQKBgQD0rihFBXVtcG1d/Vy7 -FvLHrmccD56JNV744LSn2CDM7W1IulNbDUZINdCFqL91u5LpxozeE1FPY1nhwncJ -N7V7XYCaSLCuV1YJzRmUCjnzk2RyopGpzWog3f9uUFGgrk1HGbNAv99k/REya6Iu -IRSkuQhaJOj3bRXzonh0K4GjewKBgQDXvamtCioOUMSP8vq919YMkBw7F+z/fr0p -pamO8HL9eewAUg6N92JQ9kobSo/GptdmdHIjs8LqnS5C3H13GX5Qlf5GskOlCpla -V55ElaSp0gvKwWE168U7gQH4etPQAXXJrOGFaGbPj9W81hTUud7HVE88KYdfWTBo -I7TuE25tWQKBgBRjcr2Vn9xXsvVTCGgamG5lLPhcoNREGz7X0pXt34XT/vhBdnKu -331i5pZMom+YCrzqK5DRwUPBPpseTjb5amj2OKIijn5ojqXQbmI0m/GdBZC71TF2 -CXLlrMQvcy3VeGEFVjd+BYpvwAAYkfIQFZ1IQdbpHnSHpX2guzLK8UmDAoGBANUy -PIcf0EetUVHfkCIjNQfdMcjD8BTcLhsF9vWmcDxFTA9VB8ULf0D64mjt2f85yQsa -b+EQN8KZ6alxMxuLOeRxFYLPj0F9o+Y/R8wHBV48kCKhz2r1v0b6SfQ/jSm1B61x -BrxLW64qOdIOzS8bLyhUDKkrcPesr8V548aRtUKhAoGBAKlNJFd8BCGKD9Td+3dE -oP1iHTX5XZ+cQIqL0e+GMQlK4HnQP566DFZU5/GHNNAfmyxd5iSRwhTqPMHRAmOb -pqQwsyufx0dFeIBxeSO3Z6jW5h2sl4nBipZpw9bzv6EBL1xRr0SfMNZzdnf4JFzc -0htGo/VO93Z2pv8w7uGUz1nN ------END PRIVATE KEY----- diff --git a/server/licenses/bcpkix-fips-2.0.7.jar.sha1 b/server/licenses/bcpkix-fips-2.0.7.jar.sha1 new file mode 100644 index 0000000000000..5df930b54fe44 --- /dev/null +++ b/server/licenses/bcpkix-fips-2.0.7.jar.sha1 @@ -0,0 +1 @@ +01eea0f325315ca6295b0a6926ff862d8001cdf9 \ No newline at end of file diff --git a/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java b/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java index e661ad17ac89c..f7895bb11b4ed 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java @@ -195,9 +195,12 @@ private void setup(boolean addShutdownHook, Environment environment) throws Boot BootstrapSettings.CTRLHANDLER_SETTING.get(settings) ); + SecureRandomInitializer.init(); + var cryptoStandard = System.getenv("OPENSEARCH_CRYPTO_STANDARD"); if (cryptoStandard != null && cryptoStandard.equals("FIPS-140-3")) { LogManager.getLogger(Bootstrap.class).info("running in FIPS-140-3 mode"); + SecurityProviderManager.excludeSunJCE(); } // initialize probes before the security manager is installed diff --git a/server/src/main/java/org/opensearch/bootstrap/SecureRandomInitializer.java b/server/src/main/java/org/opensearch/bootstrap/SecureRandomInitializer.java new file mode 100644 index 0000000000000..633694556847c --- /dev/null +++ b/server/src/main/java/org/opensearch/bootstrap/SecureRandomInitializer.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.bootstrap; + +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.fips.FipsDRBG; +import org.bouncycastle.crypto.util.BasicEntropySourceProvider; + +import java.security.GeneralSecurityException; +import java.security.SecureRandom; + +/** + * Instantiates {@link SecureRandom} + */ +public class SecureRandomInitializer { + + private SecureRandomInitializer() {} + + /** + * Instantiates a new {@link SecureRandom} if it is not already set. The specific implementation used depends on whether the JVM + * is running in a FIPS-approved mode or not. An instance of {@link SecureRandom} can be obtained from + * {@link CryptoServicesRegistrar#getSecureRandom()} + */ + public static void init() { + CryptoServicesRegistrar.setSecureRandom(CryptoServicesRegistrar.getSecureRandomIfSet(SecureRandomInitializer::getSecureRandom)); + } + + private static SecureRandom getSecureRandom() { + try { + if (CryptoServicesRegistrar.isInApprovedOnlyMode()) { + var entropySource = SecureRandom.getInstance("DEFAULT", "BCFIPS"); + return FipsDRBG.SHA512_HMAC.fromEntropySource(new BasicEntropySourceProvider(entropySource, true)).build(null, true); + } + return SecureRandom.getInstanceStrong(); + } catch (GeneralSecurityException e) { + throw new SecurityException("Failed to instantiate SecureRandom: " + e.getMessage(), e); + } + } + +} diff --git a/server/src/main/java/org/opensearch/bootstrap/SecurityProviderManager.java b/server/src/main/java/org/opensearch/bootstrap/SecurityProviderManager.java new file mode 100644 index 0000000000000..0b53d48b88f55 --- /dev/null +++ b/server/src/main/java/org/opensearch/bootstrap/SecurityProviderManager.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.bootstrap; + +import java.security.Security; + +/** + * Provides additional control over declared security providers in 'java.security' file. + */ +public class SecurityProviderManager { + + public static final String SUN_JCE = "SunJCE"; + + private SecurityProviderManager() { + // singleton constructor + } + + /** + * Removes the SunJCE provider from the list of installed security providers. This method is intended to be used when running + * in a FIPS JVM and when the security file specifies additional configuration, instead of a complete replacement. + */ + public static void excludeSunJCE() { + Security.removeProvider(SUN_JCE); + } + + /** + * Returns the position at which the provider is found by its name, otherwise returns -1. + * Provider's position starts by 1 and will not always represent the configured value at 'java.security' file. + */ + public static int getPosition(String providerName) { + var provider = java.security.Security.getProvider(providerName); + if (provider != null) { + var providers = java.security.Security.getProviders(); + for (int i = 0; i < providers.length; i++) { + if (providers[i].getName().equals(providerName)) { + return i + 1; // provider positions starts at 1 + } + } + } + return -1; + } +} diff --git a/server/src/main/java/org/opensearch/common/Randomness.java b/server/src/main/java/org/opensearch/common/Randomness.java index 221bc95c41f31..479d3b195bee6 100644 --- a/server/src/main/java/org/opensearch/common/Randomness.java +++ b/server/src/main/java/org/opensearch/common/Randomness.java @@ -36,7 +36,6 @@ import org.opensearch.common.settings.Settings; import java.lang.reflect.Method; -import java.security.SecureRandom; import java.util.Collections; import java.util.List; import java.util.Random; @@ -125,22 +124,6 @@ public static Random get() { } } - /** - * Provides a secure source of randomness. - *

- * This acts exactly similar to {@link #get()}, but returning a new {@link SecureRandom}. - */ - public static SecureRandom createSecure() { - if (currentMethod != null && getRandomMethod != null) { - // tests, so just use a seed from the non secure random - byte[] seed = new byte[16]; - get().nextBytes(seed); - return new SecureRandom(seed); - } else { - return new SecureRandom(); - } - } - @SuppressForbidden(reason = "ThreadLocalRandom is okay when not running tests") private static Random getWithoutSeed() { assert currentMethod == null && getRandomMethod == null : "running under tests but tried to create non-reproducible random"; diff --git a/server/src/main/java/org/opensearch/common/settings/KeyStoreWrapper.java b/server/src/main/java/org/opensearch/common/settings/KeyStoreWrapper.java index 8b54a98c3bf1d..967a8ff6682ac 100644 --- a/server/src/main/java/org/opensearch/common/settings/KeyStoreWrapper.java +++ b/server/src/main/java/org/opensearch/common/settings/KeyStoreWrapper.java @@ -41,9 +41,9 @@ import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.NIOFSDirectory; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.opensearch.bootstrap.SecureRandomInitializer; import org.opensearch.cli.ExitCodes; import org.opensearch.cli.UserException; -import org.opensearch.common.Randomness; import org.opensearch.common.SetOnce; import org.opensearch.common.crypto.KeyStoreFactory; import org.opensearch.common.crypto.KeyStoreType; @@ -78,7 +78,6 @@ import java.nio.file.attribute.PosixFilePermissions; import java.security.GeneralSecurityException; import java.security.KeyStore; -import java.security.SecureRandom; import java.util.Arrays; import java.util.Base64; import java.util.Enumeration; @@ -122,6 +121,11 @@ private static class Entry { } } + static { + // Instantiates new SecureRandom if caller is KeyStoreCli, otherwise obtains the existent. + SecureRandomInitializer.init(); + } + /** * A regex for the valid characters that a setting name in the keystore may use. */ @@ -221,11 +225,10 @@ public static KeyStoreWrapper create() { /** Add the bootstrap seed setting, which may be used as a unique, secure, random value by the node */ public static void addBootstrapSeed(KeyStoreWrapper wrapper) { assert wrapper.getSettingNames().contains(SEED_SETTING.getKey()) == false; - SecureRandom random = Randomness.createSecure(); int passwordLength = 20; // Generate 20 character passwords char[] characters = new char[passwordLength]; for (int i = 0; i < passwordLength; ++i) { - characters[i] = SEED_CHARS[random.nextInt(SEED_CHARS.length)]; + characters[i] = SEED_CHARS[CryptoServicesRegistrar.getSecureRandom().nextInt(SEED_CHARS.length)]; } wrapper.setString(SEED_SETTING.getKey(), characters); Arrays.fill(characters, (char) 0); @@ -538,15 +541,14 @@ public synchronized void save(Path configDir, char[] password) throws Exception output.writeByte(password.length == 0 ? (byte) 0 : (byte) 1); // new cipher params - SecureRandom random = Randomness.createSecure(); // use 64 bytes salt, which surpasses that recommended by OWASP // see https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet byte[] salt = new byte[64]; - random.nextBytes(salt); + CryptoServicesRegistrar.getSecureRandom().nextBytes(salt); // use 96 bits (12 bytes) for IV as recommended by NIST // see http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf section 5.2.1.1 byte[] iv = new byte[12]; - random.nextBytes(iv); + CryptoServicesRegistrar.getSecureRandom().nextBytes(iv); // encrypted data byte[] encryptedBytes = encrypt(password, salt, iv); diff --git a/server/src/main/resources/org/opensearch/bootstrap/security.policy b/server/src/main/resources/org/opensearch/bootstrap/security.policy index 0bdabba823394..5411ab96f0fcd 100644 --- a/server/src/main/resources/org/opensearch/bootstrap/security.policy +++ b/server/src/main/resources/org/opensearch/bootstrap/security.policy @@ -100,14 +100,14 @@ grant { permission java.lang.RuntimePermission "getProtectionDomain"; permission java.io.FilePermission "${java.home}/lib/security/cacerts", "read"; permission java.io.FilePermission "${java.home}/lib/security/jssecacerts", "read"; - permission java.security.SecurityPermission "getProperty.jdk.tls.disabledAlgorithms"; permission java.security.SecurityPermission "getProperty.jdk.certpath.disabledAlgorithms"; + permission java.security.SecurityPermission "getProperty.jdk.tls.disabledAlgorithms"; permission java.security.SecurityPermission "getProperty.jdk.tls.server.defaultDHEParameters"; permission java.security.SecurityPermission "getProperty.keystore.type.compat"; permission java.security.SecurityPermission "getProperty.org.bouncycastle.*"; - permission java.security.SecurityPermission "putProviderProperty.BCFIPS"; - permission java.security.SecurityPermission "putProviderProperty.BCJSSE"; + permission java.security.SecurityPermission "removeProvider.SunJCE"; permission java.util.PropertyPermission "java.runtime.name", "read"; + permission org.bouncycastle.crypto.CryptoServicesPermission "defaultRandomConfig"; permission org.bouncycastle.crypto.CryptoServicesPermission "exportSecretKey"; permission org.bouncycastle.crypto.CryptoServicesPermission "exportPrivateKey"; }; diff --git a/server/src/test/java/org/opensearch/bootstrap/SecureRandomInitializerTests.java b/server/src/test/java/org/opensearch/bootstrap/SecureRandomInitializerTests.java new file mode 100644 index 0000000000000..03ee400cb224e --- /dev/null +++ b/server/src/test/java/org/opensearch/bootstrap/SecureRandomInitializerTests.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.bootstrap; + +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.fips.FipsSecureRandom; +import org.junit.BeforeClass; +import org.opensearch.test.OpenSearchTestCase; + +import java.security.SecureRandom; +import java.util.Arrays; + +public class SecureRandomInitializerTests extends OpenSearchTestCase { + + @BeforeClass + public static void setup() { + // Reset the global state if your CryptoServicesRegistrar allows it. + // If there's a method to unset or reset the SecureRandom, call it here. + // If not, the following lines are hypothetical and depend on your actual implementation. + CryptoServicesRegistrar.setSecureRandom(null); + } + + public void testInitInNonFipsMode() { + // given + assertThrows(IllegalStateException.class, CryptoServicesRegistrar::getSecureRandom); + + // when + SecureRandomInitializer.init(); + SecureRandom secureRandom = CryptoServicesRegistrar.getSecureRandom(); + + // then + assertNotNull("SecureRandom should be initialized in non-FIPS mode", secureRandom); + byte[] randomBytes = new byte[16]; + secureRandom.nextBytes(randomBytes); + assertEquals(inFipsJvm() ? "BCFIPS_RNG" : "SUN", secureRandom.getProvider().getName()); + // BCFIPS 'DEFAULT' RNG algorithm defaults to 'HMAC-DRBG-SHA512' + assertEquals(inFipsJvm() ? "HMAC-DRBG-SHA512" : "NativePRNGBlocking", secureRandom.getAlgorithm()); + assertEquals(inFipsJvm() ? FipsSecureRandom.class : SecureRandom.class, secureRandom.getClass()); + assertFalse("Random bytes should not be all zeros", allZeros(randomBytes)); + + byte[] seed1 = secureRandom.generateSeed(16); + byte[] seed2 = secureRandom.generateSeed(16); + assertNotNull(seed1); + assertNotNull(seed2); + assertFalse("Seeds should not be identical", Arrays.equals(seed1, seed2)); + } + + private boolean allZeros(byte[] data) { + for (byte b : data) { + if (b != 0) { + return false; + } + } + return true; + } +} diff --git a/server/src/test/java/org/opensearch/bootstrap/SecurityProviderManagerTests.java b/server/src/test/java/org/opensearch/bootstrap/SecurityProviderManagerTests.java new file mode 100644 index 0000000000000..d46f3bbcc9c42 --- /dev/null +++ b/server/src/test/java/org/opensearch/bootstrap/SecurityProviderManagerTests.java @@ -0,0 +1,171 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.bootstrap; + +import org.opensearch.test.OpenSearchTestCase; +import org.junit.AfterClass; +import org.junit.Before; + +import javax.crypto.Cipher; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.Arrays; +import java.util.Locale; + +import static org.opensearch.bootstrap.BootstrapForTesting.sunJceInsertFunction; + +public class SecurityProviderManagerTests extends OpenSearchTestCase { + + private static final String BC_FIPS = "BCFIPS"; + private static final String SUN_JCE = "SunJCE"; + + // BCFIPS will only provide legacy ciphers when running in general mode, otherwise approved-only mode forbids the use. + private static final String MODE_DEPENDENT_CIPHER_PROVIDER = inFipsJvm() ? SUN_JCE : BC_FIPS; + + private static final String AES = "AES"; + private static final String RC_4 = "RC4"; + private static final String TRIPLE_DES = "DESedeWrap"; + private static final String DES = "DES"; + private static final String PBE = "PBE"; + private static final String BLOWFISH = "Blowfish"; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + var notInstalled = Arrays.stream(Security.getProviders()).noneMatch(provider -> SUN_JCE.equals(provider.getName())); + if (notInstalled && sunJceInsertFunction != null) { + sunJceInsertFunction.get(); + } + assumeTrue( + String.format( + Locale.ROOT, + "SunJCE provider has to be initially installed through '%s' file", + System.getProperty("java.security.properties", "UNDEFINED") + ), + Arrays.stream(Security.getProviders()).anyMatch(provider -> SUN_JCE.equals(provider.getName())) + ); + } + + @AfterClass + // restore the same state as before running the tests. + public static void removeSunJCE() { + if (inFipsJvm()) { + SecurityProviderManager.excludeSunJCE(); + } + } + + public void testCipherRC4() throws Exception { + // given + var cipher = Cipher.getInstance(RC_4); + assertEquals(RC_4, cipher.getAlgorithm()); + assertEquals(MODE_DEPENDENT_CIPHER_PROVIDER, cipher.getProvider().getName()); + + // when + SecurityProviderManager.excludeSunJCE(); + + // then + if (inFipsJvm()) { + expectThrows(NoSuchAlgorithmException.class, () -> Cipher.getInstance(RC_4)); + } else { + cipher = Cipher.getInstance(RC_4); + assertEquals(RC_4, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + } + } + + public void testCipherAES() throws Exception { + // given + var cipher = Cipher.getInstance(AES); + assertEquals(AES, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + + // when + SecurityProviderManager.excludeSunJCE(); + + // then + cipher = Cipher.getInstance(AES); + assertEquals(AES, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + } + + public void testCipher3Des() throws Exception { + // given + var cipher = Cipher.getInstance(TRIPLE_DES); + assertEquals(TRIPLE_DES, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + + // when + SecurityProviderManager.excludeSunJCE(); + + // then + cipher = Cipher.getInstance(TRIPLE_DES); + assertEquals(TRIPLE_DES, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + } + + public void testCipherDes() throws Exception { + // given + var cipher = Cipher.getInstance(DES); + assertEquals(DES, cipher.getAlgorithm()); + assertEquals(MODE_DEPENDENT_CIPHER_PROVIDER, cipher.getProvider().getName()); + + // when + SecurityProviderManager.excludeSunJCE(); + + // then + if (inFipsJvm()) { + expectThrows(NoSuchAlgorithmException.class, () -> Cipher.getInstance(DES)); + } else { + cipher = Cipher.getInstance(DES); + assertEquals(DES, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + } + } + + public void testCipherPBE() throws Exception { + // given + var cipher = Cipher.getInstance(PBE); + assertEquals(PBE, cipher.getAlgorithm()); + assertEquals(SUN_JCE, cipher.getProvider().getName()); + + // when + SecurityProviderManager.excludeSunJCE(); + + // then + expectThrows(NoSuchAlgorithmException.class, () -> Cipher.getInstance(PBE)); + } + + public void testCipherBlowfish() throws Exception { + // given + var cipher = Cipher.getInstance(BLOWFISH); + assertEquals(BLOWFISH, cipher.getAlgorithm()); + assertEquals(MODE_DEPENDENT_CIPHER_PROVIDER, cipher.getProvider().getName()); + + // when + SecurityProviderManager.excludeSunJCE(); + + // then + if (inFipsJvm()) { + expectThrows(NoSuchAlgorithmException.class, () -> Cipher.getInstance(BLOWFISH)); + } else { + cipher = Cipher.getInstance(BLOWFISH); + assertEquals(BLOWFISH, cipher.getAlgorithm()); + assertEquals(BC_FIPS, cipher.getProvider().getName()); + } + } + + public void testGetPosition() { + assertTrue(SUN_JCE + " is installed", SecurityProviderManager.getPosition(SUN_JCE) > 0); + removeSunJCE(); + assertTrue(SUN_JCE + " is uninstalled", SecurityProviderManager.getPosition(SUN_JCE) < 0); + } + +} diff --git a/server/src/test/resources/org/opensearch/bootstrap/test.policy b/server/src/test/resources/org/opensearch/bootstrap/test.policy index c2b5a8e9c0a4e..fe319686c38a6 100644 --- a/server/src/test/resources/org/opensearch/bootstrap/test.policy +++ b/server/src/test/resources/org/opensearch/bootstrap/test.policy @@ -10,4 +10,5 @@ grant { // allow to test Security policy and codebases permission java.util.PropertyPermission "*", "read,write"; permission java.security.SecurityPermission "createPolicy.JavaPolicy"; + permission java.security.SecurityPermission "insertProvider"; }; diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template index 22909ddf60013..69be28f4548c3 100644 --- a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/kdc.conf.template @@ -16,8 +16,8 @@ # under the License. [kdcdefaults] - kdc_listen = 88 - kdc_tcp_listen = 88 + kdc_ports = 88 + kdc_tcp_ports = 88 [realms] ${REALM_NAME} = { @@ -25,8 +25,7 @@ max_life = 12h 0m 0s max_renewable_life = 7d 0h 0m 0s master_key_type = aes256-cts - # remove aes256-cts:normal since unlimited strength policy needs installed for java to use it. - supported_enctypes = aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal + supported_enctypes = aes256-cts-hmac-sha1-96:normal aes128-cts-hmac-sha1-96:normal } [logging] diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template index 207fe939fb7a5..a87c5b50d5cf3 100644 --- a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template @@ -33,18 +33,15 @@ dns_canonicalize_hostname = false dns_lookup_kdc = false dns_lookup_realm = false - dns_uri_lookup = false forwardable = true ignore_acceptor_hostname = true rdns = false - default_tgs_enctypes = rc4-hmac - default_tkt_enctypes = rc4-hmac - permitted_enctypes = rc4-hmac + default_tgs_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 + default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 + permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 # udp_preference_limit = 1 - kdc_timeout = 3000 canonicalize = true - # See please https://seanjmullan.org/blog/2021/09/14/jdk17 (deprecate 3DES and RC4 in Kerberos) - allow_weak_crypto = true + allow_weak_crypto = false [realms] ${REALM_NAME} = { @@ -52,6 +49,8 @@ kdc = 127.0.0.1:${MAPPED_PORT} admin_server = ${KDC_NAME}:749 default_domain = ${BUILD_ZONE} + master_key_type = aes256-cts + supported_enctypes = aes256-cts-hmac-sha1-96:normal aes128-cts-hmac-sha1-96:normal } [domain_realm] diff --git a/test/framework/src/main/java/org/opensearch/bootstrap/BootstrapForTesting.java b/test/framework/src/main/java/org/opensearch/bootstrap/BootstrapForTesting.java index 933385dedcf49..b4cb2657e8dc3 100644 --- a/test/framework/src/main/java/org/opensearch/bootstrap/BootstrapForTesting.java +++ b/test/framework/src/main/java/org/opensearch/bootstrap/BootstrapForTesting.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.tests.util.LuceneTestCase; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.opensearch.common.Booleans; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtils; @@ -72,8 +73,10 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; +import static org.opensearch.bootstrap.SecurityProviderManager.SUN_JCE; import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean; /** @@ -124,6 +127,20 @@ public class BootstrapForTesting { // Log ifconfig output before SecurityManager is installed IfConfig.logIfNecessary(); + SecureRandomInitializer.init(); + + var sunJceProvider = java.security.Security.getProvider(SUN_JCE); + if (sunJceProvider != null) { + sunJceInsertFunction = () -> java.security.Security.insertProviderAt( + sunJceProvider, + SecurityProviderManager.getPosition(SUN_JCE) + ); + + if (CryptoServicesRegistrar.isInApprovedOnlyMode()) { + SecurityProviderManager.excludeSunJCE(); + } + } + // install security manager if requested if (systemPropertyAsBoolean("tests.security.manager", true)) { try { @@ -201,6 +218,8 @@ public boolean implies(ProtectionDomain domain, Permission permission) { } } + static Supplier sunJceInsertFunction; + /** Add the codebase url of the given classname to the codebases map, if the class exists. */ private static void addClassCodebase(Map codebases, String name, String classname) { try { diff --git a/test/framework/src/main/java/org/opensearch/test/KeyStoreUtils.java b/test/framework/src/main/java/org/opensearch/test/KeyStoreUtils.java new file mode 100644 index 0000000000000..90a5ce2768a30 --- /dev/null +++ b/test/framework/src/main/java/org/opensearch/test/KeyStoreUtils.java @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.test; + +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v1CertificateBuilder; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.opensearch.common.crypto.KeyStoreFactory; +import org.opensearch.common.crypto.KeyStoreType; + +import javax.security.auth.x500.X500Principal; +import javax.security.auth.x500.X500PrivateCredential; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.Date; + +public class KeyStoreUtils { + + public static final char[] KEYSTORE_PASSWORD = "keystore_password".toCharArray(); + + public static KeyStore createServerKeyStore() throws Exception { + var serverCred = createCredential(); + var keyStore = KeyStoreFactory.getInstance(KeyStoreType.BCFKS); + keyStore.load(null, null); + keyStore.setKeyEntry( + serverCred.getAlias(), + serverCred.getPrivateKey(), + KEYSTORE_PASSWORD, + new X509Certificate[] { serverCred.getCertificate() } + ); + return keyStore; + } + + private static X500PrivateCredential createCredential() throws Exception { + var keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + var keyPair = keyPairGenerator.generateKeyPair(); + var rootCert = new JcaX509CertificateConverter().getCertificate(generateCert(keyPair)); + return new X500PrivateCredential(rootCert, keyPair.getPrivate(), "server-ca"); + } + + private static X509CertificateHolder generateCert(KeyPair pair) throws Exception { + var baseTime = System.currentTimeMillis(); + // 10 years in milliseconds + var validityPeriod = 10L * 365 * 24 * 60 * 60 * 1000; + + var certBuilder = new JcaX509v1CertificateBuilder( + new X500Principal("CN=Test CA Certificate"), + BigInteger.valueOf(1), + new Date(baseTime), + new Date(baseTime + validityPeriod), + new X500Principal("CN=Test CA Certificate"), + pair.getPublic() + ); + var signer = new JcaContentSignerBuilder("SHA256withRSA").build(pair.getPrivate()); + return certBuilder.build(signer); + } + +}