package org.opendaylight.netconf.callhome.mount.tls;
import com.google.common.collect.ImmutableMultimap;
-import io.netty.channel.Channel;
-import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.ssl.SslContext;
+import java.net.SocketAddress;
import java.security.PublicKey;
import java.util.List;
import java.util.Map;
import org.opendaylight.mdsal.binding.api.DataTreeModification;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.netconf.callhome.server.tls.CallHomeTlsAuthProvider;
-import org.opendaylight.netconf.client.SslHandlerFactory;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
+import org.opendaylight.netconf.client.SslContextFactory;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
import org.opendaylight.netconf.keystore.legacy.NetconfKeystore;
import org.opendaylight.netconf.keystore.legacy.NetconfKeystoreService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev240129.NetconfCallhomeServer;
@Component(service = CallHomeTlsAuthProvider.class, immediate = true)
@Singleton
-public final class CallHomeMountTlsAuthProvider implements CallHomeTlsAuthProvider, AutoCloseable {
+public final class CallHomeMountTlsAuthProvider extends CallHomeTlsAuthProvider implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(CallHomeMountTlsAuthProvider.class);
private final ConcurrentMap<String, String> deviceToPrivateKey = new ConcurrentHashMap<>();
private final ConcurrentMap<String, String> deviceToCertificate = new ConcurrentHashMap<>();
private final Registration allowedDevicesReg;
private final Registration certificatesReg;
- private final SslHandlerFactory sslHandlerFactory;
+ private final SslContextFactory sslContextFactory;
private volatile ImmutableMultimap<PublicKey, String> publicKeyToName = ImmutableMultimap.of();
@Inject
@Activate
- public CallHomeMountTlsAuthProvider(final @Reference SslHandlerFactoryProvider sslHandlerFactoryProvider,
- final @Reference DataBroker dataBroker, final @Reference NetconfKeystoreService keystoreService) {
- sslHandlerFactory = sslHandlerFactoryProvider.getSslHandlerFactory(null);
+ public CallHomeMountTlsAuthProvider(@Reference final SslContextFactoryProvider sslContextFactoryProvider,
+ @Reference final DataBroker dataBroker, @Reference final NetconfKeystoreService keystoreService) {
+ sslContextFactory = sslContextFactoryProvider.getSslContextFactory(null);
allowedDevicesReg = dataBroker.registerTreeChangeListener(
DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION,
InstanceIdentifier.create(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class)),
}
@Override
- public SslHandler createSslHandler(final Channel channel) {
- return sslHandlerFactory.createSslHandler(Set.copyOf(deviceToPrivateKey.values()));
+ protected SslContext getSslContext(final SocketAddress remoteAddress) {
+ return sslContextFactory.createSslContext(Set.copyOf(deviceToPrivateKey.values()));
}
private final class AllowedDevicesMonitor implements DataTreeChangeListener<Device> {
/**
* Authentication provider.
*/
-public interface CallHomeTlsAuthProvider extends SslHandlerFactory {
+public abstract class CallHomeTlsAuthProvider extends SslHandlerFactory {
- @Nullable String idFor(@NonNull PublicKey publicKey);
+ public abstract @Nullable String idFor(@NonNull PublicKey publicKey);
}
import com.google.common.util.concurrent.SettableFuture;
import io.netty.channel.Channel;
import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
-import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Promise;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
+import java.net.SocketAddress;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.opendaylight.netconf.transport.api.TransportChannel;
import org.opendaylight.netconf.transport.api.TransportChannelListener;
import org.opendaylight.netconf.transport.tcp.BootstrapFactory;
+import org.opendaylight.netconf.transport.tls.FixedSslHandlerFactory;
import org.opendaylight.netconf.transport.tls.TLSServer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
// Auth provider
final var authProvider = new CallHomeTlsAuthProvider() {
@Override
- public @Nullable String idFor(@NonNull
- final PublicKey publicKey) {
+ public String idFor(final PublicKey publicKey) {
// identify client 3 only
return clientCert3.keyPair.getPublic().equals(publicKey) ? "client-id" : null;
}
@Override
- public SslHandler createSslHandler(final Channel channel) {
- return serverCtx.newHandler(channel.alloc());
+ protected SslContext getSslContext(final SocketAddress remoteAddress) {
+ return serverCtx;
}
};
// client 1 rejected on handshake, ensure exception
client1 = TLSServer.connect(
netconfTransportListener, bootstrapFactory.newBootstrap(), tcpConnectParams,
- channel -> clientCtx1.newHandler(channel.alloc())).get(TIMEOUT, TimeUnit.MILLISECONDS);
+ new FixedSslHandlerFactory(clientCtx1)).get(TIMEOUT, TimeUnit.MILLISECONDS);
verify(statusRecorder, timeout(TIMEOUT).times(1))
.onTransportChannelFailure(any(SSLHandshakeException.class));
// client 2 rejected because it's not identified by public key accepted on handshake stage
client2 = TLSServer.connect(
netconfTransportListener, bootstrapFactory.newBootstrap(), tcpConnectParams,
- channel -> clientCtx2.newHandler(channel.alloc())).get(TIMEOUT, TimeUnit.MILLISECONDS);
+ new FixedSslHandlerFactory(clientCtx2)).get(TIMEOUT, TimeUnit.MILLISECONDS);
verify(statusRecorder, timeout(TIMEOUT).times(1))
.reportUnknown(any(InetSocketAddress.class), eq(clientCert2.keyPair.getPublic()));
// client 3 accepted
client3 = TLSServer.connect(
netconfTransportListener, bootstrapFactory.newBootstrap(), tcpConnectParams,
- channel -> clientCtx3.newHandler(channel.alloc())).get(TIMEOUT, TimeUnit.MILLISECONDS);
+ new FixedSslHandlerFactory(clientCtx3)).get(TIMEOUT, TimeUnit.MILLISECONDS);
// verify netconf session established
verify(clientSessionListener, timeout(TIMEOUT).times(1)).onSessionUp(any(NetconfClientSession.class));
verify(serverSessionListener, timeout(TIMEOUT).times(1)).onSessionUp(any(NetconfServerSession.class));
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
import org.opendaylight.netconf.client.mdsal.impl.DefaultSchemaResourceManager;
import org.opendaylight.netconf.common.NetconfTimer;
import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringRpcException;
@Mock
private CredentialProvider credentialProvider;
@Mock
- private SslHandlerFactoryProvider sslHandlerFactoryProvider;
+ private SslContextFactoryProvider sslHandlerFactoryProvider;
@Mock
private DOMMountPointListener masterMountPointListener;
private final DOMMountPointService masterMountPointService = new DOMMountPointServiceImpl();
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
import org.opendaylight.netconf.shaded.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
import org.opendaylight.netconf.shaded.sshd.common.keyprovider.KeyIdentityProvider;
+import org.opendaylight.netconf.transport.tls.FixedSslHandlerFactory;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev231228.password.grouping.password.type.CleartextPasswordBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev231228.netconf.client.initiate.stack.grouping.transport.ssh.ssh.SshClientParametersBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev231228.netconf.client.initiate.stack.grouping.transport.ssh.ssh.TcpClientParametersBuilder;
@Component
@Singleton
public final class NetconfClientConfigurationBuilderFactoryImpl implements NetconfClientConfigurationBuilderFactory {
- private final SslHandlerFactoryProvider sslHandlerFactoryProvider;
+ private final SslContextFactoryProvider sslContextFactoryProvider;
private final AAAEncryptionService encryptionService;
private final CredentialProvider credentialProvider;
public NetconfClientConfigurationBuilderFactoryImpl(
@Reference final AAAEncryptionService encryptionService,
@Reference final CredentialProvider credentialProvider,
- @Reference final SslHandlerFactoryProvider sslHandlerFactoryProvider) {
+ @Reference final SslContextFactoryProvider sslHandlerContextProvider) {
this.encryptionService = requireNonNull(encryptionService);
this.credentialProvider = requireNonNull(credentialProvider);
- this.sslHandlerFactoryProvider = requireNonNull(sslHandlerFactoryProvider);
+ sslContextFactoryProvider = requireNonNull(sslHandlerContextProvider);
}
@Override
builder.withProtocol(NetconfClientProtocol.SSH);
setSshParametersFromCredentials(builder, node.getCredentials());
} else if (protocol.getName() == Name.TLS) {
- final var handlerFactory = sslHandlerFactoryProvider.getSslHandlerFactory(protocol.getSpecification());
+ final var handlerFactory = sslContextFactoryProvider.getSslContextFactory(protocol.getSpecification());
+ final var sslContext = handlerFactory.createSslContext();
builder.withProtocol(NetconfClientProtocol.TLS)
- .withSslHandlerFactory(channel -> handlerFactory.createSslHandler());
+ .withSslHandlerFactory(new FixedSslHandlerFactory(sslContext));
} else {
throw new IllegalArgumentException("Unsupported protocol type: " + protocol.getName());
}
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
+import io.netty.handler.ssl.SslContext;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opendaylight.aaa.encrypt.AAAEncryptionService;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.netconf.client.SslHandlerFactory;
+import org.opendaylight.netconf.client.SslContextFactory;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
@Mock
private CredentialProvider credentialProvider;
@Mock
- private SslHandlerFactoryProvider sslHandlerFactoryProvider;
+ private SslContextFactoryProvider sslContextFactoryProvider;
@Mock
- private SslHandlerFactory sslHandlerFactory;
+ private SslContextFactory sslContextFactory;
+ @Mock
+ private SslContext sslContext;
private NetconfNodeBuilder nodeBuilder;
private NetconfClientConfigurationBuilderFactoryImpl factory;
.setBackoffMultiplier(Decimal64.valueOf("1.5"))
.setConnectionTimeoutMillis(Uint32.valueOf(20000));
factory = new NetconfClientConfigurationBuilderFactoryImpl(encryptionService, credentialProvider,
- sslHandlerFactoryProvider);
+ sslContextFactoryProvider);
}
private void assertConfig(final NetconfClientConfiguration config) {
@Test
void testTls() {
- doReturn(sslHandlerFactory).when(sslHandlerFactoryProvider).getSslHandlerFactory(any());
+ doReturn(sslContextFactory).when(sslContextFactoryProvider).getSslContextFactory(any());
+ doReturn(sslContext).when(sslContextFactory).createSslContext();
final var config = createConfig(
nodeBuilder.setTcpOnly(false).setProtocol(new ProtocolBuilder().setName(Name.TLS).build()).build());
assertConfig(config);
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
import org.opendaylight.netconf.client.mdsal.impl.DefaultBaseNetconfSchemas;
import org.opendaylight.netconf.common.NetconfTimer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
// DefaultNetconfClientConfigurationBuilderFactory setup
@Mock
- private SslHandlerFactoryProvider sslHandlerFactoryProvider;
+ private SslContextFactoryProvider sslContextFactoryProvider;
@Mock
private AAAEncryptionService encryptionService;
@Mock
// Instantiate the handler
handler = new NetconfNodeHandler(clientFactory, timer, BASE_SCHEMAS, schemaManager, schemaAssembler,
new NetconfClientConfigurationBuilderFactoryImpl(encryptionService, credentialProvider,
- sslHandlerFactoryProvider),
+ sslContextFactoryProvider),
deviceActionFactory, delegate, DEVICE_ID, NODE_ID, new NetconfNodeBuilder()
.setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
.setPort(new PortNumber(Uint16.valueOf(9999)))
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.netconf.client.SslHandlerFactory;
+import org.opendaylight.netconf.client.SslContextFactory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.parameters.protocol.Specification;
/**
- * A provider for {@link SslHandlerFactory} implementations. This allows the factory to be tailored with a
+ * A provider for {@link SslContextFactory} implementations. This allows the factory to be tailored with a
* {@link Specification}.
*/
-public interface SslHandlerFactoryProvider {
+public interface SslContextFactoryProvider {
/**
- * Return a {@link SslHandlerFactory}, optionally conforming to a particular specification.
+ * Return a {@link SslContextFactory}, optionally conforming to a particular specification.
*
* @param specification A {@link Specification}, may be {@code null}
- * @return A {@link SslHandlerFactory}
+ * @return A {@link SslContextFactory}
* @throws IllegalArgumentException if {@code specification} is not {@code null} and it is not supported by this
* provider.
*/
- @NonNull SslHandlerFactory getSslHandlerFactory(@Nullable Specification specification);
+ @NonNull SslContextFactory getSslContextFactory(@Nullable Specification specification);
}
--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.netconf.client.mdsal.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.JdkSslContext;
+import io.netty.handler.ssl.SslContext;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.Set;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.netconf.client.SslContextFactory;
+
+class DefaultSslContextFactory implements SslContextFactory {
+ private final DefaultSslContextFactoryProvider keyStoreProvider;
+
+ DefaultSslContextFactory(final DefaultSslContextFactoryProvider keyStoreProvider) {
+ this.keyStoreProvider = requireNonNull(keyStoreProvider);
+ }
+
+ @Override
+ public final SslContext createSslContext(final Set<String> allowedKeys) {
+ final SSLContext sslContext;
+ try {
+ final var keyStore = keyStoreProvider.getJavaKeyStore(allowedKeys);
+
+ final var kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, "".toCharArray());
+
+ final var tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(keyStore);
+
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ } catch (IOException | GeneralSecurityException e) {
+ throw new IllegalStateException("Failed to initialize SSL context", e);
+ }
+
+ return wrapSslContext(new JdkSslContext(sslContext, true, ClientAuth.NONE));
+ }
+
+ @NonNull SslContext wrapSslContext(final @NonNull SslContext sslContext) {
+ return sslContext;
+ }
+}
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.netconf.client.SslHandlerFactory;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
+import org.opendaylight.netconf.client.SslContextFactory;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
import org.opendaylight.netconf.keystore.legacy.NetconfKeystore;
import org.opendaylight.netconf.keystore.legacy.NetconfKeystoreService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.parameters.protocol.Specification;
import org.osgi.service.component.annotations.Reference;
@Singleton
-@Component(service = SslHandlerFactoryProvider.class)
-public final class DefaultSslHandlerFactoryProvider implements SslHandlerFactoryProvider, AutoCloseable {
+@Component(service = SslContextFactoryProvider.class)
+public final class DefaultSslContextFactoryProvider implements SslContextFactoryProvider, AutoCloseable {
private static final X509Certificate[] EMPTY_CERTS = { };
private static final char[] EMPTY_CHARS = { };
- private final @NonNull SslHandlerFactory nospecFactory = new SslHandlerFactoryImpl(this, Set.of());
+ private final @NonNull DefaultSslContextFactory nospecFactory = new DefaultSslContextFactory(this);
private final Registration reg;
private volatile @NonNull NetconfKeystore keystore = NetconfKeystore.EMPTY;
@Inject
@Activate
- public DefaultSslHandlerFactoryProvider(@Reference final NetconfKeystoreService keystoreService) {
+ public DefaultSslContextFactoryProvider(@Reference final NetconfKeystoreService keystoreService) {
reg = keystoreService.registerKeystoreConsumer(this::onKeystoreUpdated);
}
}
@Override
- public SslHandlerFactory getSslHandlerFactory(final Specification specification) {
+ public SslContextFactory getSslContextFactory(final Specification specification) {
if (specification == null) {
return nospecFactory;
- }
- if (specification instanceof TlsCase tlsSpecification) {
+ } else if (specification instanceof TlsCase tlsSpecification) {
final var excludedVersions = tlsSpecification.nonnullTls().getExcludedVersions();
return excludedVersions == null || excludedVersions.isEmpty() ? nospecFactory
- : new SslHandlerFactoryImpl(this, excludedVersions);
+ : new FilteredSslContextFactory(this, excludedVersions);
+ } else {
+ throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
}
- throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
}
/**
--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.netconf.client.mdsal.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import io.netty.handler.ssl.DelegatingSslContext;
+import io.netty.handler.ssl.SslContext;
+import java.util.Arrays;
+import java.util.Set;
+import javax.net.ssl.SSLEngine;
+
+final class FilteredSslContext extends DelegatingSslContext {
+ private final Set<String> excludedVersions;
+
+ FilteredSslContext(final SslContext ctx, final Set<String> excludedVersions) {
+ super(ctx);
+ this.excludedVersions = requireNonNull(excludedVersions);
+ }
+
+ @Override
+ protected void initEngine(final SSLEngine engine) {
+ engine.setEnabledProtocols(Arrays.stream(engine.getSupportedProtocols())
+ .filter(protocol -> !excludedVersions.contains(protocol))
+ .toArray(String[]::new));
+ engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
+ engine.setEnableSessionCreation(true);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.netconf.client.mdsal.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import io.netty.handler.ssl.SslContext;
+import java.util.Set;
+
+final class FilteredSslContextFactory extends DefaultSslContextFactory {
+ private final Set<String> excludedVersions;
+
+ FilteredSslContextFactory(final DefaultSslContextFactoryProvider keyStoreProvider,
+ final Set<String> excludedVersions) {
+ super(keyStoreProvider);
+ this.excludedVersions = requireNonNull(excludedVersions);
+ }
+
+ @Override
+ SslContext wrapSslContext(final SslContext sslContext) {
+ return new FilteredSslContext(sslContext, excludedVersions);
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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.netconf.client.mdsal.impl;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.collect.Sets;
-import io.netty.handler.ssl.SslHandler;
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.util.Set;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.TrustManagerFactory;
-import org.opendaylight.netconf.client.SslHandlerFactory;
-
-final class SslHandlerFactoryImpl implements SslHandlerFactory {
- private final DefaultSslHandlerFactoryProvider keyStoreProvider;
- private final Set<String> excludedVersions;
-
- SslHandlerFactoryImpl(final DefaultSslHandlerFactoryProvider keyStoreProvider, final Set<String> excludedVersions) {
- this.keyStoreProvider = requireNonNull(keyStoreProvider);
- this.excludedVersions = requireNonNull(excludedVersions);
- }
-
- @Override
- public SslHandler createSslHandler(final Set<String> allowedKeys) {
- try {
- final KeyStore keyStore = keyStoreProvider.getJavaKeyStore(allowedKeys);
-
- final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(keyStore, "".toCharArray());
-
- final TrustManagerFactory tmf =
- TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- tmf.init(keyStore);
-
- final SSLContext sslCtx = SSLContext.getInstance("TLS");
- sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
- final SSLEngine engine = sslCtx.createSSLEngine();
- engine.setUseClientMode(true);
-
- final String[] engineProtocols = engine.getSupportedProtocols();
- final String[] enabledProtocols;
- if (!excludedVersions.isEmpty()) {
- final var protocols = Sets.newHashSet(engineProtocols);
- protocols.removeAll(excludedVersions);
- enabledProtocols = protocols.toArray(new String[0]);
- } else {
- enabledProtocols = engineProtocols;
- }
-
- engine.setEnabledProtocols(enabledProtocols);
- engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
- engine.setEnableSessionCreation(true);
- return new SslHandler(engine);
- } catch (GeneralSecurityException | IOException exc) {
- throw new IllegalStateException(exc);
- }
- }
-}
\ No newline at end of file
import org.w3c.dom.Element;
@ExtendWith(MockitoExtension.class)
-class DefaultSslHandlerFactoryProviderTest {
+class DefaultSslContextFactoryProviderTest {
private static final String XML_ELEMENT_PRIVATE_KEY = "private-key";
private static final String XML_ELEMENT_NAME = "name";
private static final String XML_ELEMENT_DATA = "data";
keystore.close();
}
- private DefaultSslHandlerFactoryProvider newProvider() {
- return new DefaultSslHandlerFactoryProvider(keystore);
+ private DefaultSslContextFactoryProvider newProvider() {
+ return new DefaultSslContextFactoryProvider(keystore);
}
@Test
private static Document readKeystoreXML() throws Exception {
return XmlUtil.readXmlToDocument(
- DefaultSslHandlerFactoryProviderTest.class.getResourceAsStream("/netconf-keystore.xml"));
+ DefaultSslContextFactoryProviderTest.class.getResourceAsStream("/netconf-keystore.xml"));
}
}
import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory;
import org.opendaylight.netconf.transport.tcp.TCPClient;
+import org.opendaylight.netconf.transport.tls.FixedSslHandlerFactory;
import org.opendaylight.netconf.transport.tls.TLSClient;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
import org.osgi.service.component.annotations.Activate;
} else if (TLS.equals(protocol)) {
if (configuration.getTlsParameters() != null) {
TLSClient.connect(new ClientTransportChannelListener(future, channelInitializer), bootstrap,
- configuration.getTcpParameters(), configuration.getTlsParameters());
+ configuration.getTcpParameters(), new FixedSslHandlerFactory(configuration.getTlsParameters()));
} else {
TLSClient.connect(new ClientTransportChannelListener(future, channelInitializer), bootstrap,
configuration.getTcpParameters(), configuration.getSslHandlerFactory());
--- /dev/null
+/*
+ * Copyright (c) 2018 ZTE Corporation. 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.netconf.client;
+
+import com.google.common.annotations.Beta;
+import io.netty.handler.ssl.SslContext;
+import java.util.Set;
+
+/**
+ * Basic interface for instantiating a {@link SslContext}. Used to establish TSL connection.
+ */
+@Beta
+public interface SslContextFactory {
+ /**
+ * This factory is used by the TLS client to create SslHandler that will be added into the channel pipeline when
+ * the channel is active.
+ */
+ default SslContext createSslContext() {
+ return createSslContext(Set.of());
+ }
+
+ SslContext createSslContext(Set<String> allowedKeys);
+}
+++ /dev/null
-/*
- * Copyright (c) 2018 ZTE Corporation. 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.netconf.client;
-
-import io.netty.handler.ssl.SslHandler;
-import java.util.Set;
-
-/**
- * Basic interface for {@link SslHandler} builder. Used to establish TSL connection.
- *
- * @deprecated due to design change. SslHandler will be created dynamically based on TLS layer configuration
- * {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.TlsClientGrouping}
- * by {@link NetconfClientFactory}.
- */
-@Deprecated
-public interface SslHandlerFactory {
- /**
- * This factory is used by the TLS client to create SslHandler that will be added into the channel pipeline when
- * the channel is active.
- */
- default SslHandler createSslHandler() {
- return createSslHandler(Set.of());
- }
-
- SslHandler createSslHandler(Set<String> allowedKeys);
-}
import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory;
import org.opendaylight.netconf.transport.ssh.ServerFactoryManagerConfigurator;
import org.opendaylight.netconf.transport.tcp.TCPServer;
+import org.opendaylight.netconf.transport.tls.FixedSslHandlerFactory;
import org.opendaylight.netconf.transport.tls.TLSServer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.crypt.hash.rev140806.CryptHash;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev231228.RsaPrivateKeyFormat;
final var clientContext = SslContextBuilder.forClient().keyManager(keyMgr).trustManager(trustMgr).build();
final var server = TLSServer.listen(serverTransportListener, SERVER_FACTORY.newServerBootstrap(),
- tcpServerParams, channel -> serverContext.newHandler(channel.alloc())).get(1, TimeUnit.SECONDS);
+ tcpServerParams, new FixedSslHandlerFactory(serverContext)).get(1, TimeUnit.SECONDS);
try {
final var clientConfig = NetconfClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS)
.withTcpParameters(tcpClientParams)
- .withSslHandlerFactory(channel -> clientContext.newHandler(channel.alloc()))
+ .withSslHandlerFactory(new FixedSslHandlerFactory(clientContext))
.withSessionListener(sessionListener).build();
assertNotNull(factory.createClient(clientConfig));
verify(serverTransportListener, timeout(1000L))
--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.netconf.transport.tls;
+
+import static java.util.Objects.requireNonNull;
+
+import io.netty.handler.ssl.SslContext;
+import java.net.SocketAddress;
+import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.TlsClientGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228.TlsServerGrouping;
+
+public final class FixedSslHandlerFactory extends SslHandlerFactory {
+ private final SslContext sslContext;
+
+ public FixedSslHandlerFactory(final SslContext sslContext) {
+ this.sslContext = requireNonNull(sslContext);
+ }
+
+ public FixedSslHandlerFactory(final TlsClientGrouping clientParams) throws UnsupportedConfigurationException {
+ this(createSslContext(clientParams));
+ }
+
+ public FixedSslHandlerFactory(final TlsServerGrouping serverParams) throws UnsupportedConfigurationException {
+ this(createSslContext(serverParams));
+ }
+
+ @Override
+ protected SslContext getSslContext(final SocketAddress remoteAddress) {
+ return sslContext;
+ }
+}
* @return key store instance
* @throws UnsupportedConfigurationException if key store cannot be instantiated
*/
- static KeyStore newKeyStore() throws UnsupportedConfigurationException {
+ static @NonNull KeyStore newKeyStore() throws UnsupportedConfigurationException {
final KeyStore keyStore;
try {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
/*
- * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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,
*/
package org.opendaylight.netconf.transport.tls;
-import com.google.common.annotations.Beta;
+import static org.opendaylight.netconf.transport.tls.ConfigUtils.setAsymmetricKey;
+import static org.opendaylight.netconf.transport.tls.ConfigUtils.setEndEntityCertificateWithKey;
+import static org.opendaylight.netconf.transport.tls.ConfigUtils.setX509Certificates;
+import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.buildKeyManagerFactory;
+import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.buildTrustManagerFactory;
+import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.newKeyStore;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import io.netty.channel.Channel;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
+import java.net.SocketAddress;
+import java.security.KeyStore;
+import java.util.List;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.CipherSuiteAlgBase;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes128CcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes128GcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes256GcmSha384;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsChacha20Poly1305Sha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes128Ccm;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes128GcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes256Ccm;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes256GcmSha384;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithChacha20Poly1305Sha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes128Ccm;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes128GcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes256Ccm;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes256GcmSha384;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithChacha20Poly1305Sha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithAes128GcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithAes256GcmSha384;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithChacha20Poly1305Sha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes128CcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes128GcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes256GcmSha384;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithChacha20Poly1305Sha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithAes128GcmSha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithAes256GcmSha384;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithChacha20Poly1305Sha256;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.keystore.rev231228.InlineOrKeystoreAsymmetricKeyGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.keystore.rev231228.InlineOrKeystoreEndEntityCertWithKeyGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.TlsClientGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.tls.client.grouping.client.identity.auth.type.Certificate;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.tls.client.grouping.client.identity.auth.type.RawPublicKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.common.rev231228.HelloParamsGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.common.rev231228.TlsVersionBase;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228.TlsServerGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228.tls.server.grouping.server.identity.auth.type.RawPrivateKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.truststore.rev231228.InlineOrTruststoreCertsGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.truststore.rev231228.InlineOrTruststorePublicKeysGrouping;
/**
* Extension interface for external service integration with TLS transport. Used to build {@link TLSClient} and
* {@link TLSServer} instances.
*/
-@Beta
-@FunctionalInterface
-public interface SslHandlerFactory {
+public abstract class SslHandlerFactory {
+ private static final ImmutableMap<CipherSuiteAlgBase, String> CIPHER_SUITES =
+ ImmutableMap.<CipherSuiteAlgBase, String>builder()
+ .put(TlsAes128CcmSha256.VALUE, "TLS_AES_128_CCM_SHA256")
+ .put(TlsAes128GcmSha256.VALUE, "TLS_AES_128_GCM_SHA256")
+ .put(TlsAes256GcmSha384.VALUE, "TLS_AES_256_GCM_SHA384")
+ .put(TlsChacha20Poly1305Sha256.VALUE, "TLS_CHACHA20_POLY1305_SHA256")
+ .put(TlsDhePskWithAes128Ccm.VALUE, "TLS_DHE_PSK_WITH_AES_128_CCM")
+ .put(TlsDhePskWithAes128GcmSha256.VALUE, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256")
+ .put(TlsDhePskWithAes256Ccm.VALUE, "TLS_DHE_PSK_WITH_AES_256_CCM")
+ .put(TlsDhePskWithAes256GcmSha384.VALUE, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384")
+ .put(TlsDhePskWithChacha20Poly1305Sha256.VALUE, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256")
+ .put(TlsDheRsaWithAes128Ccm.VALUE, "TLS_DHE_RSA_WITH_AES_128_CCM")
+ .put(TlsDheRsaWithAes128GcmSha256.VALUE, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
+ .put(TlsDheRsaWithAes256Ccm.VALUE, "TLS_DHE_RSA_WITH_AES_256_CCM")
+ .put(TlsDheRsaWithAes256GcmSha384.VALUE, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")
+ .put(TlsDheRsaWithChacha20Poly1305Sha256.VALUE, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
+ .put(TlsEcdheEcdsaWithAes128GcmSha256.VALUE, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
+ .put(TlsEcdheEcdsaWithAes256GcmSha384.VALUE, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
+ .put(TlsEcdheEcdsaWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
+ .put(TlsEcdhePskWithAes128CcmSha256.VALUE, "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256")
+ .put(TlsEcdhePskWithAes128GcmSha256.VALUE, "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256")
+ .put(TlsEcdhePskWithAes256GcmSha384.VALUE, "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384")
+ .put(TlsEcdhePskWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256")
+ .put(TlsEcdheRsaWithAes128GcmSha256.VALUE, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
+ .put(TlsEcdheRsaWithAes256GcmSha384.VALUE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
+ .put(TlsEcdheRsaWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
+ .build();
+
/**
* Builds {@link SslHandler} instance for given {@link Channel}.
*
* @param channel channel
- * @return A SslHandler
+ * @return A {@link SslHandler}, or {@code null} if the connection should be rejected
*/
- SslHandler createSslHandler(Channel channel);
+ public final @Nullable SslHandler createSslHandler(final @NonNull Channel channel) {
+ final var sslContext = getSslContext(channel.remoteAddress());
+ return sslContext == null ? null : sslContext.newHandler(channel.alloc());
+ }
+
+ protected abstract @Nullable SslContext getSslContext(SocketAddress remoteAddress);
+
+ protected static final @NonNull SslContext createSslContext(final @NonNull TlsClientGrouping clientParams)
+ throws UnsupportedConfigurationException {
+ final var builder = SslContextBuilder.forClient();
+
+ final var clientIdentity = clientParams.getClientIdentity();
+ if (clientIdentity != null) {
+ final var authType = clientIdentity.getAuthType();
+ if (authType instanceof Certificate cert) {
+ // if-feature "client-ident-x509-cert"
+ final var certificate = cert.getCertificate();
+ if (certificate == null) {
+ throw new UnsupportedConfigurationException("Missing certificate in " + cert);
+ }
+ builder.keyManager(newKeyManager(certificate));
+ } else if (authType instanceof RawPublicKey rawKey) {
+ // if-feature "client-ident-raw-public-key"
+ final var rawPrivateKey = rawKey.getRawPrivateKey();
+ if (rawPrivateKey == null) {
+ throw new UnsupportedConfigurationException("Missing key in " + rawKey);
+ }
+ builder.keyManager(newKeyManager(rawPrivateKey));
+ } else if (authType != null) {
+ throw new UnsupportedConfigurationException("Unsupported client authentication type " + authType);
+ }
+ }
+
+ final var serverAuth = clientParams.getServerAuthentication();
+ if (serverAuth != null) {
+ // CA && EE X509 certificates : if-feature "server-ident-x509-cert"
+ // Raw public key : if-feature "server-ident-raw-public-key"
+ final var trustManager = newTrustManager(serverAuth.getCaCerts(), serverAuth.getEeCerts(),
+ serverAuth.getRawPublicKeys());
+ if (trustManager == null) {
+ throw new UnsupportedOperationException("No server authentication methods in " + serverAuth);
+ }
+ builder.trustManager(trustManager);
+ }
+
+ return buildSslContext(builder, clientParams.getHelloParams());
+ }
+
+ protected static final @NonNull SslContext createSslContext(final @NonNull TlsServerGrouping serverParams)
+ throws UnsupportedConfigurationException {
+ final var serverIdentity = serverParams.getServerIdentity();
+ if (serverIdentity == null) {
+ throw new UnsupportedConfigurationException("Missing server identity");
+ }
+ final SslContextBuilder builder;
+ final var authType = serverIdentity.getAuthType();
+ if (authType
+ instanceof org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228
+ .tls.server.grouping.server.identity.auth.type.Certificate cert) {
+ // if-feature "server-ident-x509-cert"
+ final var certificate = cert.getCertificate();
+ if (certificate == null) {
+ throw new UnsupportedConfigurationException("Missing certificate in " + cert);
+ }
+ builder = SslContextBuilder.forServer(newKeyManager(certificate));
+ } else if (authType instanceof RawPrivateKey rawKey) {
+ // if-feature "server-ident-raw-public-key"
+ final var rawPrivateKey = rawKey.getRawPrivateKey();
+ if (rawPrivateKey == null) {
+ throw new UnsupportedConfigurationException("Missing key in " + rawKey);
+ }
+ builder = SslContextBuilder.forServer(newKeyManager(rawPrivateKey));
+ } else if (authType != null) {
+ throw new UnsupportedConfigurationException("Unsupported server authentication type " + authType);
+ } else {
+ throw new UnsupportedConfigurationException("Missing server authentication type");
+ }
+
+ final var clientAuth = serverParams.getClientAuthentication();
+ if (clientAuth != null) {
+ // CA && EE Certs : if-feature "client-ident-x509-cert"
+ // Raw public keys : if-feature "client-ident-raw-public-key"
+ final var trustManager = newTrustManager(clientAuth.getCaCerts(), clientAuth.getEeCerts(),
+ clientAuth.getRawPublicKeys());
+ if (trustManager == null) {
+ throw new UnsupportedOperationException("No client authentication methods in " + clientAuth);
+ }
+ builder.clientAuth(ClientAuth.REQUIRE).trustManager(trustManager);
+ } else {
+ builder.clientAuth(ClientAuth.NONE);
+ }
+
+ return buildSslContext(builder, serverParams.getHelloParams());
+ }
+
+ // FIXME: should be TrustManagerBuilder
+ private static @Nullable TrustManagerFactory newTrustManager(
+ final @Nullable InlineOrTruststoreCertsGrouping caCerts,
+ final @Nullable InlineOrTruststoreCertsGrouping eeCerts,
+ final @Nullable InlineOrTruststorePublicKeysGrouping publicKeys) throws UnsupportedConfigurationException {
+
+ if (publicKeys != null) {
+ // FIXME: implement this and advertize server-auth-raw-public-key from IetfTlsClientFeatureProvider
+ throw new UnsupportedConfigurationException("Public key authentication not implemented");
+ }
+ if (caCerts != null || eeCerts != null) {
+ // X.509 certificates
+ final KeyStore keyStore = newKeyStore();
+ setX509Certificates(keyStore, caCerts, eeCerts);
+ return buildTrustManagerFactory(keyStore);
+ }
+ return null;
+ }
+
+ private static KeyManagerFactory newKeyManager(
+ final @NonNull InlineOrKeystoreEndEntityCertWithKeyGrouping endEntityCert)
+ throws UnsupportedConfigurationException {
+ final var keyStore = newKeyStore();
+ setEndEntityCertificateWithKey(keyStore, endEntityCert);
+ return buildKeyManagerFactory(keyStore);
+ }
+
+ private static KeyManagerFactory newKeyManager(final @NonNull InlineOrKeystoreAsymmetricKeyGrouping rawPrivateKey)
+ throws UnsupportedConfigurationException {
+ final var keyStore = newKeyStore();
+ setAsymmetricKey(keyStore, rawPrivateKey);
+ return buildKeyManagerFactory(keyStore);
+ }
+
+ private static @NonNull SslContext buildSslContext(final SslContextBuilder builder,
+ final HelloParamsGrouping helloParams) throws UnsupportedConfigurationException {
+ if (helloParams != null) {
+ final var tlsVersions = helloParams.getTlsVersions();
+ if (tlsVersions != null) {
+ final var versions = tlsVersions.getTlsVersion();
+ if (versions != null && !versions.isEmpty()) {
+ builder.protocols(createTlsStrings(versions));
+ }
+ }
+ final var cipherSuites = helloParams.getCipherSuites();
+ if (cipherSuites != null) {
+ final var ciphers = cipherSuites.getCipherSuite();
+ if (ciphers != null && !ciphers.isEmpty()) {
+ builder.ciphers(createCipherStrings(ciphers));
+ }
+ }
+ }
+ try {
+ return builder.build();
+ } catch (SSLException e) {
+ throw new UnsupportedConfigurationException("Cannot instantiate TLS context", e);
+ }
+ }
+
+ private static String[] createTlsStrings(final List<TlsVersionBase> versions)
+ throws UnsupportedConfigurationException {
+ // FIXME: cache these
+ final var ret = new String[versions.size()];
+ int idx = 0;
+ for (var version : versions) {
+ final var str = IetfTlsCommonFeatureProvider.algorithmNameOf(version);
+ if (str == null) {
+ throw new UnsupportedConfigurationException("Unhandled TLS version " + version);
+ }
+ ret[idx++] = str;
+ }
+ return ret;
+ }
+
+ private static ImmutableList<String> createCipherStrings(final List<CipherSuiteAlgBase> ciphers)
+ throws UnsupportedConfigurationException {
+ // FIXME: cache these
+ final var builder = ImmutableList.<String>builderWithExpectedSize(ciphers.size());
+ for (var cipher : ciphers) {
+ final var str = CIPHER_SUITES.get(cipher);
+ if (str == null) {
+ throw new UnsupportedConfigurationException("Unhandled cipher suite " + cipher);
+ }
+ builder.add(str);
+ }
+ return builder.build();
+ }
}
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.netconf.transport.api.TransportChannelListener;
import org.opendaylight.netconf.transport.api.TransportStack;
import org.opendaylight.netconf.transport.tcp.TCPServer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev231228.TcpClientGrouping;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev231228.TcpServerGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.TlsClientGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.tls.client.grouping.client.identity.auth.type.Certificate;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev231228.tls.client.grouping.client.identity.auth.type.RawPublicKey;
/**
* A {@link TransportStack} acting as a TLS client.
super(listener, factory);
}
- public static @NonNull ListenableFuture<TLSClient> connect(final TransportChannelListener listener,
- final Bootstrap bootstrap, final TcpClientGrouping connectParams, final TlsClientGrouping clientParams)
- throws UnsupportedConfigurationException {
- final var client = newClient(listener, clientParams);
- return transformUnderlay(client, TCPClient.connect(client.asListener(), bootstrap, connectParams));
- }
-
public static @NonNull ListenableFuture<TLSClient> connect(final TransportChannelListener listener,
final Bootstrap bootstrap, final TcpClientGrouping connectParams, final SslHandlerFactory factory)
throws UnsupportedConfigurationException {
return transformUnderlay(client, TCPClient.connect(client.asListener(), bootstrap, connectParams));
}
- public static @NonNull ListenableFuture<TLSClient> listen(final TransportChannelListener listener,
- final ServerBootstrap bootstrap, final TcpServerGrouping listenParams, final TlsClientGrouping clientParams)
- throws UnsupportedConfigurationException {
- final var client = newClient(listener, clientParams);
- return transformUnderlay(client, TCPServer.listen(client.asListener(), bootstrap, listenParams));
- }
-
public static @NonNull ListenableFuture<TLSClient> listen(final TransportChannelListener listener,
final ServerBootstrap bootstrap, final TcpServerGrouping listenParams, final SslHandlerFactory factory)
throws UnsupportedConfigurationException {
final var client = new TLSClient(listener, factory);
return transformUnderlay(client, TCPServer.listen(client.asListener(), bootstrap, listenParams));
}
-
- private static TLSClient newClient(final TransportChannelListener listener, final TlsClientGrouping clientParams)
- throws UnsupportedConfigurationException {
- final var builder = SslContextBuilder.forClient();
-
- final var clientIdentity = clientParams.getClientIdentity();
- if (clientIdentity != null) {
- final var authType = clientIdentity.getAuthType();
- if (authType instanceof Certificate cert) {
- // if-feature "client-ident-x509-cert"
- final var certificate = cert.getCertificate();
- if (certificate == null) {
- throw new UnsupportedConfigurationException("Missing certificate in " + cert);
- }
- builder.keyManager(newKeyManager(certificate));
- } else if (authType instanceof RawPublicKey rawKey) {
- // if-feature "client-ident-raw-public-key"
- final var rawPrivateKey = rawKey.getRawPrivateKey();
- if (rawPrivateKey == null) {
- throw new UnsupportedConfigurationException("Missing key in " + rawKey);
- }
- builder.keyManager(newKeyManager(rawPrivateKey));
- } else if (authType != null) {
- throw new UnsupportedConfigurationException("Unsupported client authentication type " + authType);
- }
- }
-
- final var serverAuth = clientParams.getServerAuthentication();
- if (serverAuth != null) {
- // CA && EE X509 certificates : if-feature "server-ident-x509-cert"
- // Raw public key : if-feature "server-ident-raw-public-key"
- final var trustManager = newTrustManager(serverAuth.getCaCerts(), serverAuth.getEeCerts(),
- serverAuth.getRawPublicKeys());
- if (trustManager == null) {
- throw new UnsupportedOperationException("No server authentication methods in " + serverAuth);
- }
- builder.trustManager(trustManager);
- }
-
- return new TLSClient(listener, buildSslContext(builder, clientParams.getHelloParams()));
- }
}
import com.google.common.util.concurrent.ListenableFuture;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
-import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.netconf.transport.api.TransportChannelListener;
import org.opendaylight.netconf.transport.api.TransportStack;
import org.opendaylight.netconf.transport.tcp.TCPServer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev231228.TcpClientGrouping;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev231228.TcpServerGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228.TlsServerGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228.tls.server.grouping.server.identity.auth.type.Certificate;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev231228.tls.server.grouping.server.identity.auth.type.RawPrivateKey;
/**
* A {@link TransportStack} acting as a TLS server.
super(listener, factory);
}
- public static @NonNull ListenableFuture<TLSServer> connect(final TransportChannelListener listener,
- final Bootstrap bootstrap, final TcpClientGrouping connectParams, final TlsServerGrouping serverParams)
- throws UnsupportedConfigurationException {
- final var server = newServer(listener, serverParams);
- return transformUnderlay(server, TCPClient.connect(server.asListener(), bootstrap, connectParams));
- }
-
public static @NonNull ListenableFuture<TLSServer> connect(final TransportChannelListener listener,
final Bootstrap bootstrap, final TcpClientGrouping connectParams, final SslHandlerFactory factory)
throws UnsupportedConfigurationException {
return transformUnderlay(server, TCPClient.connect(server.asListener(), bootstrap, connectParams));
}
- public static @NonNull ListenableFuture<TLSServer> listen(final TransportChannelListener listener,
- final ServerBootstrap bootstrap, final TcpServerGrouping listenParams, final TlsServerGrouping serverParams)
- throws UnsupportedConfigurationException {
- final var server = newServer(listener, serverParams);
- return transformUnderlay(server, TCPServer.listen(server.asListener(), bootstrap, listenParams));
- }
-
public static @NonNull ListenableFuture<TLSServer> listen(final TransportChannelListener listener,
final ServerBootstrap bootstrap, final TcpServerGrouping listenParams, final SslHandlerFactory factory)
throws UnsupportedConfigurationException {
final var server = new TLSServer(listener, factory);
return transformUnderlay(server, TCPServer.listen(server.asListener(), bootstrap, listenParams));
}
-
- private static TLSServer newServer(final TransportChannelListener listener, final TlsServerGrouping serverParams)
- throws UnsupportedConfigurationException {
- final var serverIdentity = serverParams.getServerIdentity();
- if (serverIdentity == null) {
- throw new UnsupportedConfigurationException("Missing server identity");
- }
- final SslContextBuilder builder;
- final var authType = serverIdentity.getAuthType();
- if (authType instanceof Certificate cert) {
- // if-feature "server-ident-x509-cert"
- final var certificate = cert.getCertificate();
- if (certificate == null) {
- throw new UnsupportedConfigurationException("Missing certificate in " + cert);
- }
- builder = SslContextBuilder.forServer(newKeyManager(certificate));
- } else if (authType instanceof RawPrivateKey rawKey) {
- // if-feature "server-ident-raw-public-key"
- final var rawPrivateKey = rawKey.getRawPrivateKey();
- if (rawPrivateKey == null) {
- throw new UnsupportedConfigurationException("Missing key in " + rawKey);
- }
- builder = SslContextBuilder.forServer(newKeyManager(rawPrivateKey));
- } else if (authType != null) {
- throw new UnsupportedConfigurationException("Unsupported server authentication type " + authType);
- } else {
- throw new UnsupportedConfigurationException("Missing server authentication type");
- }
-
- final var clientAuth = serverParams.getClientAuthentication();
- if (clientAuth != null) {
- // CA && EE Certs : if-feature "client-ident-x509-cert"
- // Raw public keys : if-feature "client-ident-raw-public-key"
- final var trustManager = newTrustManager(clientAuth.getCaCerts(), clientAuth.getEeCerts(),
- clientAuth.getRawPublicKeys());
- if (trustManager == null) {
- throw new UnsupportedOperationException("No client authentication methods in " + clientAuth);
- }
- builder.clientAuth(ClientAuth.REQUIRE).trustManager(trustManager);
- } else {
- builder.clientAuth(ClientAuth.NONE);
- }
- return new TLSServer(listener, buildSslContext(builder, serverParams.getHelloParams()));
- }
}
package org.opendaylight.netconf.transport.tls;
import static java.util.Objects.requireNonNull;
-import static org.opendaylight.netconf.transport.tls.ConfigUtils.setAsymmetricKey;
-import static org.opendaylight.netconf.transport.tls.ConfigUtils.setEndEntityCertificateWithKey;
-import static org.opendaylight.netconf.transport.tls.ConfigUtils.setX509Certificates;
-import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.buildKeyManagerFactory;
-import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.buildTrustManagerFactory;
-import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.newKeyStore;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
-import java.security.KeyStore;
-import java.util.List;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.TrustManagerFactory;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.netconf.transport.api.AbstractOverlayTransportStack;
import org.opendaylight.netconf.transport.api.TransportChannel;
import org.opendaylight.netconf.transport.api.TransportChannelListener;
-import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.CipherSuiteAlgBase;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes128CcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes128GcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes256GcmSha384;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsChacha20Poly1305Sha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes128Ccm;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes128GcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes256Ccm;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes256GcmSha384;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithChacha20Poly1305Sha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes128Ccm;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes128GcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes256Ccm;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes256GcmSha384;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithChacha20Poly1305Sha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithAes128GcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithAes256GcmSha384;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithChacha20Poly1305Sha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes128CcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes128GcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes256GcmSha384;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithChacha20Poly1305Sha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithAes128GcmSha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithAes256GcmSha384;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithChacha20Poly1305Sha256;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.keystore.rev231228.InlineOrKeystoreAsymmetricKeyGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.keystore.rev231228.InlineOrKeystoreEndEntityCertWithKeyGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.common.rev231228.HelloParamsGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.common.rev231228.TlsVersionBase;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.truststore.rev231228.InlineOrTruststoreCertsGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.truststore.rev231228.InlineOrTruststorePublicKeysGrouping;
/**
* Base class for TLS TransportStacks.
*/
public abstract sealed class TLSTransportStack extends AbstractOverlayTransportStack<TLSTransportChannel>
permits TLSClient, TLSServer {
-
- private static final ImmutableMap<CipherSuiteAlgBase, String> CIPHER_SUITES =
- ImmutableMap.<CipherSuiteAlgBase, String>builder()
- .put(TlsAes128CcmSha256.VALUE, "TLS_AES_128_CCM_SHA256")
- .put(TlsAes128GcmSha256.VALUE, "TLS_AES_128_GCM_SHA256")
- .put(TlsAes256GcmSha384.VALUE, "TLS_AES_256_GCM_SHA384")
- .put(TlsChacha20Poly1305Sha256.VALUE, "TLS_CHACHA20_POLY1305_SHA256")
- .put(TlsDhePskWithAes128Ccm.VALUE, "TLS_DHE_PSK_WITH_AES_128_CCM")
- .put(TlsDhePskWithAes128GcmSha256.VALUE, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256")
- .put(TlsDhePskWithAes256Ccm.VALUE, "TLS_DHE_PSK_WITH_AES_256_CCM")
- .put(TlsDhePskWithAes256GcmSha384.VALUE, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384")
- .put(TlsDhePskWithChacha20Poly1305Sha256.VALUE, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256")
- .put(TlsDheRsaWithAes128Ccm.VALUE, "TLS_DHE_RSA_WITH_AES_128_CCM")
- .put(TlsDheRsaWithAes128GcmSha256.VALUE, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
- .put(TlsDheRsaWithAes256Ccm.VALUE, "TLS_DHE_RSA_WITH_AES_256_CCM")
- .put(TlsDheRsaWithAes256GcmSha384.VALUE, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")
- .put(TlsDheRsaWithChacha20Poly1305Sha256.VALUE, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
- .put(TlsEcdheEcdsaWithAes128GcmSha256.VALUE, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
- .put(TlsEcdheEcdsaWithAes256GcmSha384.VALUE, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
- .put(TlsEcdheEcdsaWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
- .put(TlsEcdhePskWithAes128CcmSha256.VALUE, "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256")
- .put(TlsEcdhePskWithAes128GcmSha256.VALUE, "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256")
- .put(TlsEcdhePskWithAes256GcmSha384.VALUE, "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384")
- .put(TlsEcdhePskWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256")
- .put(TlsEcdheRsaWithAes128GcmSha256.VALUE, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
- .put(TlsEcdheRsaWithAes256GcmSha384.VALUE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
- .put(TlsEcdheRsaWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
- .build();
-
private final SslHandlerFactory factory;
TLSTransportStack(final TransportChannelListener listener, final SslContext sslContext) {
- this(listener, channel -> sslContext.newHandler(channel.alloc()));
+ this(listener, new FixedSslHandlerFactory(sslContext));
}
TLSTransportStack(final TransportChannelListener listener, final SslHandlerFactory factory) {
}
});
}
-
- static KeyManagerFactory newKeyManager(
- final @NonNull InlineOrKeystoreEndEntityCertWithKeyGrouping endEntityCert
- ) throws UnsupportedConfigurationException {
- final var keyStore = newKeyStore();
- setEndEntityCertificateWithKey(keyStore, endEntityCert);
- return buildKeyManagerFactory(keyStore);
- }
-
- static KeyManagerFactory newKeyManager(final @NonNull InlineOrKeystoreAsymmetricKeyGrouping rawPrivateKey)
- throws UnsupportedConfigurationException {
- final var keyStore = newKeyStore();
- setAsymmetricKey(keyStore, rawPrivateKey);
- return buildKeyManagerFactory(keyStore);
- }
-
- // FIXME: should be TrustManagerBuilder
- protected static @Nullable TrustManagerFactory newTrustManager(
- final @Nullable InlineOrTruststoreCertsGrouping caCerts,
- final @Nullable InlineOrTruststoreCertsGrouping eeCerts,
- final @Nullable InlineOrTruststorePublicKeysGrouping publicKeys) throws UnsupportedConfigurationException {
-
- if (publicKeys != null) {
- // FIXME: implement this and advertize server-auth-raw-public-key from IetfTlsClientFeatureProvider
- throw new UnsupportedConfigurationException("Public key authentication not implemented");
- }
- if (caCerts != null || eeCerts != null) {
- // X.509 certificates
- final KeyStore keyStore = newKeyStore();
- setX509Certificates(keyStore, caCerts, eeCerts);
- return buildTrustManagerFactory(keyStore);
- }
- return null;
- }
-
- static SslContext buildSslContext(final SslContextBuilder builder, final HelloParamsGrouping helloParams)
- throws UnsupportedConfigurationException {
- if (helloParams != null) {
- final var tlsVersions = helloParams.getTlsVersions();
- if (tlsVersions != null) {
- final var versions = tlsVersions.getTlsVersion();
- if (versions != null && !versions.isEmpty()) {
- builder.protocols(createTlsStrings(versions));
- }
- }
- final var cipherSuites = helloParams.getCipherSuites();
- if (cipherSuites != null) {
- final var ciphers = cipherSuites.getCipherSuite();
- if (ciphers != null && !ciphers.isEmpty()) {
- builder.ciphers(createCipherStrings(ciphers));
- }
- }
- }
- try {
- return builder.build();
- } catch (SSLException e) {
- throw new UnsupportedConfigurationException("Cannot instantiate TLS context", e);
- }
- }
-
- private static String[] createTlsStrings(final List<TlsVersionBase> versions)
- throws UnsupportedConfigurationException {
- // FIXME: cache these
- final var ret = new String[versions.size()];
- int idx = 0;
- for (var version : versions) {
- final var str = IetfTlsCommonFeatureProvider.algorithmNameOf(version);
- if (str == null) {
- throw new UnsupportedConfigurationException("Unhandled TLS version " + version);
- }
- ret[idx++] = str;
- }
- return ret;
- }
-
- private static ImmutableList<String> createCipherStrings(final List<CipherSuiteAlgBase> ciphers)
- throws UnsupportedConfigurationException {
- // FIXME: cache these
- final var builder = ImmutableList.<String>builderWithExpectedSize(ciphers.size());
- for (var cipher : ciphers) {
- final var str = CIPHER_SUITES.get(cipher);
- if (str == null) {
- throw new UnsupportedConfigurationException("Unhandled cipher suite " + cipher);
- }
- builder.add(str);
- }
- return builder.build();
- }
}
integrationTest(
() -> TLSServer.listen(serverListener, NettyTransportSupport.newServerBootstrap().group(group),
- tcpServerConfig, tlsServerConfig),
+ tcpServerConfig, new FixedSslHandlerFactory(tlsServerConfig)),
() -> TLSClient.connect(clientListener, NettyTransportSupport.newBootstrap().group(group),
- tcpClientConfig, tlsClientConfig)
+ tcpClientConfig, new FixedSslHandlerFactory(tlsClientConfig))
);
}
integrationTest(
() -> TLSServer.listen(serverListener, NettyTransportSupport.newServerBootstrap().group(group),
- tcpServerConfig, channel -> serverContext.newHandler(channel.alloc())),
+ tcpServerConfig, new FixedSslHandlerFactory(serverContext)),
() -> TLSClient.connect(clientListener, NettyTransportSupport.newBootstrap().group(group),
- tcpClientConfig, channel -> clientContext.newHandler(channel.alloc()))
+ tcpClientConfig, new FixedSslHandlerFactory(clientContext))
);
}
// start call-home client
final var client = TLSClient.listen(clientListener, NettyTransportSupport.newServerBootstrap().group(group),
- tcpServerConfig, channel -> clientContext.newHandler(channel.alloc())).get(2, TimeUnit.SECONDS);
+ tcpServerConfig, new FixedSslHandlerFactory(clientContext)).get(2, TimeUnit.SECONDS);
try {
// connect with call-home servers
final var server1 = TLSServer.connect(serverListener, NettyTransportSupport.newBootstrap().group(group),
- tcpClientConfig, channel -> serverContext.newHandler(channel.alloc())).get(2, TimeUnit.SECONDS);
+ tcpClientConfig, new FixedSslHandlerFactory(serverContext)).get(2, TimeUnit.SECONDS);
final var server2 = TLSServer.connect(otherServerListener,
NettyTransportSupport.newBootstrap().group(group),
- tcpClientConfig, channel -> serverContext.newHandler(channel.alloc())).get(2, TimeUnit.SECONDS);
+ tcpClientConfig, new FixedSslHandlerFactory(serverContext)).get(2, TimeUnit.SECONDS);
try {
verify(serverListener, timeout(500))
.onTransportChannelEstablished(serverTransportChannelCaptor.capture());
.onTransportChannelEstablished(clientTransportChannelCaptor.capture());
// extract channels sorted by server address
var serverChannels = assertChannels(serverTransportChannelCaptor.getAllValues(), 2,
- Comparator.comparing((Channel channel) -> channel.localAddress().toString()));
+ Comparator.comparing((final Channel channel) -> channel.localAddress().toString()));
var clientChannels = assertChannels(clientTransportChannelCaptor.getAllValues(), 2,
- Comparator.comparing((Channel channel) -> channel.remoteAddress().toString()));
+ Comparator.comparing((final Channel channel) -> channel.remoteAddress().toString()));
for (int i = 0; i < 2; i++) {
// validate channels are connecting same sockets
assertEquals(serverChannels.get(i).remoteAddress(), clientChannels.get(i).localAddress());