diff options
author | Piotr Jaszczyk <piotr.jaszczyk@nokia.com> | 2019-02-04 14:53:57 +0100 |
---|---|---|
committer | Piotr Jaszczyk <piotr.jaszczyk@nokia.com> | 2019-02-06 09:58:53 +0100 |
commit | 6b50f21a75f76ebad011188c42b6406d7c097537 (patch) | |
tree | 804e94ade1126210092c9979ff6e3b680ecae1dc /security | |
parent | 09b68daf6b644df4dd66ab792c430898bb9d2f6d (diff) |
Add support for server-side SSL context factory
Change-Id: I2fa64c71f55f1abfdeb4a2323c5456475d87fdd1
Issue-ID: DCAEGEN2-1069
Signed-off-by: Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
Diffstat (limited to 'security')
8 files changed, 244 insertions, 37 deletions
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 @@ <parent> <groupId>org.onap.dcaegen2.services.sdk.security</groupId> <artifactId>dcaegen2-services-sdk-security</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>1.1.2-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> <groupId>org.onap.dcaegen2.services.sdk.security.crypt</groupId> <artifactId>crypt-password</artifactId> - <version>1.1.0-SNAPSHOT</version> - <name>dcaegen2-services-sdk-security-crypt-password</name> + <name>Security :: Crypt Password</name> <description>DMaaP Security Module</description> <packaging>jar</packaging> 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 @@ <groupId>org.onap.dcaegen2.services.sdk.security</groupId> <artifactId>dcaegen2-services-sdk-security</artifactId> - <version>1.1.0-SNAPSHOT</version> <name>dcaegen2-services-sdk-security</name> <description>Common SDK repo for all DCAE Security</description> 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 @@ <parent> <groupId>org.onap.dcaegen2.services.sdk.security</groupId> <artifactId>dcaegen2-services-sdk-security</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>1.1.2-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> </parent> <artifactId>ssl</artifactId> - <version>1.1.1-SNAPSHOT</version> - <name>SSL</name> - <description>Common SSL-related Classes Library</description> + <name>Security :: SSL</name> + <description>Common functionality to handle SSL/TLS in Netty-based applications</description> <packaging>jar</packaging> <dependencies> 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 <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @since 1.1.1 + */ +final class KeyStoreTypes { + static final String TYPE_JKS = "jks"; + static final String TYPE_PKCS12 = "pkcs12"; + private static final Set<String> JKS_EXTENSIONS = HashSet.of(TYPE_JKS); + private static final Set<String> PKCS12_EXTENSIONS = HashSet.of(TYPE_PKCS12, "p12"); + + private KeyStoreTypes() {} + + static Option<String> inferTypeFromExtension(Path filePath) { + return extension(filePath.toString()) + .flatMap(KeyStoreTypes::typeForExtension); + } + + private static Option<String> 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<String> 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 <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @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 <em>client</em> context using provided security keys. * * @param keys - Security keys to be used * @return configured SSL context */ - public Try<SslContext> createSecureContext(final SecurityKeys keys) { - final Try<KeyManagerFactory> keyManagerFactory = - keyManagerFactory(keys.keyStore(), keys.keyStorePassword()); - final Try<TrustManagerFactory> trustManagerFactory = - trustManagerFactory(keys.trustStore(), keys.trustStorePassword()); + public Try<SslContext> 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 <em>server</em> context using provided security keys. + * + * @param keys - Security keys to be used + * @return configured SSL context + */ + public Try<SslContext> 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<SslContext> 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> keyManagerFactory(Path path, Password password) { + private Try<TrustManagerFactory> trustManagerFactory(SecurityKeys keys) { + return trustManagerFactory(keys.trustStore(), keys.trustStorePassword()); + } + + private Try<KeyManagerFactory> keyManagerFactory(SecurityKeys keys) { + return keyManagerFactory(keys.keyStore(), keys.keyStorePassword()); + } + + private Try<KeyManagerFactory> 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> trustManagerFactory(Path path, Password password) { + private Try<TrustManagerFactory> 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<SslContext> 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 <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @since February 2019 + */ +class KeyStoreTypesTest { + + @Test + void guessType_shouldReturnExtension_forP12() { + final Option<String> result = callGuessTypeWithFileName("file.p12"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_PKCS12); + } + + @Test + void guessType_shouldReturnExtension_forPkcs12() { + final Option<String> result = callGuessTypeWithFileName("file.pkcs12"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_PKCS12); + } + + @Test + void guessType_shouldReturnExtension_forJks() { + final Option<String> result = callGuessTypeWithFileName("file.jks"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_JKS); + } + + @Test + void guessType_shouldReturnExtension_ignoringCase() { + final Option<String> result = callGuessTypeWithFileName("file.PKCS12"); + assertThat(result.get()).isEqualTo(KeyStoreTypes.TYPE_PKCS12); + } + + @Test + void guessType_shouldReturnNone_whenFileDoesNotHaveExtension() { + final Option<String> result = callGuessTypeWithFileName("file"); + assertThat(result.isEmpty()).isTrue(); + } + + @Test + void guessType_shouldReturnNone_whenFileEndsWithDot() { + final Option<String> result = callGuessTypeWithFileName("file."); + assertThat(result.isEmpty()).isTrue(); + } + + private Option<String> callGuessTypeWithFileName(String fileName) { + final Path path = Paths.get("/", "tmp", fileName); + return KeyStoreTypes.inferTypeFromExtension(path); + } +}
\ No newline at end of file |