Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PEM format support #303

Merged
merged 12 commits into from
Sep 30, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,25 @@
*/
package com.ericsson.bss.cassandra.ecchronos.application;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

import javax.net.ssl.*;

import com.ericsson.bss.cassandra.ecchronos.connection.CertificateHandler;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.channel.socket.SocketChannel;
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
import io.netty.handler.ssl.SslHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -59,17 +66,17 @@ public SSLEngine newSSLEngine(EndPoint remoteEndpoint)
{
Context context = getContext();
TLSConfig tlsConfig = context.getTlsConfig();
SSLContext sslContext = context.getSSLContext();
SslContext sslContext = context.getSSLContext();

SSLEngine sslEngine;
if (remoteEndpoint != null)
{
InetSocketAddress socketAddress = remoteEndpoint.resolve();
sslEngine = sslContext.createSSLEngine(socketAddress.getHostName(), socketAddress.getPort());
sslEngine = sslContext.newEngine(ByteBufAllocator.DEFAULT, socketAddress.getHostName(), socketAddress.getPort());
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
sslEngine = sslContext.createSSLEngine();
sslEngine = sslContext.newEngine(ByteBufAllocator.DEFAULT);
}
sslEngine.setUseClientMode(true);

Expand Down Expand Up @@ -128,7 +135,7 @@ protected Context getContext()
protected static final class Context
{
private final TLSConfig tlsConfig;
private final SSLContext sslContext;
private final SslContext sslContext;

Context(TLSConfig tlsConfig) throws NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
CertificateException, KeyStoreException, KeyManagementException
Expand All @@ -147,22 +154,41 @@ boolean sameConfig(TLSConfig tlsConfig)
return this.tlsConfig.equals(tlsConfig);
}

SSLContext getSSLContext()
SslContext getSSLContext()
{
return sslContext;
}
}

protected static SSLContext createSSLContext(TLSConfig tlsConfig) throws IOException, NoSuchAlgorithmException,
KeyStoreException, CertificateException, UnrecoverableKeyException, KeyManagementException
protected static SslContext createSSLContext(TLSConfig tlsConfig) throws IOException, NoSuchAlgorithmException,
KeyStoreException, CertificateException, UnrecoverableKeyException
{
SSLContext sslContext = SSLContext.getInstance(tlsConfig.getProtocol());
KeyManagerFactory keyManagerFactory = getKeyManagerFactory(tlsConfig);
TrustManagerFactory trustManagerFactory = getTrustManagerFactory(tlsConfig);

sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
SslContextBuilder builder = SslContextBuilder.forClient();

return sslContext;
if (tlsConfig.getCertificate().isPresent() &&
tlsConfig.getCertificate_key().isPresent() &&
tlsConfig.getCertificate_authorities().isPresent())
{
File certificateFile = new File(tlsConfig.getCertificate().get());
File certificateKeyFile = new File(tlsConfig.getCertificate_key().get());
File certificateAuthorityFile = new File(tlsConfig.getCertificate_authorities().get());

valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
builder.keyManager(certificateFile, certificateKeyFile);
builder.trustManager(certificateAuthorityFile);
}
else
{
KeyManagerFactory keyManagerFactory = getKeyManagerFactory(tlsConfig);
TrustManagerFactory trustManagerFactory = getTrustManagerFactory(tlsConfig);
builder.keyManager(keyManagerFactory);
builder.trustManager(trustManagerFactory);
}
if (tlsConfig.getCipherSuites().isPresent())
{
builder.ciphers(Arrays.asList(tlsConfig.getCipherSuites().get()));
}
return builder.protocols(tlsConfig.getProtocols()).build();
}

protected static KeyManagerFactory getKeyManagerFactory(TLSConfig tlsConfig) throws IOException,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public class TLSConfig
private String truststore;
private String truststore_password;

private String certificate;
private String certificate_key;
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
private String certificate_authorities;

private String protocol;
private String algorithm;
private String store_type;
Expand Down Expand Up @@ -85,11 +89,58 @@ public void setTruststore_password(String truststore_password)
this.truststore_password = truststore_password;
}

public Optional<String> getCertificate()
{
if (this.certificate == null)
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
{
return Optional.empty();
}
return Optional.of(certificate);
}

public void setCertificate(String certificate)
{
this.certificate = certificate;
}

public Optional<String> getCertificate_key()
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
{
if (this.certificate_key == null)
{
return Optional.empty();
}
return Optional.of(certificate_key);
}

public void setCertificate_key(String certificate_key)
{
this.certificate_key = certificate_key;
}

