Write integration test for SslFactory 67/84667/2
authorPiotr Jaszczyk <piotr.jaszczyk@nokia.com>
Tue, 9 Apr 2019 11:53:55 +0000 (13:53 +0200)
committerPiotr Jaszczyk <piotr.jaszczyk@nokia.com>
Tue, 9 Apr 2019 12:18:35 +0000 (14:18 +0200)
Change-Id: I45e0f4b4bb17678d7a00287438e87215cbd0120d
Issue-ID: DCAEGEN2-1409
Signed-off-by: Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java
security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java
security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactoryIT.java [new file with mode: 0644]
security/ssl/src/test/resources/sample/cert.jks [new file with mode: 0644]
security/ssl/src/test/resources/sample/invalid.pass [new file with mode: 0644]
security/ssl/src/test/resources/sample/jks.pass [new file with mode: 0644]
security/ssl/src/test/resources/sample/trust.jks [new file with mode: 0644]
security/ssl/src/test/resources/sample/trust.pass [new file with mode: 0644]

index 401055c..d1c912b 100644 (file)
@@ -52,4 +52,8 @@ public interface SecurityKeysStore {
         return KeyStoreTypes.inferTypeFromExtension(path())
                 .getOrElseThrow(() -> new SecurityConfigurationException("Could not determine key store type by file name"));
     }
+
+    static SecurityKeysStore fromPath(Path path) {
+        return ImmutableSecurityKeysStore.of(path);
+    }
 }
index 076490a..963484a 100644 (file)
@@ -63,16 +63,27 @@ public class SslFactory {
     }
 
     /**
-     * Creates Netty SSL <em>server</em> context using provided security keys.
+     * Creates Netty SSL <em>server</em> context using provided security keys. Will require client authentication.
      *
-     * @param keys - Security keys to be used
+     * @param keys - security keys to be used
      * @return configured SSL context
      */
     public SslContext createSecureServerContext(final SecurityKeys keys) {
+        return createSecureServerContext(keys, ClientAuth.REQUIRE);
+    }
+
+    /**
+     * Creates Netty SSL <em>server</em> context using provided security keys.
+     *
+     * @param keys - security keys to be used
+     * @param clientAuth - how to authenticate client
+     * @return configured SSL context
+     */
+    public SslContext createSecureServerContext(final SecurityKeys keys, final ClientAuth clientAuth) {
         try {
             return SslContextBuilder.forServer(keyManagerFactory(keys))
                     .trustManager(trustManagerFactory(keys))
-                    .clientAuth(ClientAuth.REQUIRE)
+                    .clientAuth(clientAuth)
                     .build();
         } catch (SSLException e) {
             throw new SecurityConfigurationException(EXCEPTION_MESSAGE, e);
@@ -111,7 +122,8 @@ public class SslFactory {
                 kmf.init(loadKeyStoreFromFile(store, passwordChars), passwordChars);
                 return kmf;
             } catch (GeneralSecurityException | IOException ex) {
-                throw new ReadingSecurityKeysStoreException("Could not read private keys from store", ex);
+                throw new ReadingSecurityKeysStoreException(
+                        "Could not read private keys from store: " + ex.getMessage(), ex);
             }
         });
     }
@@ -123,7 +135,8 @@ public class SslFactory {
                 tmf.init(loadKeyStoreFromFile(store, passwordChars));
                 return tmf;
             } catch (GeneralSecurityException | IOException ex) {
-                throw new ReadingSecurityKeysStoreException("Could not read trusted keys from store", ex);
+                throw new ReadingSecurityKeysStoreException(
+                        "Could not read trusted keys from store: " + ex.getMessage(), ex);
             }
         });
     }
@@ -133,6 +146,5 @@ public class SslFactory {
         KeyStore ks = KeyStore.getInstance(store.type());
         ks.load(Files.newInputStream(store.path(), StandardOpenOption.READ), keyStorePassword);
         return ks;
-
     }
 }
