1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.testsuite.transport.socket;
17
18 import io.netty.bootstrap.Bootstrap;
19 import io.netty.bootstrap.ServerBootstrap;
20 import io.netty.buffer.Unpooled;
21 import io.netty.channel.Channel;
22 import io.netty.channel.ChannelFuture;
23 import io.netty.channel.ChannelFutureListener;
24 import io.netty.channel.ChannelHandlerContext;
25 import io.netty.channel.ChannelInboundHandlerAdapter;
26 import io.netty.channel.ChannelInitializer;
27 import io.netty.channel.MultiThreadIoEventLoopGroup;
28 import io.netty.channel.nio.NioIoHandler;
29 import io.netty.channel.socket.nio.NioServerSocketChannel;
30 import io.netty.channel.socket.nio.NioSocketChannel;
31 import io.netty.handler.ssl.ClientAuth;
32 import io.netty.handler.ssl.OpenSsl;
33 import io.netty.handler.ssl.OpenSslContextOption;
34 import io.netty.handler.ssl.SslContext;
35 import io.netty.handler.ssl.SslContextBuilder;
36 import io.netty.handler.ssl.SslHandshakeCompletionEvent;
37 import io.netty.handler.ssl.SslProvider;
38 import io.netty.pkitesting.CertificateBuilder;
39 import io.netty.pkitesting.X509Bundle;
40 import io.netty.util.ReferenceCountUtil;
41 import io.netty.util.concurrent.ImmediateEventExecutor;
42 import io.netty.util.concurrent.Promise;
43 import org.junit.jupiter.api.AfterAll;
44 import org.junit.jupiter.api.BeforeAll;
45 import org.junit.jupiter.api.TestInstance;
46 import org.junit.jupiter.api.condition.EnabledIf;
47 import org.junit.jupiter.api.parallel.Execution;
48 import org.junit.jupiter.api.parallel.ExecutionMode;
49 import org.junit.jupiter.params.ParameterizedTest;
50 import org.junit.jupiter.params.provider.Arguments;
51 import org.junit.jupiter.params.provider.MethodSource;
52
53 import java.net.InetAddress;
54 import java.net.InetSocketAddress;
55 import java.nio.charset.StandardCharsets;
56 import java.util.concurrent.ExecutionException;
57 import java.util.concurrent.ThreadLocalRandom;
58 import java.util.concurrent.TimeUnit;
59 import java.util.stream.IntStream;
60 import java.util.stream.Stream;
61 import javax.net.ssl.KeyManagerFactory;
62 import javax.net.ssl.SNIHostName;
63 import javax.net.ssl.TrustManagerFactory;
64
65 @EnabledIf("supportKeyManagerAndTLS13")
66 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
67 @Execution(ExecutionMode.SAME_THREAD)
68 public class SocketSslLargeCertificateTest {
69
70 private CertificateBuilder base;
71 private X509Bundle rootCert;
72 private MultiThreadIoEventLoopGroup group;
73
74 @BeforeAll
75 public void setUp() throws Exception {
76 base = new CertificateBuilder()
77 .ecp256()
78 .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature,
79 CertificateBuilder.KeyUsage.keyCertSign);
80 rootCert = base.copy()
81 .subject("cn=root.netty.io")
82 .setIsCertificateAuthority(true)
83 .buildSelfSigned();
84 group = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
85 }
86
87 @AfterAll
88 public void tearDown() {
89 group.shutdownGracefully(100, 1000, TimeUnit.MILLISECONDS);
90 }
91
92 public static boolean supportKeyManagerAndTLS13() {
93 return OpenSsl.isAvailable() &&
94 OpenSsl.supportsKeyManagerFactory() &&
95 SslProvider.isTlsv13Supported(SslProvider.OPENSSL);
96 }
97
98 public static Stream<Arguments> certExtensionSizes() {
99 int defaultMaxHandshakeMessageLength = 16384;
100 return IntStream.rangeClosed(defaultMaxHandshakeMessageLength - 768, defaultMaxHandshakeMessageLength)
101 .mapToObj(Arguments::of);
102 }
103
104 @ParameterizedTest
105 @MethodSource("certExtensionSizes")
106 void resumptionWithLargeCertificates(int certExtensionSize) throws Exception {
107 X509Bundle serverCert = base.copy()
108 .subject("cn=localhost")
109 .addExtendedKeyUsageServerAuth()
110 .buildIssuedBy(rootCert);
111 byte[] extension = new byte[certExtensionSize];
112 ThreadLocalRandom.current().nextBytes(extension);
113 X509Bundle clientCert = base.copy()
114 .subject("cn=client")
115 .addExtendedKeyUsageClientAuth()
116 .addExtensionOctetString("1.2.840.113635.100.6.2.1", false, extension)
117 .buildIssuedBy(rootCert);
118
119 TrustManagerFactory tmf = rootCert.toTrustManagerFactory();
120 KeyManagerFactory serverKmf = serverCert.toKeyManagerFactory();
121 KeyManagerFactory clientKmf = clientCert.toKeyManagerFactory();
122
123 SslContext serverSsl = SslContextBuilder.forServer(serverKmf)
124 .sslProvider(SslProvider.OPENSSL)
125 .trustManager(tmf)
126 .protocols("TLSv1.3")
127 .clientAuth(ClientAuth.REQUIRE)
128 .option(OpenSslContextOption.MAX_CERTIFICATE_LIST_BYTES, 32768)
129 .build();
130 SslContext clientSsl = SslContextBuilder.forClient()
131 .sslProvider(SslProvider.OPENSSL)
132 .keyManager(clientKmf)
133 .trustManager(tmf)
134 .protocols("TLSv1.3")
135 .option(OpenSslContextOption.MAX_CERTIFICATE_LIST_BYTES, 32768)
136 .serverName(new SNIHostName("localhost"))
137 .endpointIdentificationAlgorithm(null)
138 .build();
139
140 final Promise<Void> completion = ImmediateEventExecutor.INSTANCE.newPromise();
141
142 ChannelFuture bindFuture = new ServerBootstrap()
143 .group(group)
144 .channel(NioServerSocketChannel.class)
145 .childHandler(new ChannelInitializer<Channel>() {
146 @Override
147 protected void initChannel(Channel ch) throws Exception {
148 ch.pipeline().addLast(serverSsl.newHandler(ch.alloc()));
149 ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
150 @Override
151 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
152 if (evt instanceof SslHandshakeCompletionEvent) {
153 SslHandshakeCompletionEvent completionEvent = (SslHandshakeCompletionEvent) evt;
154 if (completionEvent.isSuccess()) {
155 ctx.writeAndFlush(Unpooled.buffer());
156 } else {
157 completion.tryFailure(new ExecutionException(completionEvent.cause()));
158 ctx.close();
159 }
160 }
161 }
162
163 @Override
164 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
165 ctx.write(msg);
166 }
167
168 @Override
169 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
170 ctx.flush();
171 }
172 });
173 }
174 })
175 .bind(InetAddress.getLoopbackAddress(), 0);
176 Channel serverChannel = bindFuture.sync().channel();
177 InetSocketAddress serverAddress = (InetSocketAddress) serverChannel.localAddress();
178 ChannelFuture connectFuture = new Bootstrap()
179 .group(group)
180 .channel(NioSocketChannel.class)
181 .handler(new ChannelInitializer<Channel>() {
182 @Override
183 protected void initChannel(Channel ch) throws Exception {
184 ch.pipeline().addLast(clientSsl.newHandler(ch.alloc(), "localhost", serverAddress.getPort()));
185 ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
186 @Override
187 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
188 if (evt instanceof SslHandshakeCompletionEvent) {
189 SslHandshakeCompletionEvent completionEvent = (SslHandshakeCompletionEvent) evt;
190 if (completionEvent.isSuccess()) {
191 ctx.writeAndFlush(Unpooled.copiedBuffer("hello", StandardCharsets.UTF_8));
192 } else {
193 completion.tryFailure(new ExecutionException(completionEvent.cause()));
194 ctx.close();
195 }
196 }
197 }
198
199 private boolean receivedRead;
200 @Override
201 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
202 receivedRead = true;
203 ReferenceCountUtil.release(msg);
204 }
205
206 @Override
207 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
208 ctx.fireChannelReadComplete();
209 if (receivedRead) {
210 receivedRead = false;
211 ctx.writeAndFlush(Unpooled.buffer()).addListener(ChannelFutureListener.CLOSE);
212 ctx.channel().closeFuture().addListener(future -> completion.setSuccess(null));
213 }
214 }
215
216 @Override
217 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
218 completion.tryFailure(cause);
219 super.exceptionCaught(ctx, cause);
220 }
221 });
222 }
223 })
224 .connect(serverAddress);
225 Channel clientChannel = connectFuture.sync().channel();
226 completion.sync();
227 clientChannel.close().sync();
228 serverChannel.close().sync();
229 }
230 }