From df6b3baf1f12bc5bf657f91bb6fa0574c6b49f47 Mon Sep 17 00:00:00 2001 From: Milos Fabian Date: Mon, 27 Apr 2015 21:51:01 +0200 Subject: [PATCH] Bug 2231 - Secure transport for PCEP http://tools.ietf.org/html/draft-ietf-pce-pceps-03 -configurable TLS parameters (location of keystore, truststore, etc.) -example initial configuration -if TLS is configured, PCEPS is enabled, session negotiation starts with StartTLS msg -StartTLSWait timer set to 60 seconds -extended PCEP FRM to handle StartTLS Change-Id: I4ad4ebc52187eaa58654db3ccabe484758132761 Signed-off-by: Ladislav Borak Signed-off-by: Milos Fabian --- .../src/main/resources/initial/32-pcep.xml | 15 ++ pcep/impl/pom.xml | 4 + .../pcep/impl/PCEPDispatcherImplModule.java | 23 ++- .../impl/AbstractPCEPSessionNegotiator.java | 63 ++++++++ .../impl/DefaultPCEPSessionNegotiator.java | 11 +- .../DefaultPCEPSessionNegotiatorFactory.java | 11 +- .../pcep/impl/tls/SslContextFactory.java | 75 +++++++++ .../protocol/pcep/impl/tls/SslKeyStore.java | 62 ++++++++ .../impl/src/main/yang/odl-pcep-impl-cfg.yang | 62 ++++++++ .../impl/PCEPDispatcherImplModuleTest.java | 143 ++++++++++++++++++ .../pcep/impl/AbstractPCEPSessionTest.java | 8 + .../pcep/impl/FiniteStateMachineTest.java | 71 ++++++++- .../pcep/impl/SslContextFactoryTest.java | 43 ++++++ .../src/test/resources/exemplary-ctlKeystore | Bin 0 -> 2254 bytes .../test/resources/exemplary-ctlTrustStore | Bin 0 -> 957 bytes .../protocol/pcep/spi/PCEPErrors.java | 27 +++- 16 files changed, 607 insertions(+), 11 deletions(-) create mode 100644 pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslContextFactory.java create mode 100644 pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslKeyStore.java create mode 100644 pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/SslContextFactoryTest.java create mode 100644 pcep/impl/src/test/resources/exemplary-ctlKeystore create mode 100644 pcep/impl/src/test/resources/exemplary-ctlTrustStore diff --git a/pcep/controller-config/src/main/resources/initial/32-pcep.xml b/pcep/controller-config/src/main/resources/initial/32-pcep.xml index 4ceee88cc9..fd77fb11e7 100644 --- a/pcep/controller-config/src/main/resources/initial/32-pcep.xml +++ b/pcep/controller-config/src/main/resources/initial/32-pcep.xml @@ -119,6 +119,21 @@ md5-server-channel-factory --> + + diff --git a/pcep/impl/pom.xml b/pcep/impl/pom.xml index 1913483057..6768baf338 100644 --- a/pcep/impl/pom.xml +++ b/pcep/impl/pom.xml @@ -79,6 +79,10 @@ io.netty netty-common + + io.netty + netty-handler + io.netty netty-transport diff --git a/pcep/impl/src/main/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModule.java b/pcep/impl/src/main/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModule.java index cd63d91b73..09749f4af7 100644 --- a/pcep/impl/src/main/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModule.java +++ b/pcep/impl/src/main/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModule.java @@ -26,6 +26,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.typ */ public final class PCEPDispatcherImplModule extends org.opendaylight.controller.config.yang.pcep.impl.AbstractPCEPDispatcherImplModule { + private static final String VALUE_IS_NOT_SET = "value is not set."; + private static final String KEYSTORE = "keystore"; + private static final String TRUSTSTORE = "truststore"; + private static final String PASSWORD = " password "; + private static final String LOCATION = " location "; + private static final String TYPE = " type "; + private static final String PATH_TYPE = " path" + TYPE; + public PCEPDispatcherImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier name, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(name, dependencyResolver); @@ -40,15 +48,26 @@ public final class PCEPDispatcherImplModule extends org.opendaylight.controller. @Override public void validate() { super.validate(); - JmxAttributeValidationException.checkNotNull(getMaxUnknownMessages(), "value is not set.", maxUnknownMessagesJmxAttribute); + JmxAttributeValidationException.checkNotNull(getMaxUnknownMessages(), VALUE_IS_NOT_SET, maxUnknownMessagesJmxAttribute); JmxAttributeValidationException.checkCondition(getMaxUnknownMessages() > 0, "Parameter 'maxUnknownMessages' " + "must be greater than 0", maxUnknownMessagesJmxAttribute); + if (getTls() != null) { + JmxAttributeValidationException.checkNotNull(getTls().getCertificatePassword(), "certificate" + PASSWORD + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getKeystore(), KEYSTORE + LOCATION + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getKeystorePassword(), KEYSTORE + PASSWORD + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getKeystorePathType(), KEYSTORE + PATH_TYPE + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getKeystoreType(), KEYSTORE + TYPE + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getTruststore(), TRUSTSTORE + LOCATION + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getTruststorePassword(), TRUSTSTORE + PASSWORD + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getTruststorePathType(), TRUSTSTORE + PATH_TYPE + VALUE_IS_NOT_SET, tlsJmxAttribute); + JmxAttributeValidationException.checkNotNull(getTls().getTruststoreType(), TRUSTSTORE + TYPE + VALUE_IS_NOT_SET, tlsJmxAttribute); + } } @Override public java.lang.AutoCloseable createInstance() { Open localPrefs = getPcepSessionProposalFactoryDependency().getSessionProposal(null, 0); - DefaultPCEPSessionNegotiatorFactory negFactory = new DefaultPCEPSessionNegotiatorFactory(localPrefs, getMaxUnknownMessages()); + DefaultPCEPSessionNegotiatorFactory negFactory = new DefaultPCEPSessionNegotiatorFactory(localPrefs, getMaxUnknownMessages(), getTls()); return new PCEPDispatcherImpl(getPcepExtensionsDependency().getMessageHandlerRegistry(), negFactory, getBossGroupDependency(), getWorkerGroupDependency(), getMd5ChannelFactoryDependency(), getMd5ServerChannelFactoryDependency()); } diff --git a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionNegotiator.java b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionNegotiator.java index 36f6289d63..bf85961409 100644 --- a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionNegotiator.java +++ b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionNegotiator.java @@ -10,16 +10,23 @@ package org.opendaylight.protocol.pcep.impl; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import io.netty.channel.Channel; +import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Promise; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import org.opendaylight.controller.config.yang.pcep.impl.Tls; import org.opendaylight.protocol.framework.AbstractSessionNegotiator; +import org.opendaylight.protocol.pcep.impl.tls.SslContextFactory; import org.opendaylight.protocol.pcep.spi.PCEPErrors; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Keepalive; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.KeepaliveBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.OpenBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Pcerr; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Starttls; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.StarttlsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.Message; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.OpenMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.keepalive.message.KeepaliveMessageBuilder; @@ -27,6 +34,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.typ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.Open; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcep.error.object.ErrorObject; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcerr.message.pcerr.message.error.type.SessionCase; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.start.tls.message.StartTlsMessageBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +61,10 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot * point we move into OpenWait state. */ IDLE, + /** + * Waiting for the peer's StartTLS message + */ + START_TLS_WAIT, /** * Waiting for the peer's OPEN message. */ @@ -75,6 +87,7 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot private Future failTimer; private Open localPrefs; private Open remotePrefs; + private Tls tlsConfiguration; protected AbstractPCEPSessionNegotiator(final Promise promise, final Channel channel) { super(promise, channel); @@ -140,6 +153,10 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot case FINISHED: case IDLE: break; + case START_TLS_WAIT: + sendErrorMessage(PCEPErrors.STARTTLS_TIMER_EXP); + negotiationFailed(new TimeoutException("StartTLSWait timer expired")); + AbstractPCEPSessionNegotiator.this.state = State.FINISHED; case KEEP_WAIT: sendErrorMessage(PCEPErrors.NO_MSG_BEFORE_EXP_KEEPWAIT); negotiationFailed(new TimeoutException("KeepWait timer expired")); @@ -160,6 +177,17 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot @Override protected final void startNegotiation() { Preconditions.checkState(this.state == State.IDLE); + if (this.tlsConfiguration != null) { + this.sendMessage(new StarttlsBuilder().setStartTlsMessage(new StartTlsMessageBuilder().build()).build()); + this.state = State.START_TLS_WAIT; + scheduleFailTimer(); + LOG.info("Started TLS connection negotiation with peer {}", this.channel); + } else { + startNegotiationWithOpen(); + } + } + + private void startNegotiationWithOpen() { this.localPrefs = getInitialProposal(); final OpenMessage m = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.OpenBuilder().setOpenMessage( new OpenMessageBuilder().setOpen(this.localPrefs).build()).build(); @@ -250,6 +278,32 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot return true; } + private boolean handleMessageStartTlsWait(final Message msg) { + if (msg instanceof Starttls) { + final SslContextFactory sslFactory = new SslContextFactory(this.tlsConfiguration); + final SSLContext sslContext = sslFactory.getServerContext(); + if (sslContext == null) { + this.sendErrorMessage(PCEPErrors.NOT_POSSIBLE_WITHOUT_TLS); + negotiationFailed(new IllegalStateException("Failed to establish a TLS connection.")); + this.state = State.FINISHED; + return true; + } + final SSLEngine engine = sslContext.createSSLEngine(); + engine.setNeedClientAuth(true); + engine.setUseClientMode(false); + this.channel.pipeline().addFirst(new SslHandler(engine)); + LOG.info("PCEPS TLS connection with peer: {} established succesfully.", this.channel); + startNegotiationWithOpen(); + return true; + } else if (! (msg instanceof Pcerr)) { + this.sendErrorMessage(PCEPErrors.NON_STARTTLS_MSG_RCVD); + negotiationFailed(new IllegalStateException("Unexpected message recieved.")); + this.state = State.FINISHED; + return true; + } + return false; + } + @Override protected final void handleMessage(final Message msg) { this.failTimer.cancel(false); @@ -260,6 +314,11 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot case FINISHED: case IDLE: throw new IllegalStateException("Unexpected handleMessage in state " + this.state); + case START_TLS_WAIT: + if (handleMessageStartTlsWait(msg)) { + return; + } + break; case KEEP_WAIT: if (handleMessageKeepWait(msg)) { return; @@ -283,4 +342,8 @@ public abstract class AbstractPCEPSessionNegotiator extends AbstractSessionNegot State getState() { return this.state; } + + public void setTlsConfiguration(final Tls tlsConfiguration) { + this.tlsConfiguration = tlsConfiguration; + } } diff --git a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiator.java b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiator.java index 62a22025bc..f748074386 100644 --- a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiator.java +++ b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiator.java @@ -9,10 +9,9 @@ package org.opendaylight.protocol.pcep.impl; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; - import io.netty.channel.Channel; import io.netty.util.concurrent.Promise; - +import org.opendaylight.controller.config.yang.pcep.impl.Tls; import org.opendaylight.protocol.pcep.PCEPSessionListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.Open; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.OpenBuilder; @@ -22,14 +21,20 @@ public final class DefaultPCEPSessionNegotiator extends AbstractPCEPSessionNegot private final int maxUnknownMessages; public DefaultPCEPSessionNegotiator(final Promise promise, final Channel channel, - final PCEPSessionListener listener, final short sessionId, final int maxUnknownMessages, final Open localPrefs) { + final PCEPSessionListener listener, final short sessionId, final int maxUnknownMessages, final Open localPrefs, final Tls tlsConfiguration) { super(promise, channel); + super.setTlsConfiguration(tlsConfiguration); this.maxUnknownMessages = maxUnknownMessages; this.myLocalPrefs = new OpenBuilder().setKeepalive(localPrefs.getKeepalive()).setDeadTimer(localPrefs.getDeadTimer()).setSessionId( sessionId).setTlvs(localPrefs.getTlvs()).build(); this.listener = Preconditions.checkNotNull(listener); } + public DefaultPCEPSessionNegotiator(final Promise promise, final Channel channel, + final PCEPSessionListener listener, final short sessionId, final int maxUnknownMessages, final Open localPrefs) { + this(promise, channel, listener, sessionId, maxUnknownMessages, localPrefs, null); + } + private final Open myLocalPrefs; @Override diff --git a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiatorFactory.java b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiatorFactory.java index 004c8c1dc6..d32372c9ea 100644 --- a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiatorFactory.java +++ b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/DefaultPCEPSessionNegotiatorFactory.java @@ -8,25 +8,30 @@ package org.opendaylight.protocol.pcep.impl; import com.google.common.base.Preconditions; - import io.netty.channel.Channel; import io.netty.util.concurrent.Promise; - +import org.opendaylight.controller.config.yang.pcep.impl.Tls; import org.opendaylight.protocol.pcep.PCEPSessionListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.Open; public final class DefaultPCEPSessionNegotiatorFactory extends AbstractPCEPSessionNegotiatorFactory { private final Open localPrefs; private final int maxUnknownMessages; + private final Tls tlsConfiguration; public DefaultPCEPSessionNegotiatorFactory(final Open localPrefs, final int maxUnknownMessages) { + this(localPrefs, maxUnknownMessages, null); + } + + public DefaultPCEPSessionNegotiatorFactory(final Open localPrefs, final int maxUnknownMessages, final Tls tlsConfiguration) { this.localPrefs = Preconditions.checkNotNull(localPrefs); this.maxUnknownMessages = maxUnknownMessages; + this.tlsConfiguration = tlsConfiguration; } @Override protected AbstractPCEPSessionNegotiator createNegotiator(final Promise promise, final PCEPSessionListener listener, final Channel channel, final short sessionId) { - return new DefaultPCEPSessionNegotiator(promise, channel, listener, sessionId, this.maxUnknownMessages, this.localPrefs); + return new DefaultPCEPSessionNegotiator(promise, channel, listener, sessionId, this.maxUnknownMessages, this.localPrefs, this.tlsConfiguration); } } diff --git a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslContextFactory.java b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslContextFactory.java new file mode 100644 index 0000000000..102cddd8f7 --- /dev/null +++ b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslContextFactory.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.pcep.impl.tls; + +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import org.opendaylight.controller.config.yang.pcep.impl.Tls; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class for setting up TLS connection. + */ +public class SslContextFactory { + + private static final String PROTOCOL = "TLS"; + private final Tls tlsConfig; + + private static final Logger LOG = LoggerFactory + .getLogger(SslContextFactory.class); + + /** + * @param tlsConfig + * TLS configuration object, contains keystore locations and + * keystore types + */ + public SslContextFactory(final Tls tlsConfig) { + this.tlsConfig = Preconditions.checkNotNull(tlsConfig); + } + + public SSLContext getServerContext() { + try { + final KeyStore ks = KeyStore.getInstance(this.tlsConfig.getKeystoreType().name()); + ks.load(SslKeyStore.asInputStream(this.tlsConfig.getKeystore(), this.tlsConfig.getKeystorePathType()), + this.tlsConfig.getKeystorePassword().toCharArray()); + final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, this.tlsConfig.getCertificatePassword().toCharArray()); + + final KeyStore ts = KeyStore.getInstance(this.tlsConfig.getTruststoreType().name()); + ts.load(SslKeyStore.asInputStream(this.tlsConfig.getTruststore(), this.tlsConfig.getTruststorePathType()), + this.tlsConfig.getTruststorePassword().toCharArray()); + final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ts); + + final SSLContext serverContext = SSLContext.getInstance(PROTOCOL); + serverContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return serverContext; + } catch (final IOException e) { + LOG.warn("IOException - Failed to load keystore / truststore." + + " Failed to initialize the server-side SSLContext", e); + } catch (final NoSuchAlgorithmException e) { + LOG.warn("NoSuchAlgorithmException - Unsupported algorithm." + + " Failed to initialize the server-side SSLContext", e); + } catch (final CertificateException e) { + LOG.warn("CertificateException - Unable to access certificate (check password)." + + " Failed to initialize the server-side SSLContext", e); + } catch (final Exception e) { + LOG.warn("Exception - Failed to initialize the server-side SSLContext", e); + } + //TODO try to use default SSLContext instance? + return null; + } +} + diff --git a/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslKeyStore.java b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslKeyStore.java new file mode 100644 index 0000000000..85c6b5a3f2 --- /dev/null +++ b/pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/tls/SslKeyStore.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.pcep.impl.tls; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.impl.rev130627.PathType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class SslKeyStore { + + private static final Logger LOG = LoggerFactory.getLogger(SslKeyStore.class); + + private SslKeyStore() { + throw new UnsupportedOperationException("Utility class shouldn't be instantiated"); + } + + /** + * InputStream instance of key - key location is on classpath or specific path + * + * @param filename + * keystore location + * @param pathType + * keystore location type - "classpath" or "path" + * + * @return key as InputStream + */ + public static InputStream asInputStream(final String filename, final PathType pathType) { + InputStream in; + switch (pathType) { + case CLASSPATH: + in = SslKeyStore.class.getResourceAsStream(filename); + if (in == null) { + throw new IllegalStateException("KeyStore file not found: " + + filename); + } + break; + case PATH: + LOG.debug("Current dir using System: {}", System.getProperty("user.dir")); + final File keystorefile = new File(filename); + try { + in = new FileInputStream(keystorefile); + } catch (FileNotFoundException e) { + throw new IllegalStateException("KeyStore file not found: " + + filename,e); + } + break; + default: + throw new IllegalArgumentException("Unknown path type: " + pathType); + } + return in; + } +} + diff --git a/pcep/impl/src/main/yang/odl-pcep-impl-cfg.yang b/pcep/impl/src/main/yang/odl-pcep-impl-cfg.yang index 6f166c0e03..6d531b45fd 100644 --- a/pcep/impl/src/main/yang/odl-pcep-impl-cfg.yang +++ b/pcep/impl/src/main/yang/odl-pcep-impl-cfg.yang @@ -35,6 +35,20 @@ module odl-pcep-impl-cfg { "Initial revision"; } + typedef path-type { + type enumeration { + enum "PATH"; + enum "CLASSPATH"; + } + } + + typedef store-type { + type enumeration { + enum "JKS"; + enum "PKCS12"; + } + } + identity pcep-parser-base { base config:module-type; config:provided-service spi:extension; @@ -115,6 +129,54 @@ module odl-pcep-impl-cfg { } } } + + container tls { + leaf keystore { + description "keystore location"; + type string; + mandatory true; + } + leaf keystore-type { + description "keystore type (JKS or PKCS12)"; + type store-type; + mandatory true; + } + leaf keystore-path-type { + description "keystore path type (CLASSPATH or PATH)"; + type path-type; + mandatory true; + } + leaf keystore-password { + description "password protecting keystore"; + type string; + mandatory true; + } + leaf certificate-password { + description "password protecting certificate"; + type string; + mandatory true; + } + leaf truststore { + description "truststore location"; + type string; + mandatory true; + } + leaf truststore-type { + description "truststore type (JKS or PKCS12)"; + type store-type; + mandatory true; + } + leaf truststore-path-type { + description "truststore path type (CLASSPATH or PATH)"; + type path-type; + mandatory true; + } + leaf truststore-password { + description "password protecting truststore"; + type string; + mandatory true; + } + } } } diff --git a/pcep/impl/src/test/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModuleTest.java b/pcep/impl/src/test/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModuleTest.java index fcc86a8d87..ffc4af21e8 100644 --- a/pcep/impl/src/test/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModuleTest.java +++ b/pcep/impl/src/test/java/org/opendaylight/controller/config/yang/pcep/impl/PCEPDispatcherImplModuleTest.java @@ -23,6 +23,8 @@ import org.opendaylight.controller.config.yang.netty.threadgroup.NettyThreadgrou import org.opendaylight.controller.config.yang.netty.threadgroup.NettyThreadgroupModuleMXBean; import org.opendaylight.controller.config.yang.pcep.spi.SimplePCEPExtensionProviderContextModuleFactory; import org.opendaylight.controller.config.yang.pcep.spi.SimplePCEPExtensionProviderContextModuleMXBean; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.impl.rev130627.PathType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.impl.rev130627.StoreType; public class PCEPDispatcherImplModuleTest extends AbstractConfigTest { @@ -36,6 +38,10 @@ public class PCEPDispatcherImplModuleTest extends AbstractConfigTest { private static final String EXTENSION_INSTANCE_NAME = "pcep-extension-privider"; private static final String EXTENSION_FACTORYNAME = SimplePCEPExtensionProviderContextModuleFactory.NAME; + private static final String STORE_PASSWORD = "opendaylight"; + private static final String KEYSTORE = "ctl.jks"; + private static final String TRUSTSTORE = "truststore.jks"; + @Before public void setUp() throws Exception { super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(this.mockedContext, new PCEPDispatcherImplModuleFactory(), new PCEPSessionProposalFactoryImplModuleFactory(), new NettyThreadgroupModuleFactory(), new SimplePCEPExtensionProviderContextModuleFactory())); @@ -61,6 +67,105 @@ public class PCEPDispatcherImplModuleTest extends AbstractConfigTest { } } + @Test + public void testValidationExceptionCertificatePassword() throws Exception { + try { + createDispatcherInstance(1, createTls(null, KEYSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS, TRUSTSTORE, STORE_PASSWORD, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("certificate password")); + } + } + + @Test + public void testValidationExceptionKeystoreLocation() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, null, STORE_PASSWORD, PathType.PATH, StoreType.JKS, TRUSTSTORE, STORE_PASSWORD, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("keystore location")); + } + } + + @Test + public void testValidationExceptionKeystorePassword() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, null, PathType.PATH, StoreType.JKS, TRUSTSTORE, STORE_PASSWORD, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("keystore password")); + } + } + + @Test + public void testValidationExceptionKeystorePathType() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, null, StoreType.JKS, TRUSTSTORE, STORE_PASSWORD, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("keystore path type")); + } + } + + @Test + public void testValidationExceptionKeystoreType() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, PathType.PATH, null, TRUSTSTORE, STORE_PASSWORD, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("keystore type")); + } + } + + @Test + public void testValidationExceptionTruststoreLocation() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS, null, STORE_PASSWORD, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("truststore location")); + } + } + + @Test + public void testValidationExceptionTruststorePassword() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS, TRUSTSTORE, null, + PathType.PATH, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("truststore password")); + } + } + + @Test + public void testValidationExceptionTruststorePathType() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS, TRUSTSTORE, STORE_PASSWORD, + null, StoreType.JKS)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("truststore path type")); + } + } + + @Test + public void testValidationExceptionTruststoreType() throws Exception { + try { + createDispatcherInstance(1, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS, TRUSTSTORE, STORE_PASSWORD, + PathType.PATH, null)); + fail(); + } catch (final ValidationException e) { + assertTrue(e.getMessage().contains("truststore type")); + } + } + @Test public void testCreateBean() throws Exception { final CommitStatus status = createDispatcherInstance(5); @@ -91,12 +196,26 @@ public class PCEPDispatcherImplModuleTest extends AbstractConfigTest { assertStatus(status, 0, 1, 4); } + @Test + public void testCreateBeanWithTls() throws Exception { + final CommitStatus status = createDispatcherInstance(5, createTls(STORE_PASSWORD, KEYSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS, + TRUSTSTORE, STORE_PASSWORD, PathType.PATH, StoreType.JKS)); + assertBeanCount(1, FACTORY_NAME); + assertStatus(status, 5, 0, 0); + } + private CommitStatus createDispatcherInstance(final Integer maxUnknownMessages) throws Exception { final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); createDispatcherInstance(transaction, maxUnknownMessages); return transaction.commit(); } + private CommitStatus createDispatcherInstance(final Integer maxUnknownMessages, final Tls tls) throws Exception { + final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); + createDispatcherInstance(transaction, maxUnknownMessages, tls); + return transaction.commit(); + } + public static ObjectName createDispatcherInstance(final ConfigTransactionJMXClient transaction, final Integer maxUnknownMessages) throws Exception { final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, INSTANCE_NAME); @@ -109,6 +228,14 @@ public class PCEPDispatcherImplModuleTest extends AbstractConfigTest { return nameCreated; } + private static ObjectName createDispatcherInstance(final ConfigTransactionJMXClient transaction, final Integer maxUnknownMessages, + final Tls tls) throws Exception { + final ObjectName objName = createDispatcherInstance(transaction, maxUnknownMessages); + final PCEPDispatcherImplModuleMXBean mxBean = transaction.newMXBeanProxy(objName, PCEPDispatcherImplModuleMXBean.class); + mxBean.setTls(tls); + return objName; + } + private static ObjectName createExtensionsInstance(final ConfigTransactionJMXClient transaction) throws InstanceAlreadyExistsException { final ObjectName nameCreated = transaction.createModule(EXTENSION_FACTORYNAME, EXTENSION_INSTANCE_NAME); transaction.newMXBeanProxy(nameCreated, SimplePCEPExtensionProviderContextModuleMXBean.class); @@ -124,4 +251,20 @@ public class PCEPDispatcherImplModuleTest extends AbstractConfigTest { return nameCreated; } + private static Tls createTls(final String certificatePassword, final String keystore, final String keystorePassword, + final PathType keystorePathType, final StoreType keystoreType, final String truststore, + final String truststorePassword, final PathType truststorePathType, final StoreType truststoreType) { + final Tls tls = new Tls(); + tls.setCertificatePassword(certificatePassword); + tls.setKeystore(keystore); + tls.setKeystorePassword(keystorePassword); + tls.setKeystorePathType(keystorePathType); + tls.setKeystoreType(keystoreType); + tls.setTruststore(truststore); + tls.setTruststorePassword(truststorePassword); + tls.setTruststorePathType(truststorePathType); + tls.setTruststoreType(truststoreType); + return tls; + } + } diff --git a/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionTest.java b/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionTest.java index bb491f5900..8eac65ae0b 100644 --- a/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionTest.java +++ b/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/AbstractPCEPSessionTest.java @@ -34,8 +34,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.mes import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.KeepaliveBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Open; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.OpenBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Starttls; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.StarttlsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.keepalive.message.KeepaliveMessageBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.message.OpenMessageBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.start.tls.message.StartTlsMessageBuilder; import org.opendaylight.yangtools.yang.binding.Notification; public class AbstractPCEPSessionTest { @@ -63,6 +66,8 @@ public class AbstractPCEPSessionTest { protected Open openMsg; + protected Starttls startTlsMsg; + protected Keepalive kaMsg; protected SimpleSessionListener listener; @@ -87,6 +92,7 @@ public class AbstractPCEPSessionTest { doReturn(true).when(this.future).cancel(false); doReturn(this.future).when(this.eventLoop).schedule(any(Runnable.class), any(long.class), any(TimeUnit.class)); doReturn(this.pipeline).when(this.pipeline).replace(any(ChannelHandler.class), any(String.class), any(ChannelHandler.class)); + doReturn(this.pipeline).when(this.pipeline).addFirst(any(ChannelHandler.class)); doReturn(true).when(this.channel).isActive(); doReturn(mock(ChannelFuture.class)).when(this.channel).close(); doReturn(new InetSocketAddress(IP_ADDRESS, 4189)).when(this.channel).remoteAddress(); @@ -96,6 +102,8 @@ public class AbstractPCEPSessionTest { new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.OpenBuilder().setDeadTimer( DEADTIMER).setKeepalive(KEEP_ALIVE).setSessionId((short) 0).build()).build()).build(); this.kaMsg = new KeepaliveBuilder().setKeepaliveMessage(new KeepaliveMessageBuilder().build()).build(); + this.startTlsMsg = new StarttlsBuilder().setStartTlsMessage(new StartTlsMessageBuilder().build()).build(); + this.listener = new SimpleSessionListener(); } diff --git a/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/FiniteStateMachineTest.java b/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/FiniteStateMachineTest.java index 55a3794663..4cb597b1aa 100644 --- a/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/FiniteStateMachineTest.java +++ b/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/FiniteStateMachineTest.java @@ -16,23 +16,30 @@ import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.opendaylight.controller.config.yang.pcep.impl.Tls; import org.opendaylight.protocol.pcep.spi.PCEPErrors; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Keepalive; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Open; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Pcerr; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Starttls; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.OpenMessage; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.OpenBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcerr.message.pcerr.message.Errors; import org.opendaylight.yangtools.yang.binding.Notification; public class FiniteStateMachineTest extends AbstractPCEPSessionTest { private DefaultPCEPSessionNegotiator serverSession; + private DefaultPCEPSessionNegotiator tlsSessionNegotiator; @Before public void setup() { - final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.Open localPrefs = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.OpenBuilder().setKeepalive( + final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.Open localPrefs = new OpenBuilder().setKeepalive( (short) 1).build(); - this.serverSession = new DefaultPCEPSessionNegotiator(new DefaultPromise(GlobalEventExecutor.INSTANCE), this.channel, this.listener, (short) 1, 20, localPrefs); + this.serverSession = new DefaultPCEPSessionNegotiator(new DefaultPromise(GlobalEventExecutor.INSTANCE), + this.channel, this.listener, (short) 1, 20, localPrefs); + this.tlsSessionNegotiator = new DefaultPCEPSessionNegotiator(new DefaultPromise(GlobalEventExecutor.INSTANCE), + this.channel, this.listener, (short) 1, 20, localPrefs, new Tls()); } /** @@ -53,6 +60,66 @@ public class FiniteStateMachineTest extends AbstractPCEPSessionTest { assertEquals(this.serverSession.getState(), DefaultPCEPSessionNegotiator.State.FINISHED); } + /** + * Establish PCEPS TLS connection with peer + */ + @Test + public void testEstablishTLS() { + final DefaultPCEPSessionNegotiator negotiator = new DefaultPCEPSessionNegotiator(new DefaultPromise(GlobalEventExecutor.INSTANCE), + this.channel, this.listener, (short) 1, 20, new OpenBuilder().setKeepalive((short) 1).build(), + SslContextFactoryTest.createTlsConfig()); + negotiator.channelActive(null); + assertEquals(1, this.msgsSend.size()); + assertTrue(this.msgsSend.get(0) instanceof Starttls); + assertEquals(DefaultPCEPSessionNegotiator.State.START_TLS_WAIT, negotiator.getState()); + negotiator.handleMessage(this.startTlsMsg); + assertEquals(DefaultPCEPSessionNegotiator.State.OPEN_WAIT, negotiator.getState()); + assertEquals(2, this.msgsSend.size()); + assertTrue(this.msgsSend.get(1) instanceof Open); + negotiator.handleMessage(this.openMsg); + assertEquals(DefaultPCEPSessionNegotiator.State.KEEP_WAIT, negotiator.getState()); + } + + /** + * As Tls is not configured properly, PCE will send error PCEPErrors.NOT_POSSIBLE_WITHOUT_TLS + * + * @throws Exception + */ + @Test + public void testFailedToEstablishTLS() throws Exception { + this.tlsSessionNegotiator.channelActive(null); + assertEquals(1, this.msgsSend.size()); + assertTrue(this.msgsSend.get(0) instanceof Starttls); + assertEquals(DefaultPCEPSessionNegotiator.State.START_TLS_WAIT, this.tlsSessionNegotiator.getState()); + this.tlsSessionNegotiator.handleMessage(this.startTlsMsg); + assertEquals(2, this.msgsSend.size()); + assertTrue(this.msgsSend.get(1) instanceof Pcerr); + final Errors obj = ((Pcerr) this.msgsSend.get(1)).getPcerrMessage().getErrors().get(0); + assertEquals(PCEPErrors.NOT_POSSIBLE_WITHOUT_TLS.getErrorType(), obj.getErrorObject().getType().shortValue()); + assertEquals(PCEPErrors.NOT_POSSIBLE_WITHOUT_TLS.getErrorValue(), obj.getErrorObject().getValue().shortValue()); + assertEquals(DefaultPCEPSessionNegotiator.State.FINISHED, this.tlsSessionNegotiator.getState()); + } + + /** + * As PCE does not receive expected message (StartTLS), error PCEPErrors.NON_STARTTLS_MSG_RCVD is send + * + * @throws Exception + */ + @Test + public void testTLSUnexpectedMessage() { + this.tlsSessionNegotiator.channelActive(null); + assertEquals(1, this.msgsSend.size()); + assertTrue(this.msgsSend.get(0) instanceof Starttls); + assertEquals(DefaultPCEPSessionNegotiator.State.START_TLS_WAIT, this.tlsSessionNegotiator.getState()); + this.tlsSessionNegotiator.handleMessage(this.openMsg); + assertEquals(2, this.msgsSend.size()); + assertTrue(this.msgsSend.get(1) instanceof Pcerr); + final Errors obj = ((Pcerr) this.msgsSend.get(1)).getPcerrMessage().getErrors().get(0); + assertEquals(PCEPErrors.NON_STARTTLS_MSG_RCVD.getErrorType(), obj.getErrorObject().getType().shortValue()); + assertEquals(PCEPErrors.NON_STARTTLS_MSG_RCVD.getErrorValue(), obj.getErrorObject().getValue().shortValue()); + assertEquals(this.tlsSessionNegotiator.getState(), DefaultPCEPSessionNegotiator.State.FINISHED); + } + /** * Mock PCE does not accept session characteristics the first time. * diff --git a/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/SslContextFactoryTest.java b/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/SslContextFactoryTest.java new file mode 100644 index 0000000000..6eada3f2f0 --- /dev/null +++ b/pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/SslContextFactoryTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.protocol.pcep.impl; + +import static org.junit.Assert.assertNotNull; + +import javax.net.ssl.SSLContext; +import org.junit.Test; +import org.opendaylight.controller.config.yang.pcep.impl.Tls; +import org.opendaylight.protocol.pcep.impl.tls.SslContextFactory; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.impl.rev130627.PathType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.impl.rev130627.StoreType; + +public class SslContextFactoryTest { + + @Test + public void testSslContextFactory() { + final SslContextFactory sslContextFactory = new SslContextFactory(createTlsConfig()); + final SSLContext sslContext = sslContextFactory.getServerContext(); + assertNotNull(sslContext); + } + + public static Tls createTlsConfig() { + final Tls tlsConfig = new Tls(); + tlsConfig.setCertificatePassword("opendaylight"); + tlsConfig.setKeystore("/exemplary-ctlKeystore"); + tlsConfig.setKeystorePassword("opendaylight"); + tlsConfig.setKeystorePathType(PathType.CLASSPATH); + tlsConfig.setKeystoreType(StoreType.JKS); + tlsConfig.setTruststore("/exemplary-ctlTrustStore"); + tlsConfig.setTruststorePassword("opendaylight"); + tlsConfig.setTruststorePathType(PathType.CLASSPATH); + tlsConfig.setTruststoreType(StoreType.JKS); + return tlsConfig; + } + +} diff --git a/pcep/impl/src/test/resources/exemplary-ctlKeystore b/pcep/impl/src/test/resources/exemplary-ctlKeystore new file mode 100644 index 0000000000000000000000000000000000000000..c7310975775cf0418464970fb3f0922901437077 GIT binary patch literal 2254 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 VALUE_MAP; -- 2.36.6