SSL/TLS support for OVSDB Southbound API

Change-Id: Ib76653131bdf4b934a484eb72f91af60ff5861c0
Wiki Link : https://wiki.onosproject.org/pages/viewpage.action?pageId=23333242
diff --git a/protocols/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java b/protocols/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java
index 12447e3..ddd93eb 100644
--- a/protocols/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java
+++ b/protocols/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java
@@ -124,4 +124,12 @@
 
     /** Memory column of Cpu_Memory_Data table. */
     public static final String DEVICE_MEMORY = "memory";
+
+    public static final boolean OVSDB_TLS_FLAG = false;
+
+    //TODO CONFIG_DIR is duplicated from ConfigFileBasedClusterMetadataProvider
+    public static final String CONFIG_DIR = "../config/";
+    public static final String KS_FILE_NAME = "onos.jks";
+    public static final String DEFAULT_KS_FILE = CONFIG_DIR + KS_FILE_NAME;
+    public static final String DEFAULT_KS_PASSWORD = "222222";
 }
diff --git a/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java
index 8bf0609..86998c0 100644
--- a/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java
+++ b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java
@@ -29,6 +29,8 @@
 import io.netty.channel.EventLoop;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.ServerChannel;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
@@ -41,13 +43,23 @@
 
 import static org.onlab.util.Tools.groupedThreads;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
+import io.netty.util.concurrent.GlobalEventExecutor;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.util.Tools;
@@ -60,6 +72,10 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
 /**
  * The main controller class. Handles all setup and network listeners -
  * distributed OVSDBClient.
@@ -83,6 +99,17 @@
     private static final int MAX_RETRY = 5;
     private static final int IDLE_TIMEOUT_SEC = 10;
 
+    private ChannelGroup cg;
+
+    protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
+
+    protected TlsParams tlsParams = new TlsParams();
+    protected SSLContext sslContext;
+    protected KeyStore keyStore;
+
+    protected static final short MIN_KS_LENGTH = 6;
+    private static final String JAVA_KEY_STORE = "JKS";
+
     /**
      * Initialization.
      */
@@ -96,16 +123,20 @@
      * Accepts incoming connections.
      */
     private void startAcceptingConnections() throws InterruptedException {
-        ServerBootstrap b = new ServerBootstrap();
+        if (cg == null) {
+            return;
+        }
+        final ServerBootstrap b = new ServerBootstrap();
 
         b.group(bossGroup, workerGroup).channel(serverChannelClass)
-                .childHandler(new OnosCommunicationChannelInitializer());
+                .childHandler(new OvsdbChannelInitializer(this, sslContext));
+        b.option(ChannelOption.SO_REUSEADDR, true);
         b.option(ChannelOption.SO_BACKLOG, 128);
-        b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
-        b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
         b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+        b.childOption(ChannelOption.SO_SNDBUF, Controller.SEND_BUFFER_SIZE);
         b.childOption(ChannelOption.SO_KEEPALIVE, true);
-        b.bind(ovsdbPort).sync();
+
+        cg.add(b.bind(ovsdbPort).syncUninterruptibly().channel());
     }
 
     /**
@@ -133,11 +164,23 @@
     }
 
     /**
+     * Sets new TLS parameters.
+     *
+     * @param newTlsParams Modified Tls Params
+     * @return true if restart is required
+     */
+    protected boolean setTlsParameters(TlsParams newTlsParams) {
+        TlsParams oldParams = this.tlsParams;
+        this.tlsParams = newTlsParams;
+        return !Objects.equals(this.tlsParams, oldParams); // restart if TLS params change
+    }
+
+    /**
      * Handles the new connection of node.
      *
      * @param channel the channel to use.
      */