diff --git a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactoryIT.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactoryIT.java
new file mode 100644 (file)
index 0000000..966aa5c
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * ============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.assertThatThrownBy;
+import static org.onap.dcaegen2.services.sdk.security.ssl.Passwords.fromResource;
+
+import io.netty.handler.ssl.SslContext;
+import java.net.URISyntaxException;
+import java.nio.file.Paths;
+import org.assertj.core.api.Assertions;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.ReadingSecurityKeysStoreException;
+
+/**
+ * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a>
+ * @since April 2019
+ */
+class SslFactoryIT {
+
+    private SslFactory sut = new SslFactory();
+
+    @Test
+    void testSuccessCase() throws Exception {
+        // given
+        final SecurityKeys securityKeys = ImmutableSecurityKeys.builder()
+                .keyStore(keyStoreFromResource("/sample/cert.jks"))
+                .keyStorePassword(fromResource("/sample/jks.pass"))
+                .trustStore(keyStoreFromResource("/sample/trust.jks"))
+                .trustStorePassword(fromResource("/sample/trust.pass"))
+                .build();
+
+        // when
+        final SslContext ctx = sut.createSecureServerContext(securityKeys);
+
+        // then
+        assertThat(ctx.isServer()).describedAs("is server ssl context").isTrue();
+    }
+
+    @Test
+    void testInvalidKeyStorePasswordCase() throws Exception {
+        // given
+        final SecurityKeys securityKeys = ImmutableSecurityKeys.builder()
+                .keyStore(keyStoreFromResource("/sample/cert.jks"))
+                .keyStorePassword(fromResource("/sample/invalid.pass"))
+                .trustStore(keyStoreFromResource("/sample/trust.jks"))
+                .trustStorePassword(fromResource("/sample/trust.pass"))
+                .build();
+
+        // when & then
+        assertThatThrownBy(() -> sut.createSecureServerContext(securityKeys))
+                .isInstanceOf(ReadingSecurityKeysStoreException.class)
+                .hasMessageContaining("Keystore was tampered with, or password was incorrect");
+    }
+
+    @Test
+    void testInvalidTrustStorePasswordCase() throws Exception {
+        // given
+        final SecurityKeys securityKeys = ImmutableSecurityKeys.builder()
+                .keyStore(keyStoreFromResource("/sample/cert.jks"))
+                .keyStorePassword(fromResource("/sample/jks.pass"))
+                .trustStore(keyStoreFromResource("/sample/trust.jks"))
+                .trustStorePassword(fromResource("/sample/invalid.pass"))
+                .build();
+
+        // when & then
+        assertThatThrownBy(() -> sut.createSecureServerContext(securityKeys))
+                .isInstanceOf(ReadingSecurityKeysStoreException.class)
+                .hasMessageContaining("Keystore was tampered with, or password was incorrect");
+    }
+
+    private @NotNull SecurityKeysStore keyStoreFromResource(String resource) throws URISyntaxException {
+        return SecurityKeysStore.fromPath(
+                Paths.get(Passwords.class.getResource(resource).toURI()));
+    }
+}
diff --git a/security/ssl/src/test/resources/sample/cert.jks b/security/ssl/src/test/resources/sample/cert.jks
new file mode 100644 (file)
index 0000000..e74ce64
Binary files /dev/null and b/security/ssl/src/test/resources/sample/cert.jks differ
diff --git a/security/ssl/src/test/resources/sample/invalid.pass b/security/ssl/src/test/resources/sample/invalid.pass
new file mode 100644 (file)
index 0000000..6003d10
--- /dev/null
@@ -0,0 +1 @@
+invalid password
\ No newline at end of file
diff --git a/security/ssl/src/test/resources/sample/jks.pass b/security/ssl/src/test/resources/sample/jks.pass
new file mode 100644 (file)
index 0000000..3982387
--- /dev/null
@@ -0,0 +1 @@
+mYHC98!qX}7h?W}jRv}MIXTJ
\ No newline at end of file
diff --git a/security/ssl/src/test/resources/sample/trust.jks b/security/ssl/src/test/resources/sample/trust.jks
new file mode 100644 (file)
index 0000000..10103cf
Binary files /dev/null and b/security/ssl/src/test/resources/sample/trust.jks differ
diff --git a/security/ssl/src/test/resources/sample/trust.pass b/security/ssl/src/test/resources/sample/trust.pass
new file mode 100644 (file)
index 0000000..168e64b
--- /dev/null
@@ -0,0 +1 @@
+*TQH?Lnszprs4LmlAj38yds(
\ No newline at end of file