public Optional<String> getCertificate_authorities()
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
{
if (this.certificate_authorities == null)
{
return Optional.empty();
}
return Optional.of(certificate_authorities);
}

public void setCertificate_authorities(String certificate_authorities)
{
this.certificate_authorities = certificate_authorities;
}

public String getProtocol()
{
return protocol;
}

public String[] getProtocols()
{
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
return protocol.split(",");
}

public void setProtocol(String protocol)
{
this.protocol = protocol;
Expand Down Expand Up @@ -159,6 +210,9 @@ public boolean equals(Object o)
Objects.equals(keystore_password, tlsConfig.keystore_password) &&
Objects.equals(truststore, tlsConfig.truststore) &&
Objects.equals(truststore_password, tlsConfig.truststore_password) &&
Objects.equals(certificate, tlsConfig.certificate) &&
Objects.equals(certificate_key, tlsConfig.certificate_key) &&
Objects.equals(certificate_authorities, tlsConfig.certificate_authorities) &&
Objects.equals(protocol, tlsConfig.protocol) &&
Objects.equals(algorithm, tlsConfig.algorithm) &&
Objects.equals(store_type, tlsConfig.store_type) &&
Expand All @@ -168,8 +222,8 @@ public boolean equals(Object o)
@Override
public int hashCode()
{
int result = Objects.hash(enabled, keystore, keystore_password, truststore, truststore_password, protocol,
algorithm, store_type, require_endpoint_verification);
int result = Objects.hash(enabled, keystore, keystore_password, truststore, truststore_password, certificate,
certificate_key, certificate_authorities, protocol, algorithm, store_type, require_endpoint_verification);
result = 31 * result + Arrays.hashCode(cipher_suites);
return result;
}
Expand Down
3 changes: 3 additions & 0 deletions application/src/main/resources/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ cql:
keystore_password: ecchronos
truststore: /path/to/truststore
truststore_password: ecchronos
certificate:
valmiranogueira marked this conversation as resolved.
Show resolved Hide resolved
certificate_key:
certificate_authorities:
protocol: TLSv1.2
algorithm:
store_type: JKS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public void testDefault() throws Exception
cqlTlsConfig.setKeystore_password("ecchronos");
cqlTlsConfig.setTruststore("/path/to/truststore");
cqlTlsConfig.setTruststore_password("ecchronos");
cqlTlsConfig.setCertificate(null);
cqlTlsConfig.setCertificate_key(null);
cqlTlsConfig.setCertificate_authorities(null);
cqlTlsConfig.setProtocol("TLSv1.2");
cqlTlsConfig.setAlgorithm(null);
cqlTlsConfig.setStore_type("JKS");
Expand Down Expand Up @@ -102,4 +105,29 @@ public void testEnabled() throws Exception
assertThat(config.getCql().getTls()).isEqualTo(cqlTlsConfig);
assertThat(config.getJmx().getTls()).isEqualTo(jmxTlsConfig);
}

@Test
public void testEnabledWithCertificate() throws Exception
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
File file = new File(classLoader.getResource("enabled_certificate_security.yml").getFile());

ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());

Security config = objectMapper.readValue(file, Security.class);

Credentials expectedCqlCredentials = new Credentials(true, "cqluser", "cqlpassword");

TLSConfig cqlTlsConfig = new TLSConfig();
cqlTlsConfig.setEnabled(true);
cqlTlsConfig.setCertificate("/path/to/cql/certificate");
cqlTlsConfig.setCertificate_key("/path/to/cql/certificate_key");
cqlTlsConfig.setCertificate_authorities("/path/to/cql/certificate_authorities");
cqlTlsConfig.setProtocol("TLSv1.2");
cqlTlsConfig.setCipher_suites("VALID_CIPHER_SUITE,VALID_CIPHER_SUITE2");
cqlTlsConfig.setRequire_endpoint_verification(true);

assertThat(config.getCql().getCredentials()).isEqualTo(expectedCqlCredentials);
assertThat(config.getCql().getTls()).isEqualTo(cqlTlsConfig);
}
}
28 changes: 28 additions & 0 deletions application/src/test/resources/enabled_certificate_security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#
# Copyright 2022 Telefonaktiebolaget LM Ericsson
#
# Licensed 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.
#

cql:
credentials:
enabled: true
username: cqluser
password: cqlpassword
tls:
enabled: true
certificate: /path/to/cql/certificate
certificate_key: /path/to/cql/certificate_key
certificate_authorities: /path/to/cql/certificate_authorities
protocol: TLSv1.2
cipher_suites: VALID_CIPHER_SUITE,VALID_CIPHER_SUITE2
require_endpoint_verification: true