import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+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 static org.opendaylight.netconf.transport.tls.KeyUtils.EC_ALGORITHM;
import static org.opendaylight.netconf.transport.tls.KeyUtils.RSA_ALGORITHM;
import static org.opendaylight.netconf.transport.tls.TestUtils.buildEndEntityCertWithKeyGrouping;
-import static org.opendaylight.netconf.transport.tls.TestUtils.buildLocalOrTruststore;
+import static org.opendaylight.netconf.transport.tls.TestUtils.buildInlineOrTruststore;
import static org.opendaylight.netconf.transport.tls.TestUtils.generateX509CertData;
import static org.opendaylight.netconf.transport.tls.TestUtils.isRSA;
+import com.google.common.util.concurrent.ListenableFuture;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opendaylight.netconf.transport.api.TransportChannel;
import org.opendaylight.netconf.transport.api.TransportChannelListener;
import org.opendaylight.netconf.transport.tcp.NettyTransportSupport;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev221212.EcPrivateKeyFormat;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev221212.RsaPrivateKeyFormat;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev221212.SubjectPublicKeyInfoFormat;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev230417.EcPrivateKeyFormat;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev230417.RsaPrivateKeyFormat;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev230417.SubjectPublicKeyInfoFormat;
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;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev221212.TcpClientGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev221212.TcpServerGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev221212.TlsClientGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev221212.tls.client.grouping.ClientIdentityBuilder;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev221212.tls.client.grouping.ServerAuthenticationBuilder;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev221212.TlsServerGrouping;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev221212.tls.server.grouping.ClientAuthenticationBuilder;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev221212.tls.server.grouping.ServerIdentityBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev230417.TcpClientGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev230417.TcpServerGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev230417.TlsClientGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev230417.tls.client.grouping.ClientIdentityBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev230417.tls.client.grouping.ServerAuthenticationBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417.TlsServerGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417.tls.server.grouping.ClientAuthenticationBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417.tls.server.grouping.ServerIdentityBuilder;
import org.opendaylight.yangtools.yang.common.Uint16;
@ExtendWith(MockitoExtension.class)
// create temp socket to get available port for test
socket = new ServerSocket(0);
- final var localAddress = IetfInetUtil.INSTANCE.ipAddressFor(InetAddress.getLoopbackAddress());
+ final var localAddress = IetfInetUtil.ipAddressFor(InetAddress.getLoopbackAddress());
final var localPort = new PortNumber(Uint16.valueOf(socket.getLocalPort()));
socket.close();
final var data = generateX509CertData(algorithm);
// common config parts
- var localOrKeystore = buildEndEntityCertWithKeyGrouping(
+ var inlineOrKeystore = buildEndEntityCertWithKeyGrouping(
SubjectPublicKeyInfoFormat.VALUE, data.publicKey(),
isRSA(algorithm) ? RsaPrivateKeyFormat.VALUE : EcPrivateKeyFormat.VALUE,
- data.privateKey(), data.certBytes()).getLocalOrKeystore();
- var localOrTrustStore = buildLocalOrTruststore(Map.of("cert", data.certBytes()));
+ data.privateKey(), data.certBytes()).getInlineOrKeystore();
+ var inlineOrTrustStore = buildInlineOrTruststore(Map.of("cert", data.certBytes()));
// client config
final var clientIdentity = new ClientIdentityBuilder()
- .setAuthType(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev221212
- .tls.client.grouping.client.identity.auth.type.CertificateBuilder()
- .setCertificate(
- new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev221212
- .tls.client.grouping.client.identity.auth.type.certificate.CertificateBuilder()
- .setLocalOrKeystore(localOrKeystore)
- .build()).build()).build();
- final var serverAuth = new ServerAuthenticationBuilder().setCaCerts(
- new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev221212
- .tls.client.grouping.server.authentication.CaCertsBuilder()
- .setLocalOrTruststore(localOrTrustStore).build()).build();
+ .setAuthType(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev230417
+ .tls.client.grouping.client.identity.auth.type.CertificateBuilder()
+ .setCertificate(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev230417
+ .tls.client.grouping.client.identity.auth.type.certificate.CertificateBuilder()
+ .setInlineOrKeystore(inlineOrKeystore)
+ .build())
+ .build())
+ .build();
+ final var serverAuth = new ServerAuthenticationBuilder()
+ .setCaCerts(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.client.rev230417
+ .tls.client.grouping.server.authentication.CaCertsBuilder()
+ .setInlineOrTruststore(inlineOrTrustStore)
+ .build())
+ .build();
when(tlsClientConfig.getClientIdentity()).thenReturn(clientIdentity);
when(tlsClientConfig.getServerAuthentication()).thenReturn(serverAuth);
// server config
final var serverIdentity = new ServerIdentityBuilder()
- .setAuthType(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev221212
- .tls.server.grouping.server.identity.auth.type.CertificateBuilder()
- .setCertificate(
- new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev221212
- .tls.server.grouping.server.identity.auth.type.certificate.CertificateBuilder()
- .setLocalOrKeystore(localOrKeystore)
- .build()).build()).build();
- final var clientAuth = new ClientAuthenticationBuilder().setCaCerts(
- new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev221212
- .tls.server.grouping.client.authentication.CaCertsBuilder()
- .setLocalOrTruststore(localOrTrustStore).build()).build();
+ .setAuthType(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417
+ .tls.server.grouping.server.identity.auth.type.CertificateBuilder()
+ .setCertificate(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417
+ .tls.server.grouping.server.identity.auth.type.certificate.CertificateBuilder()
+ .setInlineOrKeystore(inlineOrKeystore)
+ .build())
+ .build())
+ .build();
+ final var clientAuth = new ClientAuthenticationBuilder()
+ .setCaCerts(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417
+ .tls.server.grouping.client.authentication.CaCertsBuilder()
+ .setInlineOrTruststore(inlineOrTrustStore)
+ .build())
+ .build();
when(tlsServerConfig.getServerIdentity()).thenReturn(serverIdentity);
when(tlsServerConfig.getClientAuthentication()).thenReturn(clientAuth);
- integrationTest();
+ integrationTest(
+ TLSServer.listen(serverListener, NettyTransportSupport.newServerBootstrap().group(group),
+ tcpServerConfig, tlsServerConfig),
+ TLSClient.connect(clientListener, NettyTransportSupport.newBootstrap().group(group),
+ tcpClientConfig, tlsClientConfig)
+ );
}
- private void integrationTest() throws Exception {
+ @Test
+ @DisplayName("External SslHandlerFactory integration")
+ void sslHandlerFactory() throws Exception {
+
+ final var serverKs = buildKeystoreWithGeneratedCert(RSA_ALGORITHM);
+ final var clientKs = buildKeystoreWithGeneratedCert(EC_ALGORITHM);
+ final var serverContext = SslContextBuilder.forServer(buildKeyManagerFactory(serverKs))
+ .clientAuth(ClientAuth.REQUIRE).trustManager(buildTrustManagerFactory(clientKs)).build();
+ final var clientContext = SslContextBuilder.forClient().keyManager(buildKeyManagerFactory(clientKs))
+ .trustManager(buildTrustManagerFactory(serverKs)).build();
+
+ integrationTest(
+ TLSServer.listen(serverListener, NettyTransportSupport.newServerBootstrap().group(group),
+ tcpServerConfig, channel -> serverContext.newHandler(channel.alloc())),
+ TLSClient.connect(clientListener, NettyTransportSupport.newBootstrap().group(group),
+ tcpClientConfig, channel -> clientContext.newHandler(channel.alloc()))
+ );
+ }
+
+ private static KeyStore buildKeystoreWithGeneratedCert(final String algorithm) throws Exception {
+ final var data = generateX509CertData(algorithm);
+ final var ret = newKeyStore();
+ ret.setCertificateEntry("certificate", data.certificate());
+ ret.setKeyEntry("key", data.keyPair().getPrivate(), new char[0], new Certificate[]{data.certificate()});
+ return ret;
+ }
+
+ private void integrationTest(final ListenableFuture<TLSServer> serverFuture,
+ final ListenableFuture<TLSClient> clientFuture) throws Exception {
// start server
- final var server = TLSServer.listen(serverListener, NettyTransportSupport.newServerBootstrap().group(group),
- tcpServerConfig, tlsServerConfig).get(2, TimeUnit.SECONDS);
+ final var server = serverFuture.get(2, TimeUnit.SECONDS);
try {
// connect with client
- final var client = TLSClient.connect(clientListener, NettyTransportSupport.newBootstrap().group(group),
- tcpClientConfig, tlsClientConfig).get(2, TimeUnit.SECONDS);
+ final var client = clientFuture.get(2, TimeUnit.SECONDS);
try {
verify(serverListener, timeout(500))
.onTransportChannelEstablished(serverTransportChannelCaptor.capture());
}
}
- private static Channel assertChannel(List<TransportChannel> transportChannels) {
+ private static Channel assertChannel(final List<TransportChannel> transportChannels) {
assertNotNull(transportChannels);
assertEquals(1, transportChannels.size());
final var channel = assertInstanceOf(TLSTransportChannel.class, transportChannels.get(0)).channel();