-    private void handleNewNodeConnection(final Channel channel) {
+    protected void handleNewNodeConnection(final Channel channel) {
         executorService.execute(() -> {
             log.info("Handle new node connection");
 
@@ -205,6 +248,7 @@
         // therefore, ONOS only runs as an OVSDB client
         if (mode) {
             try {
+                this.init();
                 this.run();
             } catch (InterruptedException e) {
                 log.warn("Interrupted while waiting to start");
@@ -218,8 +262,21 @@
      *
      */
     public void stop() {
-        workerGroup.shutdownGracefully();
-        bossGroup.shutdownGracefully();
+        if (cg != null) {
+            cg.close();
+            cg = null;
+
+            workerGroup.shutdownGracefully();
+            bossGroup.shutdownGracefully();
+            // Wait until all threads are terminated.
+            try {
+                bossGroup.terminationFuture().sync();
+                workerGroup.terminationFuture().sync();
+            } catch (InterruptedException e) {
+                log.warn("Interrupted while stopping", e);
+                Thread.currentThread().interrupt();
+            }
+        }
     }
 
     /**
@@ -257,11 +314,11 @@
 
                     @Override
                     protected void initChannel(SocketChannel channel) throws Exception {
-                        ChannelPipeline p = channel.pipeline();
-                        p.addLast(new MessageDecoder(),
-                                  new StringEncoder(CharsetUtil.UTF_8),
-                                  new IdleStateHandler(IDLE_TIMEOUT_SEC, 0, 0),
-                                  new ConnectionHandler());
+                        ChannelPipeline pipeline = channel.pipeline();
+                        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
+                        pipeline.addLast(new MessageDecoder());
+                        pipeline.addLast(new IdleStateHandler(IDLE_TIMEOUT_SEC, 0, 0));
+                        pipeline.addLast(new ConnectionHandler());
                     }
                 });
         b.remoteAddress(ip.toString(), port.toInt());
@@ -320,4 +377,36 @@
             }
         }
     }
+
+    /**
+     * Initialize internal data structures.
+     */
+    public void init() {
+        cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+        if (tlsParams.isTlsEnabled()) {
+            initSsl();
+        }
+
+    }
+
+    private void initSsl()  {
+        try {
+            TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            KeyStore ts = KeyStore.getInstance(JAVA_KEY_STORE);
+            ts.load(new FileInputStream(tlsParams.tsLocation), tlsParams.tsPwd());
+            tmFactory.init(ts);
+
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            keyStore = KeyStore.getInstance(JAVA_KEY_STORE);
+            keyStore.load(new FileInputStream(tlsParams.ksLocation), tlsParams.ksPwd());
+            kmf.init(keyStore, tlsParams.ksPwd());
+
+            sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
+        } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException |
+                IOException | KeyManagementException | UnrecoverableKeyException ex) {
+            log.error("SSL init failed: {}", ex.getMessage());
+        }
+    }
 }
diff --git a/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbChannelInitializer.java b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbChannelInitializer.java
new file mode 100644
index 0000000..5badebf
--- /dev/null
+++ b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbChannelInitializer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+package org.onosproject.ovsdb.controller.impl;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.util.CharsetUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Creates a ChannelInitializer for a server-side OVSDB channel.
+ */
+public class OvsdbChannelInitializer
+        extends ChannelInitializer<SocketChannel> {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final SSLContext sslContext;
+    protected Controller controller;
+
+    private static final int READER_IDLE_TIME = 20;
+    private static final int WRITER_IDLE_TIME = 25;
+    private static final int ALL_IDLE_TIME = 0;
+    private static final int TIMEOUT = 180;
+
+    public OvsdbChannelInitializer(Controller controller, SSLContext sslContext) {
+        super();
+        this.controller = controller;
+        this.sslContext = sslContext;
+    }
+
+    @Override
+    protected void initChannel(SocketChannel channel) throws Exception {
+
+        ChannelPipeline pipeline = channel.pipeline();
+        if (sslContext != null) {
+            log.info("OVSDB SSL enabled.");
+            SSLEngine sslEngine = sslContext.createSSLEngine();
+
+            sslEngine.setNeedClientAuth(true);
+            sslEngine.setUseClientMode(false);
+            sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols());
+            sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
+            sslEngine.setEnableSessionCreation(true);
+
+            SslHandler sslHandler = new SslHandler(sslEngine);
+            pipeline.addLast("ssl", sslHandler);
+        } else {
+            log.info("OVSDB SSL disabled.");
+        }
+        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
+        pipeline.addLast(new MessageDecoder());
+
+        pipeline.addLast(new IdleStateHandler(READER_IDLE_TIME, WRITER_IDLE_TIME, ALL_IDLE_TIME));
+        pipeline.addLast(new ReadTimeoutHandler(TIMEOUT));
+        controller.handleNewNodeConnection(channel);
+    }
+}
+
diff --git a/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java
index 0d1e662..11bfaae 100644
--- a/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java
+++ b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java
@@ -16,6 +16,7 @@
 package org.onosproject.ovsdb.controller.impl;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -46,6 +47,7 @@
 import org.onosproject.ovsdb.controller.OvsdbPortNumber;
 import org.onosproject.ovsdb.controller.OvsdbPortType;
 import org.onosproject.ovsdb.controller.driver.OvsdbAgent;
+import org.onosproject.ovsdb.controller.impl.TlsParams.TlsMode;
 import org.onosproject.ovsdb.rfc.jsonrpc.Callback;
 import org.onosproject.ovsdb.rfc.message.TableUpdate;
 import org.onosproject.ovsdb.rfc.message.TableUpdates;
@@ -70,6 +72,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -79,7 +82,12 @@
 import java.util.function.Consumer;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.get;
 import static org.onosproject.ovsdb.controller.OvsdbConstant.SERVER_MODE;
+import static org.onosproject.ovsdb.controller.OvsdbConstant.DEFAULT_KS_PASSWORD;
+import static org.onosproject.ovsdb.controller.OvsdbConstant.DEFAULT_KS_FILE;
+import static org.onosproject.ovsdb.controller.OvsdbConstant.OVSDB_TLS_FLAG;
+import static org.onosproject.ovsdb.controller.impl.Controller.MIN_KS_LENGTH;
 
 /**
  * The implementation of OvsdbController.
@@ -109,11 +117,31 @@
             label = "Run as server mode, listen on 6640 port")
     private boolean serverMode = SERVER_MODE;
 
+    @Property(name = "enableOvsdbTls", boolValue = OVSDB_TLS_FLAG,
+            label = "TLS mode for OVSDB channel; options are: true false")
+    private boolean enableOvsdbTls = OVSDB_TLS_FLAG;
+
+    @Property(name = "keyStoreLocation", value = DEFAULT_KS_FILE,
+            label = "File path to KeyStore for Ovsdb TLS Connections")
+    protected String keyStoreLocation = DEFAULT_KS_FILE;
+
+    @Property(name = "trustStoreLocation", value = DEFAULT_KS_FILE,
+            label = "File path to TrustStore for Ovsdb TLS Connections")
+    protected String trustStoreLocation = DEFAULT_KS_FILE;
+
+    @Property(name = "keyStorePassword", value = DEFAULT_KS_PASSWORD,
+            label = "KeyStore Password")
+    protected String keyStorePassword = DEFAULT_KS_PASSWORD;
+
+    @Property(name = "trustStorePassword", value = DEFAULT_KS_PASSWORD,
+            label = "TrustStore Password")
+    protected String trustStorePassword = DEFAULT_KS_PASSWORD;
+
     @Activate
     public void activate(ComponentContext context) {
-        controller.start(agent, updateCallback, serverMode);
-
         configService.registerProperties(getClass());
+        modified(context);
+        controller.start(agent, updateCallback, serverMode);
 
         log.info("Started");
     }
@@ -124,25 +152,90 @@
 
         configService.unregisterProperties(getClass(), false);
 
-        log.info("Stoped");
+        log.info("Stopped");
     }
 
     @Modified
     protected void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        Boolean flag;
+        this.setConfigParams(context.getProperties());
+    }
 
-        flag = Tools.isPropertyEnabled(properties, "serverMode");
-        if (flag == null) {
-            log.info("OVSDB server mode is not configured, " +
-                    "using current value of {}", serverMode);
+    /**
+     * Sets config params.
+     *
+     * @param properties dictionary
+     */
+    public void setConfigParams(Dictionary<?, ?> properties) {
+        boolean restartRequired = setServerMode(properties);
+        TlsParams tlsParams = getTlsParams(properties);
+        restartRequired |= controller.setTlsParameters(tlsParams);
+        if (restartRequired) {
+            restartController();
+        }
+    }
+
+    /**
+     * Gets the TLS parameters from the properties dict.
+     *
+     * @param properties dictionary
+     * @return TlsParams Modified Tls Params
+     */
+    private TlsParams getTlsParams(Dictionary<?, ?> properties) {
+        TlsMode mode = null;
+
+        boolean flag = Tools.isPropertyEnabled(properties, "enableOvsdbTls");
+        if (Objects.isNull(flag) || !flag) {
+            log.warn("OvsdbTLS Disabled");
+            mode = TlsMode.DISABLED;
+        } else {
+            log.warn("OvsdbTLS Enabled");
+            mode = TlsMode.ENABLED;
+        }
+
+        String ksLocation = null, tsLocation = null, ksPwd = null, tsPwd = null;
+
+        ksLocation = get(properties, "keyStoreLocation");
+        if (Strings.isNullOrEmpty(ksLocation)) {
+            log.warn("trustStoreLocation is not configured");
+            mode = TlsMode.DISABLED;
+        }
+
+        tsLocation = get(properties, "trustStoreLocation");
+        if (Strings.isNullOrEmpty(tsLocation)) {
+            log.warn("trustStoreLocation is not configured");
+            mode = TlsMode.DISABLED;
+        }
+
+        ksPwd = get(properties, "keyStorePassword");
+        if (Strings.isNullOrEmpty(ksPwd) || MIN_KS_LENGTH > ksPwd.length()) {
+            log.warn("keyStorePassword is not configured or Password length too small");
+            mode = TlsMode.DISABLED;
+        }
+
+        tsPwd = get(properties, "trustStorePassword");
+        if (Strings.isNullOrEmpty(tsPwd) || MIN_KS_LENGTH > tsPwd.length()) {
+            log.warn("trustStorePassword is not configured or Password length too small");
+            mode = TlsMode.DISABLED;
+        }
+
+        TlsParams tlsParams = new TlsParams(mode, ksLocation, tsLocation, ksPwd, tsPwd);
+        log.info("OVSDB TLS Params: {}", tlsParams);
+        return tlsParams;
+    }
+
+    private boolean setServerMode(Dictionary<?, ?> properties) {
+        boolean flag = Tools.isPropertyEnabled(properties, "serverMode");
+        if (Objects.isNull(flag) || flag == serverMode) {
+            log.info("Ovsdb server mode is not configured, " +
+                             "or modified. Using current value of {}", serverMode);
+            return false;
         } else {
             serverMode = flag;
             log.info("Configured. OVSDB server mode was {}",
-                    serverMode ? "enabled" : "disabled");
+                     serverMode ? "enabled" : "disabled");
+            return true;
         }
 
-        restartController();
     }
 
     @Override
diff --git a/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/TlsParams.java b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/TlsParams.java
new file mode 100644
index 0000000..1d09e1d
--- /dev/null
+++ b/protocols/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/TlsParams.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * 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.
+ */
+package org.onosproject.ovsdb.controller.impl;
+
+import com.google.common.base.MoreObjects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Objects;
+
+import static org.onosproject.ovsdb.controller.OvsdbConstant.DEFAULT_KS_FILE;
+import static org.onosproject.ovsdb.controller.OvsdbConstant.DEFAULT_KS_PASSWORD;
+
+/**
+ * TlsParams Class for properties required for configuring OVSDB TLS Connection.
+ */
+public class TlsParams {
+
+    private static final Logger log = LoggerFactory
+            .getLogger(Controller.class);
+
+    /**
+     * Options for Activated / Deactivated TLS Mode.
+     */
+    enum TlsMode {
+        /**
+         * Signifies that TLS is enabled.
+         */
+        ENABLED,
+        /**
+         * Signifies that TLS is disabled.
+         */
+        DISABLED
+    }
+
+    protected static final EnumSet<TlsMode> TLS_ENABLED = EnumSet.of(TlsMode.ENABLED);
+
+    final TlsMode mode;
+    final String ksLocation;
+    final String tsLocation;
+    final String ksPwd;
+    final String tsPwd;
+    final byte[] ksSignature;
+    final byte[] tsSignature;
+
+    /**
+     * Default Constructor.
+     */
+    TlsParams() {
+        this.mode = TlsMode.DISABLED;
+        this.ksLocation = DEFAULT_KS_FILE;
+        this.tsLocation = DEFAULT_KS_FILE;
+        this.ksPwd = DEFAULT_KS_PASSWORD;
+        this.tsPwd = DEFAULT_KS_PASSWORD;
+        this.ksSignature = getSha1Checksum(ksLocation);
+        this.tsSignature = getSha1Checksum(tsLocation);
+    }
+
+    /**
+     * Creates new Tls params.
+     *
+     * @param mode TlsMode
+     * @param ksLocation keyStore Location
+     * @param tsLocation trustStore Location
+     * @param ksPwd keyStore Password
+     * @param tsPwd trustStore Password
+     */
+    TlsParams(TlsMode mode, String ksLocation, String tsLocation,
+              String ksPwd, String tsPwd) {
+        this.mode = mode;
+        this.ksLocation = ksLocation;
+        this.tsLocation = tsLocation;
+        this.ksPwd = ksPwd;
+        this.tsPwd = tsPwd;
+        this.ksSignature = getSha1Checksum(ksLocation);
+        this.tsSignature = getSha1Checksum(tsLocation);
+    }
+
+    /**
+     * Exposes the keyStore password in char[] format.
+     *
+     * @return the keyStorePassword as a char array
+     */
+    public char[] ksPwd() {
+        return ksPwd.toCharArray();
+    }
+
+    /**
+     * Exposes the trustStore password in char[] format.
+     *
+     * @return the trustStorePassword as a char array
+     */
+    public char[] tsPwd() {
+        return tsPwd.toCharArray();
+    }
+
+    /**
+     * Returns whether TLS is enabled or not.
+     *
+     * @return true if TLS is enabled otherwise false
+     */
+    public boolean isTlsEnabled() {
+        return TLS_ENABLED.contains(mode);
+    }
+
+    /**
+     * Returns SHA1 Checksum from a JKS.
+     *
+     * @param filepath JKS FilePath
+     * @return byte[] sha1checksum
+     */
+    public byte[] getSha1Checksum(String filepath) {
+        if (filepath == null) {
+            return new byte[0];
+        }
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA1");
+            File f = new File(filepath);
+            FileInputStream is = new FileInputStream(f);
+            DigestInputStream dis = new DigestInputStream(is, digest);
+            byte[] buffer = new byte[1024];
+            while (dis.read(buffer) > 0) {
+                // nothing to do :)
+            }
+            return dis.getMessageDigest().digest();
+        } catch (NoSuchAlgorithmException e) {
+            log.error("Algorithm SHA1 Not found");
+        } catch (IOException e) {
+            log.info("Error reading file file: {}", filepath);
+        }
+        return new byte[0];
+    }
+
+    @Override
+    public int hashCode() {
+        if (mode == TlsMode.DISABLED) {
+            return Objects.hash(mode);
+        }
+        return Objects.hash(mode, ksLocation, tsLocation,
+                            ksPwd, tsPwd,
+                            Arrays.hashCode(ksSignature),
+                            Arrays.hashCode(tsSignature));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof TlsParams) {
+            final TlsParams that = (TlsParams) obj;
+            if (this.getClass() != that.getClass()) {
+                return false;
+            } else if (this.mode == that.mode && this.mode == TlsMode.DISABLED) {
+                // All disabled objects should be equal regardless of other params
+                return true;
+            }
+            return this.mode == that.mode &&
+                    Objects.equals(this.ksLocation, that.ksLocation) &&
+                    Objects.equals(this.tsLocation, that.tsLocation) &&
+                    Objects.equals(this.ksPwd, that.ksPwd) &&
+                    Objects.equals(this.tsPwd, that.tsPwd) &&
+                    Arrays.equals(this.ksSignature, that.ksSignature) &&
+                    Arrays.equals(this.tsSignature, that.tsSignature);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("tlsMode", mode.toString().toLowerCase())
+                .add("ksLocation", ksLocation)
+                .add("tsLocation", tsLocation)
+                .toString();
+    }
+}
\ No newline at end of file