Skip to content

Commit cf65dcb

Browse files
author
Igonin
committed
Migrate from BC to BCFIPS libraries
Signed-off-by: Igonin <[email protected]>
1 parent a961ec7 commit cf65dcb

File tree

54 files changed

+503
-173
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+503
-173
lines changed

CHANGELOG-3.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4545
- Use Lucene `BM25Similarity` as default since the `LegacyBM25Similarity` is marked as deprecated ([#17306](https://github.com/opensearch-project/OpenSearch/pull/17306))
4646
- Wildcard field index only 3gram of the input data [#17349](https://github.com/opensearch-project/OpenSearch/pull/17349)
4747
- Use BC libraries to parse PEM files, increase key length, allow general use of known cryptographic binary extensions, remove unused BC dependencies ([#3420](https://github.com/opensearch-project/OpenSearch/pull/14912))
48+
- Migrate BC libs to their FIPS counterparts ([#3420](https://github.com/opensearch-project/OpenSearch/pull/14912))
4849

4950
### Deprecated
5051

buildSrc/src/main/java/org/opensearch/gradle/OpenSearchTestBasePlugin.java

+6
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ public void execute(Task t) {
164164
test.systemProperty("tests.seed", BuildParams.getTestSeed());
165165
}
166166

167+
var securityFile = "java.security";
168+
test.systemProperty(
169+
"java.security.properties",
170+
project.getRootProject().getLayout().getProjectDirectory() + "/distribution/src/config/" + securityFile
171+
);
172+
167173
// don't track these as inputs since they contain absolute paths and break cache relocatability
168174
File gradleHome = project.getGradle().getGradleUserHomeDir();
169175
String gradleVersion = project.getGradle().getGradleVersion();

client/rest/build.gradle

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
*/
3030

3131
import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis
32+
import org.opensearch.gradle.precommit.TestingConventionsTasks
3233

3334
apply plugin: 'opensearch.build'
3435
apply plugin: 'opensearch.publish'
@@ -51,6 +52,9 @@ dependencies {
5152
api "commons-codec:commons-codec:${versions.commonscodec}"
5253
api "commons-logging:commons-logging:${versions.commonslogging}"
5354
api "org.slf4j:slf4j-api:${versions.slf4j}"
55+
runtimeOnly "org.bouncycastle:bc-fips:${versions.bouncycastle_jce}"
56+
runtimeOnly "org.bouncycastle:bctls-fips:${versions.bouncycastle_tls}"
57+
runtimeOnly "org.bouncycastle:bcutil-fips:${versions.bouncycastle_util}"
5458

5559
// reactor
5660
api "io.projectreactor:reactor-core:${versions.reactor}"
@@ -70,6 +74,10 @@ dependencies {
7074
testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}"
7175
}
7276

77+
tasks.named("dependencyLicenses").configure {
78+
mapping from: /bc.*/, to: 'bouncycastle'
79+
}
80+
7381
tasks.withType(CheckForbiddenApis).configureEach {
7482
//client does not depend on server, so only jdk and http signatures should be checked
7583
replaceSignatureFiles('jdk-signatures', 'http-signatures')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c8df3d47f9854f3e9ca57e9fc862da18c9381fa9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9c0632a6c5ca09a86434cf5e02e72c221e1c930f
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1d37b7a28560684f5b8e4fd65478c9130d4015d0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (c) 2000 - 2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
5+
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
6+
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9+
Software.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.client;
10+
11+
import com.carrotsearch.randomizedtesting.ThreadFilter;
12+
13+
public class BCDisposalDaemonFilter implements ThreadFilter {
14+
15+
@Override
16+
public boolean reject(Thread t) {
17+
return t.getName().equals("BC Disposal Daemon")
18+
|| t.getName().equals("BC Cleanup Executor");
19+
}
20+
}

client/rest/src/test/java/org/opensearch/client/RestClientBuilderIntegTests.java

+36-34
Original file line numberDiff line numberDiff line change
@@ -32,33 +32,31 @@
3232

3333
package org.opensearch.client;
3434

35+
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
36+
3537
import com.sun.net.httpserver.HttpExchange;
3638
import com.sun.net.httpserver.HttpHandler;
3739
import com.sun.net.httpserver.HttpsConfigurator;
3840
import com.sun.net.httpserver.HttpsServer;
3941

4042
import org.apache.hc.core5.http.HttpHost;
43+
import org.apache.hc.core5.ssl.SSLContextBuilder;
4144
import org.junit.AfterClass;
4245
import org.junit.BeforeClass;
4346

4447
import javax.net.ssl.KeyManagerFactory;
4548
import javax.net.ssl.SSLContext;
46-
import javax.net.ssl.SSLHandshakeException;
49+
import javax.net.ssl.SSLException;
4750
import javax.net.ssl.TrustManagerFactory;
4851

4952
import java.io.IOException;
5053
import java.io.InputStream;
5154
import java.net.InetAddress;
5255
import java.net.InetSocketAddress;
53-
import java.nio.file.Files;
54-
import java.nio.file.Paths;
5556
import java.security.AccessController;
56-
import java.security.KeyFactory;
5757
import java.security.KeyStore;
5858
import java.security.PrivilegedAction;
59-
import java.security.cert.Certificate;
60-
import java.security.cert.CertificateFactory;
61-
import java.security.spec.PKCS8EncodedKeySpec;
59+
import java.security.SecureRandom;
6260

6361
import static org.hamcrest.Matchers.instanceOf;
6462
import static org.junit.Assert.assertEquals;
@@ -68,14 +66,15 @@
6866
/**
6967
* Integration test to validate the builder builds a client with the correct configuration
7068
*/
69+
@ThreadLeakFilters(filters = { BCDisposalDaemonFilter.class })
7170
public class RestClientBuilderIntegTests extends RestClientTestCase {
7271

7372
private static HttpsServer httpsServer;
7473

7574
@BeforeClass
7675
public static void startHttpServer() throws Exception {
7776
httpsServer = HttpsServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
78-
httpsServer.setHttpsConfigurator(new HttpsConfigurator(getSslContext()));
77+
httpsServer.setHttpsConfigurator(new HttpsConfigurator(getSslContext(true)));
7978
httpsServer.createContext("/", new ResponseHandler());
8079
httpsServer.start();
8180
}
@@ -103,11 +102,11 @@ public void testBuilderUsesDefaultSSLContext() throws Exception {
103102
client.performRequest(new Request("GET", "/"));
104103
fail("connection should have been rejected due to SSL handshake");
105104
} catch (Exception e) {
106-
assertThat(e, instanceOf(SSLHandshakeException.class));
105+
assertThat(e.getCause(), instanceOf(SSLException.class));
107106
}
108107
}
109108

110-
SSLContext.setDefault(getSslContext());
109+
SSLContext.setDefault(getSslContext(false));
111110
try (RestClient client = buildRestClient()) {
112111
Response response = client.performRequest(new Request("GET", "/"));
113112
assertEquals(200, response.getStatusLine().getStatusCode());
@@ -122,34 +121,37 @@ private RestClient buildRestClient() {
122121
return RestClient.builder(new HttpHost("https", address.getHostString(), address.getPort())).build();
123122
}
124123

125-
private static SSLContext getSslContext() throws Exception {
126-
SSLContext sslContext = SSLContext.getInstance(getProtocol());
124+
private static SSLContext getSslContext(boolean server) throws Exception {
125+
SSLContext sslContext;
126+
char[] password = "password".toCharArray();
127+
SecureRandom secureRandom = SecureRandom.getInstance("DEFAULT", "BCFIPS");
128+
String fileExtension = ".jks";
129+
127130
try (
128-
InputStream certFile = RestClientBuilderIntegTests.class.getResourceAsStream("/test.crt");
129-
InputStream keyStoreFile = RestClientBuilderIntegTests.class.getResourceAsStream("/test_truststore.jks")
131+
InputStream trustStoreFile = RestClientBuilderIntegTests.class.getResourceAsStream("/test_truststore" + fileExtension);
132+
InputStream keyStoreFile = RestClientBuilderIntegTests.class.getResourceAsStream("/testks" + fileExtension)
130133
) {
131-
// Build a keystore of default type programmatically since we can't use JKS keystores to
132-
// init a KeyManagerFactory in FIPS 140 JVMs.
133-
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
134-
keyStore.load(null, "password".toCharArray());
135-
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
136-
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
137-
Files.readAllBytes(Paths.get(RestClientBuilderIntegTests.class.getResource("/test.der").toURI()))
138-
);
139-
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
140-
keyStore.setKeyEntry(
141-
"mykey",
142-
keyFactory.generatePrivate(privateKeySpec),
143-
"password".toCharArray(),
144-
new Certificate[] { certFactory.generateCertificate(certFile) }
145-
);
146-
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
147-
kmf.init(keyStore, "password".toCharArray());
134+
KeyStore keyStore = KeyStore.getInstance("JKS");
135+
keyStore.load(keyStoreFile, password);
136+
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX", "BCJSSE");
137+
kmf.init(keyStore, password);
138+
148139
KeyStore trustStore = KeyStore.getInstance("JKS");
149-
trustStore.load(keyStoreFile, "password".toCharArray());
150-
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
140+
trustStore.load(trustStoreFile, password);
141+
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", "BCJSSE");
151142
tmf.init(trustStore);
152-
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
143+
144+
SSLContextBuilder sslContextBuilder = SSLContextBuilder.create()
145+
.setProvider("BCJSSE")
146+
.setProtocol(getProtocol())
147+
.setSecureRandom(secureRandom);
148+
149+
if (server) {
150+
sslContextBuilder.loadKeyMaterial(keyStore, password);
151+
}
152+
sslContextBuilder.loadTrustMaterial(trustStore, null);
153+
sslContext = sslContextBuilder.build();
154+
153155
}
154156
return sslContext;
155157
}

distribution/src/config/java.security

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Security properties for non-approved mode 'org.bouncycastle.fips.approved_only=false'. Intended to be used complementary e.g.
2+
3+
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
4+
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
5+
security.provider.3=SUN
6+
security.provider.4=SunJGSS
7+
8+
ssl.KeyManagerFactory.algorithm=PKIX
9+
ssl.TrustManagerFactory.algorithm=PKIX

0 commit comments

Comments
 (0)