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/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