From: Piotr Jaszczyk Date: Mon, 4 Feb 2019 13:53:57 +0000 (+0100) Subject: Add support for server-side SSL context factory X-Git-Tag: 1.1.3~23 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F86%2F77786%2F9;p=dcaegen2%2Fservices%2Fsdk.git Add support for server-side SSL context factory Change-Id: I2fa64c71f55f1abfdeb4a2323c5456475d87fdd1 Issue-ID: DCAEGEN2-1069 Signed-off-by: Piotr Jaszczyk --- diff --git a/security/crypt-password/pom.xml b/security/crypt-password/pom.xml index e7fc357d..7135e213 100644 --- a/security/crypt-password/pom.xml +++ b/security/crypt-password/pom.xml @@ -6,15 +6,15 @@ org.onap.dcaegen2.services.sdk.security dcaegen2-services-sdk-security - 1.1.0-SNAPSHOT + 1.1.2-SNAPSHOT + ../pom.xml 4.0.0 org.onap.dcaegen2.services.sdk.security.crypt crypt-password - 1.1.0-SNAPSHOT - dcaegen2-services-sdk-security-crypt-password + Security :: Crypt Password DMaaP Security Module jar diff --git a/security/pom.xml b/security/pom.xml index bf3ba379..fc298258 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -13,7 +13,6 @@ org.onap.dcaegen2.services.sdk.security dcaegen2-services-sdk-security - 1.1.0-SNAPSHOT dcaegen2-services-sdk-security Common SDK repo for all DCAE Security diff --git a/security/ssl/pom.xml b/security/ssl/pom.xml index ecccd767..0100d65b 100644 --- a/security/ssl/pom.xml +++ b/security/ssl/pom.xml @@ -6,14 +6,14 @@ org.onap.dcaegen2.services.sdk.security dcaegen2-services-sdk-security - 1.1.0-SNAPSHOT + 1.1.2-SNAPSHOT + ../pom.xml ssl - 1.1.1-SNAPSHOT - SSL - Common SSL-related Classes Library + Security :: SSL + Common functionality to handle SSL/TLS in Netty-based applications jar diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/KeyStoreTypes.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/KeyStoreTypes.java new file mode 100644 index 00000000..61e551e6 --- /dev/null +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/KeyStoreTypes.java @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security.ssl; + +import io.vavr.collection.HashSet; +import io.vavr.collection.Set; +import io.vavr.control.Option; +import java.nio.file.Path; + +/** + * @author Piotr Jaszczyk + * @since 1.1.1 + */ +final class KeyStoreTypes { + static final String TYPE_JKS = "jks"; + static final String TYPE_PKCS12 = "pkcs12"; + private static final Set JKS_EXTENSIONS = HashSet.of(TYPE_JKS); + private static final Set PKCS12_EXTENSIONS = HashSet.of(TYPE_PKCS12, "p12"); + + private KeyStoreTypes() {} + + static Option inferTypeFromExtension(Path filePath) { + return extension(filePath.toString()) + .flatMap(KeyStoreTypes::typeForExtension); + } + + private static Option extension(String filePath) { + final int dotIndex = filePath.lastIndexOf('.'); + return dotIndex < 0 || dotIndex + 1 >= filePath.length() + ? Option.none() + : Option.of(filePath.substring(dotIndex + 1).toLowerCase()); + } + + private static Option typeForExtension(String extension) { + if (JKS_EXTENSIONS.contains(extension)) { + return Option.of(TYPE_JKS); + } else if (PKCS12_EXTENSIONS.contains(extension)) { + return Option.of(TYPE_PKCS12); + } else { + return Option.none(); + } + } +} diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java index 05c3c470..244e33c8 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java @@ -20,7 +20,6 @@ package org.onap.dcaegen2.services.sdk.security.ssl; -import java.nio.file.Path; import org.immutables.value.Value; /** @@ -29,9 +28,10 @@ import org.immutables.value.Value; */ @Value.Immutable public interface SecurityKeys { - Path keyStore(); + + SecurityKeysStore keyStore(); Password keyStorePassword(); - Path trustStore(); + SecurityKeysStore trustStore(); Password trustStorePassword(); } diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java new file mode 100644 index 00000000..0ebfc451 --- /dev/null +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security.ssl; + +import java.nio.file.Path; +import org.immutables.value.Value; + +/** + * @author Piotr Jaszczyk + * @since 1.1.1 + */ +@Value.Immutable +public interface SecurityKeysStore { + /** + * Stores the file path of the key store. It should contain data in format specified by {@link #type()}. + * + * @return key store path + */ + @Value.Parameter + Path path(); + + /** + * Type of the key store. Can be anything supported by the JVM, eg. {@code jks} or {@code pkcs12}. + * + * If not set it will be guessed from the {@link #path()}. {@link IllegalStateException} will be thrown if it will + * not be possible. + * + * @return key store type + */ + @Value.Default + default String type() { + return KeyStoreTypes.inferTypeFromExtension(path()) + .getOrElseThrow(() -> new IllegalStateException("Could not determine key store type by file name")); + } +} diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java index 15739eb6..1f3f4cfd 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java @@ -36,54 +36,72 @@ import javax.net.ssl.TrustManagerFactory; public class SslFactory { /** - * Function for creating secure ssl context. + * Creates Netty SSL client context using provided security keys. * * @param keys - Security keys to be used * @return configured SSL context */ - public Try createSecureContext(final SecurityKeys keys) { - final Try keyManagerFactory = - keyManagerFactory(keys.keyStore(), keys.keyStorePassword()); - final Try trustManagerFactory = - trustManagerFactory(keys.trustStore(), keys.trustStorePassword()); + public Try createSecureClientContext(final SecurityKeys keys) { + return Try.success(SslContextBuilder.forClient()) + .flatMap(ctx -> keyManagerFactory(keys).map(ctx::keyManager)) + .flatMap(ctx -> trustManagerFactory(keys).map(ctx::trustManager)) + .mapTry(SslContextBuilder::build); + } + /** + * Creates Netty SSL server context using provided security keys. + * + * @param keys - Security keys to be used + * @return configured SSL context + */ + public Try createSecureServerContext(final SecurityKeys keys) { + return keyManagerFactory(keys) + .map(SslContextBuilder::forServer) + .flatMap(ctx -> trustManagerFactory(keys).map(ctx::trustManager)) + .mapTry(SslContextBuilder::build); + } + + /** + * Function for creating insecure SSL context. + * + * @return configured insecure ssl context + * @deprecated Do not use in production. Will trust anyone. + */ + @Deprecated + public Try createInsecureClientContext() { return Try.success(SslContextBuilder.forClient()) - .flatMap(ctx -> keyManagerFactory.map(ctx::keyManager)) - .flatMap(ctx -> trustManagerFactory.map(ctx::trustManager)) + .map(ctx -> ctx.trustManager(InsecureTrustManagerFactory.INSTANCE)) .mapTry(SslContextBuilder::build); } - private Try keyManagerFactory(Path path, Password password) { + private Try trustManagerFactory(SecurityKeys keys) { + return trustManagerFactory(keys.trustStore(), keys.trustStorePassword()); + } + + private Try keyManagerFactory(SecurityKeys keys) { + return keyManagerFactory(keys.keyStore(), keys.keyStorePassword()); + } + + private Try keyManagerFactory(SecurityKeysStore store, Password password) { return password.useChecked(passwordChars -> { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(loadKeyStoreFromFile(path, passwordChars), passwordChars); + kmf.init(loadKeyStoreFromFile(store, passwordChars), passwordChars); return kmf; }); } - private Try trustManagerFactory(Path path, Password password) { + private Try trustManagerFactory(SecurityKeysStore store, Password password) { return password.useChecked(passwordChars -> { TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(loadKeyStoreFromFile(path, passwordChars)); + tmf.init(loadKeyStoreFromFile(store, passwordChars)); return tmf; }); } - private KeyStore loadKeyStoreFromFile(Path path, char[] keyStorePassword) + private KeyStore loadKeyStoreFromFile(SecurityKeysStore store, char[] keyStorePassword) throws GeneralSecurityException, IOException { - KeyStore ks = KeyStore.getInstance("pkcs12"); - ks.load(Files.newInputStream(path, StandardOpenOption.READ), keyStorePassword); + KeyStore ks = KeyStore.getInstance(store.type()); + ks.load(Files.newInputStream(store.path(), StandardOpenOption.READ), keyStorePassword); return ks; } - - /** - * Function for creating insecure ssl context. - * - * @return configured insecure ssl context - */ - public Try createInsecureContext() { - return Try.success(SslContextBuilder.forClient()) - .map(ctx -> ctx.trustManager(InsecureTrustManagerFactory.INSTANCE)) - .mapTry(SslContextBuilder::build); - } } diff --git a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/KeyStoreTypesTest.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/KeyStoreTypesTest.java new file mode 100644 index 00000000..ab2aa773 --- /dev/null +++ b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/KeyStoreTypesTest.java @@ -0,0 +1,76 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security.ssl; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.vavr.control.Option; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; + +/** + * @author Piotr Jaszczyk + * @since February 2019 + */ +class KeyStoreTypesTest { + + @Test + void guessType_shouldReturnExtension_forP12() { + final Option result = callGuessTypeWithFileName("file.p12"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_PKCS12); + } + + @Test + void guessType_shouldReturnExtension_forPkcs12() { + final Option result = callGuessTypeWithFileName("file.pkcs12"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_PKCS12); + } + + @Test + void guessType_shouldReturnExtension_forJks() { + final Option result = callGuessTypeWithFileName("file.jks"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_JKS); + } + + @Test + void guessType_shouldReturnExtension_ignoringCase() { + final Option result = callGuessTypeWithFileName("file.PKCS12"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_PKCS12); + } + + @Test + void guessType_shouldReturnNone_whenFileDoesNotHaveExtension() { + final Option result = callGuessTypeWithFileName("file"); + assertThat(result.isEmpty()).isTrue(); + } + + @Test + void guessType_shouldReturnNone_whenFileEndsWithDot() { + final Option result = callGuessTypeWithFileName("file."); + assertThat(result.isEmpty()).isTrue(); + } + + private Option callGuessTypeWithFileName(String fileName) { + final Path path = Paths.get("/", "tmp", fileName); + return KeyStoreTypes.inferTypeFromExtension(path); + } +} \ No newline at end of file diff --git a/services/hv-ves-client/producer/api/pom.xml b/services/hv-ves-client/producer/api/pom.xml index 70238e7d..8ee5e4fd 100644 --- a/services/hv-ves-client/producer/api/pom.xml +++ b/services/hv-ves-client/producer/api/pom.xml @@ -46,7 +46,7 @@ org.onap.dcaegen2.services.sdk.security ssl - 1.1.1-SNAPSHOT + ${project.version} org.reactivestreams diff --git a/services/hv-ves-client/producer/ct/src/test/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/ct/SystemUnderTestWrapper.java b/services/hv-ves-client/producer/ct/src/test/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/ct/SystemUnderTestWrapper.java index ddc87bc4..bb947a5a 100644 --- a/services/hv-ves-client/producer/ct/src/test/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/ct/SystemUnderTestWrapper.java +++ b/services/hv-ves-client/producer/ct/src/test/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/ct/SystemUnderTestWrapper.java @@ -27,6 +27,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import org.onap.dcaegen2.services.sdk.security.ssl.ImmutableSecurityKeys; +import org.onap.dcaegen2.services.sdk.security.ssl.ImmutableSecurityKeysStore; import org.onap.dcaegen2.services.sdk.security.ssl.Passwords; import org.onap.dcaegen2.services.sdk.services.hvves.client.producer.api.HvVesProducer; import org.onap.dcaegen2.services.sdk.services.hvves.client.producer.api.HvVesProducerFactory; @@ -56,9 +57,9 @@ public class SystemUnderTestWrapper { public void startSecure() { start(ImmutableProducerOptions.builder() .securityKeys(ImmutableSecurityKeys.builder() - .keyStore(resource("/client.p12").get()) + .keyStore(ImmutableSecurityKeysStore.of(resource("/client.p12").get())) .keyStorePassword(Passwords.fromResource("/client.pass").get()) - .trustStore(resource("/trust.p12").get()) + .trustStore(ImmutableSecurityKeysStore.of(resource("/trust.p12").get())) .trustStorePassword(Passwords.fromResource("/trust.pass").get()) .build())); } diff --git a/services/hv-ves-client/producer/impl/src/main/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/impl/HvVesProducerFactoryImpl.java b/services/hv-ves-client/producer/impl/src/main/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/impl/HvVesProducerFactoryImpl.java index ab10088b..b60d6856 100644 --- a/services/hv-ves-client/producer/impl/src/main/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/impl/HvVesProducerFactoryImpl.java +++ b/services/hv-ves-client/producer/impl/src/main/java/org/onap/dcaegen2/services/sdk/services/hvves/client/producer/impl/HvVesProducerFactoryImpl.java @@ -48,7 +48,7 @@ public class HvVesProducerFactoryImpl extends HvVesProducerFactory { LOGGER.warn("Using insecure connection"); } else { LOGGER.info("Using secure tunnel"); - final SslContext ctx = sslFactory.createSecureContext(options.securityKeys()).get(); + final SslContext ctx = sslFactory.createSecureClientContext(options.securityKeys()).get(); tcpClient = tcpClient.secure(ssl -> ssl.sslContext(ctx)); }