Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
Expand All @@ -46,21 +48,78 @@
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;

import lombok.extern.slf4j.Slf4j;
import org.apache.bookkeeper.conf.AbstractConfiguration;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A factory to manage TLS contexts.
*/
@Slf4j
public class TLSContextFactory implements SecurityHandlerFactory {

static {
// Fixes loading PKCS8Key file: https://stackoverflow.com/a/18912362
java.security.Security.addProvider(new org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider());
public static final Provider BC_PROVIDER = getProvider();
public static final String BC_FIPS_PROVIDER_CLASS = "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider";
public static final String BC_NON_FIPS_PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";

// Security.getProvider("BC") / Security.getProvider("BCFIPS").
// also used to get Factories. e.g. CertificateFactory.getInstance("X.509", "BCFIPS")
public static final String BC_FIPS = "BCFIPS";
public static final String BC = "BC";

/**
* Get Bouncy Castle provider, and call Security.addProvider(provider) if success.
*/
public static Provider getProvider() {
boolean isProviderInstalled =
Security.getProvider(BC) != null || Security.getProvider(BC_FIPS) != null;

if (isProviderInstalled) {
Provider provider = Security.getProvider(BC) != null
? Security.getProvider(BC)
Comment thread
Ghatage marked this conversation as resolved.
: Security.getProvider(BC_FIPS);
if (log.isDebugEnabled()) {
log.debug("Already instantiated Bouncy Castle provider {}", provider.getName());
}
return provider;
}

// Not installed, try load from class path
try {
return getBCProviderFromClassPath();
} catch (Exception e) {
log.warn("Not able to get Bouncy Castle provider for both FIPS and Non-FIPS from class path:", e);
throw new RuntimeException(e);
}
}

/**
* Get Bouncy Castle provider from classpath, and call Security.addProvider.
* Throw Exception if failed.
*/
public static Provider getBCProviderFromClassPath() throws Exception {
Class clazz;
try {
clazz = Class.forName(BC_FIPS_PROVIDER_CLASS);
} catch (ClassNotFoundException cnf) {
if (log.isDebugEnabled()) {
log.debug("Not able to get Bouncy Castle provider: {}, try to get FIPS provider {}",
BC_NON_FIPS_PROVIDER_CLASS, BC_FIPS_PROVIDER_CLASS);
}
// attempt to use the NON_FIPS provider.
clazz = Class.forName(BC_NON_FIPS_PROVIDER_CLASS);

}

@SuppressWarnings("unchecked")
Provider provider = (Provider) clazz.getDeclaredConstructor().newInstance();
Security.addProvider(provider);
if (log.isDebugEnabled()) {
log.debug("Found and Instantiated Bouncy Castle provider in classpath {}", provider.getName());
}
return provider;
}

/**
Expand All @@ -83,7 +142,6 @@ public String toString() {
}
}

private static final Logger LOG = LoggerFactory.getLogger(TLSContextFactory.class);
private static final String TLSCONTEXT_HANDLER_NAME = "tls";
private String[] protocols;
private String[] ciphers;
Expand Down Expand Up @@ -130,7 +188,7 @@ private KeyManagerFactory initKeyManagerFactory(String keyStoreType, String keyS
KeyManagerFactory kmf = null;

if (Strings.isNullOrEmpty(keyStoreLocation)) {
LOG.error("Key store location cannot be empty when Mutual Authentication is enabled!");
log.error("Key store location cannot be empty when Mutual Authentication is enabled!");
Comment thread
jiazhai marked this conversation as resolved.
throw new SecurityException("Key store location cannot be empty when Mutual Authentication is enabled!");
}

Expand All @@ -153,7 +211,7 @@ private TrustManagerFactory initTrustManagerFactory(String trustStoreType, Strin
TrustManagerFactory tmf;

if (Strings.isNullOrEmpty(trustStoreLocation)) {
LOG.error("Trust Store location cannot be empty!");
log.error("Trust Store location cannot be empty!");
throw new SecurityException("Trust Store location cannot be empty!");
}

Expand All @@ -173,18 +231,18 @@ private TrustManagerFactory initTrustManagerFactory(String trustStoreType, Strin
private SslProvider getTLSProvider(String sslProvider) {
if (sslProvider.trim().equalsIgnoreCase("OpenSSL")) {
if (OpenSsl.isAvailable()) {
LOG.info("Security provider - OpenSSL");
log.info("Security provider - OpenSSL");
return SslProvider.OPENSSL;
}

Throwable causeUnavailable = OpenSsl.unavailabilityCause();
LOG.warn("OpenSSL Unavailable: ", causeUnavailable);
log.warn("OpenSSL Unavailable: ", causeUnavailable);

LOG.info("Security provider - JDK");
log.info("Security provider - JDK");
return SslProvider.JDK;
}

LOG.info("Security provider - JDK");
log.info("Security provider - JDK");
return SslProvider.JDK;
}

Expand Down Expand Up @@ -306,7 +364,7 @@ private synchronized SslContext getSSLContext() {
|| tlsKeyStorePasswordFilePath.checkAndRefresh() || tlsTrustStoreFilePath.checkAndRefresh()
|| tlsTrustStorePasswordFilePath.checkAndRefresh()) {
try {
LOG.info("Updating tls certs certFile={}, keyStoreFile={}, trustStoreFile={}",
log.info("Updating tls certs certFile={}, keyStoreFile={}, trustStoreFile={}",
tlsCertificateFilePath.getFileName(), tlsKeyStoreFilePath.getFileName(),
tlsTrustStoreFilePath.getFileName());
if (isServerCtx) {
Expand All @@ -315,7 +373,7 @@ private synchronized SslContext getSSLContext() {
updateClientContext();
}
} catch (Exception e) {
LOG.info("Failed to refresh tls certs", e);
log.info("Failed to refresh tls certs", e);
}
}
}
Expand Down Expand Up @@ -469,15 +527,15 @@ public SslHandler newTLSHandler() {
if (protocols != null && protocols.length != 0) {
sslHandler.engine().setEnabledProtocols(protocols);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Enabled cipher protocols: {} ", Arrays.toString(sslHandler.engine().getEnabledProtocols()));
if (log.isDebugEnabled()) {
log.debug("Enabled cipher protocols: {} ", Arrays.toString(sslHandler.engine().getEnabledProtocols()));
}

if (ciphers != null && ciphers.length != 0) {
sslHandler.engine().setEnabledCipherSuites(ciphers);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Enabled cipher suites: {} ", Arrays.toString(sslHandler.engine().getEnabledCipherSuites()));
if (log.isDebugEnabled()) {
log.debug("Enabled cipher suites: {} ", Arrays.toString(sslHandler.engine().getEnabledCipherSuites()));
}

return sslHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@ public void tearDown() throws Exception {
super.tearDown();
}

/**
* Verify the BouncyCastleProvider Name is expected.
*/
@Test
public void testGetBouncyCastleProviderName() throws Exception {
String bcName = TLSContextFactory.getProvider().getName();
Assert.assertEquals(bcName, TLSContextFactory.BC_FIPS);
}

/**
* Verify that a server will not start if tls is enabled but no cert is specified.
*/
Expand Down
79 changes: 79 additions & 0 deletions tests/backward-compat/bc-non-fips/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.bookkeeper.tests</groupId>
<artifactId>backward-compat</artifactId>
<version>4.15.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

<groupId>org.apache.bookkeeper.tests.backward-compat</groupId>
<artifactId>bc-non-fips</artifactId>
<packaging>jar</packaging>
<name>Apache BookKeeper :: Tests :: Backward Compatibility :: Test Bouncy Castle Provider load non FIPS version</name>
<properties>
<bc-non-fips.version>1.68</bc-non-fips.version>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>

<dependency>
<groupId>org.apache.bookkeeper</groupId>
<artifactId>bookkeeper-server</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bc-non-fips.version}</version>
</dependency>

<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>${bc-non-fips.version}</version>
</dependency>

</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.bookkeeper.tls;

import org.junit.Assert;
import org.junit.Test;

/**
* Test Bouncy Castle Provider load non FIPS version.
*/
public class TestBCNonFips {

/**
* Verify the BouncyCastleProvider Name is expected.
*/
@Test
public void testGetBouncyCastleProviderName() {
String bcName = TLSContextFactory.getProvider().getName();
Assert.assertEquals(bcName, TLSContextFactory.BC);
}
}
1 change: 1 addition & 0 deletions tests/backward-compat/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
<module>old-cookie-new-cluster</module>
<module>current-server-old-clients</module>
<module>yahoo-custom-version</module>
<module>bc-non-fips</module>
</modules>
</project>