aboutsummaryrefslogtreecommitdiffstats
path: root/security/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'security/ssl')
-rw-r--r--security/ssl/pom.xml48
-rw-r--r--security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java72
-rw-r--r--security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java87
-rw-r--r--security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java37
-rw-r--r--security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java89
-rw-r--r--security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java109
-rw-r--r--security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java99
-rw-r--r--security/ssl/src/test/resources/password.txt2
8 files changed, 543 insertions, 0 deletions
diff --git a/security/ssl/pom.xml b/security/ssl/pom.xml
new file mode 100644
index 00000000..ecccd767
--- /dev/null
+++ b/security/ssl/pom.xml
@@ -0,0 +1,48 @@
+<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.onap.dcaegen2.services.sdk.security</groupId>
+ <artifactId>dcaegen2-services-sdk-security</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>ssl</artifactId>
+ <version>1.1.1-SNAPSHOT</version>
+
+ <name>SSL</name>
+ <description>Common SSL-related Classes Library</description>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>io.projectreactor.netty</groupId>
+ <artifactId>reactor-netty</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.immutables</groupId>
+ <artifactId>value</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.vavr</groupId>
+ <artifactId>vavr</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project> \ No newline at end of file
diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java
new file mode 100644
index 00000000..35fc7bbe
--- /dev/null
+++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java
@@ -0,0 +1,72 @@
+/*
+ * ============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.CheckedFunction1;
+import io.vavr.Function1;
+import io.vavr.control.Try;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Simple password representation.
+ *
+ * A password can be used only once. After it the corresponding memory is zeroed.
+ *
+ * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a>
+ * @since 1.1.1
+ */
+public class Password {
+
+ private char[] value;
+
+ public Password(@NotNull char[] value) {
+ this.value = value;
+ }
+
+ /**
+ * Consume the password.
+ *
+ * After consumption following uses of this method will return Failure(GeneralSecurityException).
+ *
+ * @param user of the password
+ */
+ public <T> Try<T> use(Function1<char[], Try<T>> user) {
+ if (value == null)
+ return Try.failure(new GeneralSecurityException("Password had been already used so it is in cleared state"));
+
+ try {
+ return user.apply(value);
+ } finally {
+ clear();
+ }
+ }
+
+ public <T> Try<T> useChecked(CheckedFunction1<char[], T> user) {
+ return use(CheckedFunction1.liftTry(user));
+ }
+
+ public void clear() {
+ Arrays.fill(value, (char) 0);
+ value = null;
+ }
+}
diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java
new file mode 100644
index 00000000..39828086
--- /dev/null
+++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java
@@ -0,0 +1,87 @@
+/*
+ * ============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.control.Try;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Utility functions for loading passwords.
+ *
+ * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a>
+ * @since 1.1.1
+ */
+public final class Passwords {
+
+ private Passwords() {
+ }
+
+ public static @NotNull Try<Password> fromFile(File file) {
+ return fromPath(file.toPath());
+ }
+
+ public static @NotNull Try<Password> fromPath(Path path) {
+ return Try.of(() -> {
+ final byte[] bytes = Files.readAllBytes(path);
+ final CharBuffer password = decodeChars(bytes);
+ final char[] result = convertToCharArray(password);
+ return new Password(result);
+ });
+ }
+
+ public static @NotNull Try<Password> fromResource(String resource) {
+ return Try.of(() -> Paths.get(Passwords.class.getResource(resource).toURI()))
+ .flatMap(Passwords::fromPath);
+ }
+
+ private static @NotNull CharBuffer decodeChars(byte[] bytes) {
+ try {
+ return Charset.defaultCharset().decode(ByteBuffer.wrap(bytes));
+ } finally {
+ Arrays.fill(bytes, (byte) 0);
+ }
+ }
+
+ private static char[] convertToCharArray(CharBuffer password) {
+ try {
+ final char[] result = new char[password.limit()];
+ password.get(result);
+ return result;
+ } finally {
+ password.flip();
+ clearBuffer(password);
+ }
+ }
+
+ private static void clearBuffer(CharBuffer password) {
+ while (password.remaining() > 0) {
+ password.put((char) 0);
+ }
+ }
+}
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
new file mode 100644
index 00000000..05c3c470
--- /dev/null
+++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java
@@ -0,0 +1,37 @@
+/*
+ * ============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 SecurityKeys {
+ Path keyStore();
+ Password keyStorePassword();
+
+ Path trustStore();
+ Password trustStorePassword();
+}
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
new file mode 100644
index 00000000..15739eb6
--- /dev/null
+++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java
@@ -0,0 +1,89 @@
+/*
+ * ============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.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.vavr.control.Try;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+public class SslFactory {
+
+ /**
+ * Function for creating secure ssl context.
+ *
+ * @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());
+
+ return Try.success(SslContextBuilder.forClient())
+ .flatMap(ctx -> keyManagerFactory.map(ctx::keyManager))
+ .flatMap(ctx -> trustManagerFactory.map(ctx::trustManager))
+ .mapTry(SslContextBuilder::build);
+ }
+
+ private Try<KeyManagerFactory> keyManagerFactory(Path path, Password password) {
+ return password.useChecked(passwordChars -> {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(loadKeyStoreFromFile(path, passwordChars), passwordChars);
+ return kmf;
+ });
+ }
+
+ private Try<TrustManagerFactory> trustManagerFactory(Path path, Password password) {
+ return password.useChecked(passwordChars -> {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(loadKeyStoreFromFile(path, passwordChars));
+ return tmf;
+ });
+ }
+
+ private KeyStore loadKeyStoreFromFile(Path path, char[] keyStorePassword)
+ throws GeneralSecurityException, IOException {
+ KeyStore ks = KeyStore.getInstance("pkcs12");
+ ks.load(Files.newInputStream(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/PasswordTest.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java
new file mode 100644
index 00000000..ede227eb
--- /dev/null
+++ b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java
@@ -0,0 +1,109 @@
+/*
+ * ============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 static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import io.vavr.collection.Array;
+import io.vavr.control.Try;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a>
+ */
+class PasswordTest {
+
+ @Test
+ void use_shouldInvokeConsumerWithStoredPassword() {
+ // given
+ final String password = "hej ho";
+ final Password cut = new Password(password.toCharArray());
+
+ // when
+ String result = cut.useChecked(String::new).get();
+
+ // then
+ assertThat(result).isEqualTo(password);
+ }
+
+ @Test
+ void use_shouldClearPasswordAfterUse() {
+ // given
+ final char[] passwordChars = "hej ho".toCharArray();
+ final Password cut = new Password(passwordChars);
+
+ // when
+ useThePassword(cut);
+
+ // then
+ assertAllCharsAreNull(passwordChars);
+ }
+
+ @Test
+ void use_shouldFail_whenItWasAlreadyCalled() {
+ // given
+ final Password cut = new Password("ala ma kota".toCharArray());
+
+ // when & then
+ useThePassword(cut).get();
+
+ assertThatExceptionOfType(GeneralSecurityException.class).isThrownBy(() ->
+ useThePassword(cut).get());
+ }
+
+ @Test
+ void use_shouldFail_whenItWasCleared() {
+ // given
+ final Password cut = new Password("ala ma kota".toCharArray());
+
+ // when & then
+ cut.clear();
+
+ assertThatExceptionOfType(GeneralSecurityException.class).isThrownBy(() ->
+ useThePassword(cut).get());
+ }
+
+ @Test
+ void clear_shouldClearThePassword() {
+ // given
+ final char[] passwordChars = "hej ho".toCharArray();
+ final Password cut = new Password(passwordChars);
+
+ // when
+ cut.clear();
+
+ // then
+ assertAllCharsAreNull(passwordChars);
+ }
+
+ private Try<Object> useThePassword(Password cut) {
+ return cut.use((pass) -> Try.success(42));
+ }
+
+ private void assertAllCharsAreNull(char[] passwordChars) {
+ assertThat(Array.ofAll(passwordChars).forAll(ch -> ch == '\0'))
+ .describedAs("all characters in " + Arrays.toString(passwordChars) + " should be == '\\0'")
+ .isTrue();
+ }
+} \ No newline at end of file
diff --git a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java
new file mode 100644
index 00000000..07c5afe8
--- /dev/null
+++ b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java
@@ -0,0 +1,99 @@
+/*
+ * ============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.Try;
+import java.io.File;
+import java.net.URISyntaxException;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a>
+ * @since January 2019
+ */
+class PasswordsTest {
+
+ @Test
+ void fromFile() {
+ // given
+ final File file = new File("./src/test/resources/password.txt");
+
+ // when
+ final Try<Password> result = Passwords.fromFile(file);
+
+ // then
+ assertSuccessful(result);
+ assertThat(extractPassword(result)).isEqualTo("ja baczewski\n2nd line");
+ }
+
+ @Test
+ void fromPath() throws URISyntaxException {
+ // given
+ final Path path = Paths.get(PasswordsTest.class.getResource("/password.txt").toURI());
+
+ // when
+ final Try<Password> result = Passwords.fromPath(path);
+
+ // then
+ assertSuccessful(result);
+ assertThat(extractPassword(result)).isEqualTo("ja baczewski\n2nd line");
+ }
+
+ @Test
+ void fromPath_shouldFail_whenNotFound() {
+ // given
+ final Path path = Paths.get("/", UUID.randomUUID().toString());
+
+ // when
+ final Try<Password> result = Passwords.fromPath(path);
+
+ // then
+ assertThat(result.isFailure()).describedAs("Try.failure?").isTrue();
+ assertThat(result.getCause()).isInstanceOf(NoSuchFileException.class);
+ }
+
+ @Test
+ void fromResource() {
+ // given
+ final String resource = "/password.txt";
+
+ // when
+ final Try<Password> result = Passwords.fromResource(resource);
+
+ // then
+ assertSuccessful(result);
+ assertThat(extractPassword(result)).isEqualTo("ja baczewski\n2nd line");
+ }
+
+ private void assertSuccessful(Try<Password> result) {
+ assertThat(result.isSuccess()).describedAs("Try.success?").isTrue();
+ }
+
+ private String extractPassword(Try<Password> result) {
+ return result.flatMap(pass -> pass.useChecked(String::new)).get();
+ }
+} \ No newline at end of file
diff --git a/security/ssl/src/test/resources/password.txt b/security/ssl/src/test/resources/password.txt
new file mode 100644
index 00000000..93e4a005
--- /dev/null
+++ b/security/ssl/src/test/resources/password.txt
@@ -0,0 +1,2 @@
+ja baczewski
+2nd line \ No newline at end of file