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