1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.ssl;
17
18 import io.netty5.handler.ssl.util.KeyManagerFactoryWrapper;
19 import io.netty5.handler.ssl.util.TrustManagerFactoryWrapper;
20 import io.netty5.util.internal.UnstableApi;
21
22 import javax.net.ssl.KeyManager;
23 import javax.net.ssl.KeyManagerFactory;
24 import javax.net.ssl.SSLEngine;
25 import javax.net.ssl.SSLException;
26 import javax.net.ssl.TrustManager;
27 import javax.net.ssl.TrustManagerFactory;
28 import java.io.File;
29 import java.io.InputStream;
30 import java.security.KeyStore;
31 import java.security.PrivateKey;
32 import java.security.Provider;
33 import java.security.cert.X509Certificate;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38
39 import static io.netty5.util.internal.EmptyArrays.EMPTY_STRINGS;
40 import static io.netty5.util.internal.EmptyArrays.EMPTY_X509_CERTIFICATES;
41 import static io.netty5.util.internal.ObjectUtil.checkNonEmpty;
42 import static io.netty5.util.internal.ObjectUtil.checkNotNullWithIAE;
43 import static java.util.Objects.requireNonNull;
44
45
46
47
48 public final class SslContextBuilder {
49 @SuppressWarnings("rawtypes")
50 private static final Map.Entry[] EMPTY_ENTRIES = new Map.Entry[0];
51
52
53
54
55 public static SslContextBuilder forClient() {
56 return new SslContextBuilder(false);
57 }
58
59
60
61
62
63
64
65
66 public static SslContextBuilder forServer(File keyCertChainFile, File keyFile) {
67 return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile);
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public static SslContextBuilder forServer(InputStream keyCertChainInputStream, InputStream keyInputStream) {
83 return new SslContextBuilder(true).keyManager(keyCertChainInputStream, keyInputStream);
84 }
85
86
87
88
89
90
91
92
93 public static SslContextBuilder forServer(PrivateKey key, X509Certificate... keyCertChain) {
94 return new SslContextBuilder(true).keyManager(key, keyCertChain);
95 }
96
97
98
99
100
101
102
103
104 public static SslContextBuilder forServer(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
105 return forServer(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
106 }
107
108
109
110
111
112
113
114
115
116
117 public static SslContextBuilder forServer(
118 File keyCertChainFile, File keyFile, String keyPassword) {
119 return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile, keyPassword);
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 public static SslContextBuilder forServer(
136 InputStream keyCertChainInputStream, InputStream keyInputStream, String keyPassword) {
137 return new SslContextBuilder(true).keyManager(keyCertChainInputStream, keyInputStream, keyPassword);
138 }
139
140
141
142
143
144
145
146
147
148
149 public static SslContextBuilder forServer(
150 PrivateKey key, String keyPassword, X509Certificate... keyCertChain) {
151 return new SslContextBuilder(true).keyManager(key, keyPassword, keyCertChain);
152 }
153
154
155
156
157
158
159
160
161
162
163 public static SslContextBuilder forServer(
164 PrivateKey key, String keyPassword, Iterable<? extends X509Certificate> keyCertChain) {
165 return forServer(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
166 }
167
168
169
170
171
172
173
174
175
176
177 public static SslContextBuilder forServer(KeyManagerFactory keyManagerFactory) {
178 return new SslContextBuilder(true).keyManager(keyManagerFactory);
179 }
180
181
182
183
184
185
186 public static SslContextBuilder forServer(KeyManager keyManager) {
187 return new SslContextBuilder(true).keyManager(keyManager);
188 }
189
190 private final boolean forServer;
191 private SslProvider provider;
192 private Provider sslContextProvider;
193 private X509Certificate[] trustCertCollection;
194 private TrustManagerFactory trustManagerFactory;
195 private X509Certificate[] keyCertChain;
196 private PrivateKey key;
197 private String keyPassword;
198 private KeyManagerFactory keyManagerFactory;
199 private Iterable<String> ciphers;
200 private CipherSuiteFilter cipherFilter = IdentityCipherSuiteFilter.INSTANCE;
201 private ApplicationProtocolConfig apn;
202 private long sessionCacheSize;
203 private long sessionTimeout;
204 private ClientAuth clientAuth = ClientAuth.NONE;
205 private String[] protocols;
206 private boolean startTls;
207 private boolean enableOcsp;
208 private String keyStoreType = KeyStore.getDefaultType();
209 private final Map<SslContextOption<?>, Object> options = new HashMap<>();
210
211 private SslContextBuilder(boolean forServer) {
212 this.forServer = forServer;
213 }
214
215
216
217
218 public <T> SslContextBuilder option(SslContextOption<T> option, T value) {
219 if (value == null) {
220 options.remove(option);
221 } else {
222 options.put(option, value);
223 }
224 return this;
225 }
226
227
228
229
230 public SslContextBuilder sslProvider(SslProvider provider) {
231 this.provider = provider;
232 return this;
233 }
234
235
236
237
238 public SslContextBuilder keyStoreType(String keyStoreType) {
239 this.keyStoreType = keyStoreType;
240 return this;
241 }
242
243
244
245
246
247 public SslContextBuilder sslContextProvider(Provider sslContextProvider) {
248 this.sslContextProvider = sslContextProvider;
249 return this;
250 }
251
252
253
254
255
256 public SslContextBuilder trustManager(File trustCertCollectionFile) {
257 try {
258 return trustManager(SslContext.toX509Certificates(trustCertCollectionFile));
259 } catch (Exception e) {
260 throw new IllegalArgumentException("File does not contain valid certificates: "
261 + trustCertCollectionFile, e);
262 }
263 }
264
265
266
267
268
269
270
271 public SslContextBuilder trustManager(InputStream trustCertCollectionInputStream) {
272 try {
273 return trustManager(SslContext.toX509Certificates(trustCertCollectionInputStream));
274 } catch (Exception e) {
275 throw new IllegalArgumentException("Input stream does not contain valid certificates.", e);
276 }
277 }
278
279
280
281
282 public SslContextBuilder trustManager(X509Certificate... trustCertCollection) {
283 this.trustCertCollection = trustCertCollection != null ? trustCertCollection.clone() : null;
284 trustManagerFactory = null;
285 return this;
286 }
287
288
289
290
291 public SslContextBuilder trustManager(Iterable<? extends X509Certificate> trustCertCollection) {
292 return trustManager(toArray(trustCertCollection, EMPTY_X509_CERTIFICATES));
293 }
294
295
296
297
298 public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) {
299 trustCertCollection = null;
300 this.trustManagerFactory = trustManagerFactory;
301 return this;
302 }
303
304
305
306
307
308
309
310
311 public SslContextBuilder trustManager(TrustManager trustManager) {
312 trustManagerFactory = new TrustManagerFactoryWrapper(trustManager);
313 trustCertCollection = null;
314 return this;
315 }
316
317
318
319
320
321
322
323
324 public SslContextBuilder keyManager(File keyCertChainFile, File keyFile) {
325 return keyManager(keyCertChainFile, keyFile, null);
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339 public SslContextBuilder keyManager(InputStream keyCertChainInputStream, InputStream keyInputStream) {
340 return keyManager(keyCertChainInputStream, keyInputStream, null);
341 }
342
343
344
345
346
347
348
349
350 public SslContextBuilder keyManager(PrivateKey key, X509Certificate... keyCertChain) {
351 return keyManager(key, null, keyCertChain);
352 }
353
354
355
356
357
358
359
360
361 public SslContextBuilder keyManager(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
362 return keyManager(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
363 }
364
365
366
367
368
369
370
371
372
373
374 public SslContextBuilder keyManager(File keyCertChainFile, File keyFile, String keyPassword) {
375 X509Certificate[] keyCertChain;
376 PrivateKey key;
377 try {
378 keyCertChain = SslContext.toX509Certificates(keyCertChainFile);
379 } catch (Exception e) {
380 throw new IllegalArgumentException("File does not contain valid certificates: " + keyCertChainFile, e);
381 }
382 try {
383 key = SslContext.toPrivateKey(keyFile, keyPassword);
384 } catch (Exception e) {
385 throw new IllegalArgumentException("File does not contain valid private key: " + keyFile, e);
386 }
387 return keyManager(key, keyPassword, keyCertChain);
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403 public SslContextBuilder keyManager(InputStream keyCertChainInputStream, InputStream keyInputStream,
404 String keyPassword) {
405 X509Certificate[] keyCertChain;
406 PrivateKey key;
407 try {
408 keyCertChain = SslContext.toX509Certificates(keyCertChainInputStream);
409 } catch (Exception e) {
410 throw new IllegalArgumentException("Input stream not contain valid certificates.", e);
411 }
412 try {
413 key = SslContext.toPrivateKey(keyInputStream, keyPassword);
414 } catch (Exception e) {
415 throw new IllegalArgumentException("Input stream does not contain valid private key.", e);
416 }
417 return keyManager(key, keyPassword, keyCertChain);
418 }
419
420
421
422
423
424
425
426
427
428
429 public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Certificate... keyCertChain) {
430 if (forServer) {
431 checkNonEmpty(keyCertChain, "keyCertChain");
432 requireNonNull(key, "key required for servers");
433 }
434 if (keyCertChain == null || keyCertChain.length == 0) {
435 this.keyCertChain = null;
436 } else {
437 for (X509Certificate cert: keyCertChain) {
438 checkNotNullWithIAE(cert, "cert");
439 }
440 this.keyCertChain = keyCertChain.clone();
441 }
442 this.key = key;
443 this.keyPassword = keyPassword;
444 keyManagerFactory = null;
445 return this;
446 }
447
448
449
450
451
452
453
454
455
456
457 public SslContextBuilder keyManager(PrivateKey key, String keyPassword,
458 Iterable<? extends X509Certificate> keyCertChain) {
459 return keyManager(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473 public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) {
474 if (forServer) {
475 requireNonNull(keyManagerFactory, "keyManagerFactory required for servers");
476 }
477 keyCertChain = null;
478 key = null;
479 keyPassword = null;
480 this.keyManagerFactory = keyManagerFactory;
481 return this;
482 }
483
484
485
486
487
488
489
490
491 public SslContextBuilder keyManager(KeyManager keyManager) {
492 if (forServer) {
493 requireNonNull(keyManager, "keyManager required for servers");
494 }
495 if (keyManager != null) {
496 keyManagerFactory = new KeyManagerFactoryWrapper(keyManager);
497 } else {
498 keyManagerFactory = null;
499 }
500 keyCertChain = null;
501 key = null;
502 keyPassword = null;
503 return this;
504 }
505
506
507
508
509
510 public SslContextBuilder ciphers(Iterable<String> ciphers) {
511 return ciphers(ciphers, IdentityCipherSuiteFilter.INSTANCE);
512 }
513
514
515
516
517
518
519 public SslContextBuilder ciphers(Iterable<String> ciphers, CipherSuiteFilter cipherFilter) {
520 requireNonNull(cipherFilter, "cipherFilter");
521 this.ciphers = ciphers;
522 this.cipherFilter = cipherFilter;
523 return this;
524 }
525
526
527
528
529 public SslContextBuilder applicationProtocolConfig(ApplicationProtocolConfig apn) {
530 this.apn = apn;
531 return this;
532 }
533
534
535
536
537
538 public SslContextBuilder sessionCacheSize(long sessionCacheSize) {
539 this.sessionCacheSize = sessionCacheSize;
540 return this;
541 }
542
543
544
545
546
547 public SslContextBuilder sessionTimeout(long sessionTimeout) {
548 this.sessionTimeout = sessionTimeout;
549 return this;
550 }
551
552
553
554
555 public SslContextBuilder clientAuth(ClientAuth clientAuth) {
556 this.clientAuth = requireNonNull(clientAuth, "clientAuth");
557 return this;
558 }
559
560
561
562
563
564
565 public SslContextBuilder protocols(String... protocols) {
566 this.protocols = protocols == null ? null : protocols.clone();
567 return this;
568 }
569
570
571
572
573
574
575 public SslContextBuilder protocols(Iterable<String> protocols) {
576 return protocols(toArray(protocols, EMPTY_STRINGS));
577 }
578
579
580
581
582 public SslContextBuilder startTls(boolean startTls) {
583 this.startTls = startTls;
584 return this;
585 }
586
587
588
589
590
591
592
593 @UnstableApi
594 public SslContextBuilder enableOcsp(boolean enableOcsp) {
595 this.enableOcsp = enableOcsp;
596 return this;
597 }
598
599
600
601
602
603
604 public SslContext build() throws SSLException {
605 if (forServer) {
606 return SslContext.newServerContextInternal(provider, sslContextProvider, trustCertCollection,
607 trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
608 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
609 enableOcsp, keyStoreType, toArray(options.entrySet(), EMPTY_ENTRIES));
610 } else {
611 return SslContext.newClientContextInternal(provider, sslContextProvider, trustCertCollection,
612 trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
613 ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, enableOcsp, keyStoreType,
614 toArray(options.entrySet(), EMPTY_ENTRIES));
615 }
616 }
617
618 private static <T> T[] toArray(Iterable<? extends T> iterable, T[] prototype) {
619 if (iterable == null) {
620 return null;
621 }
622 final List<T> list = new ArrayList<>();
623 for (T element : iterable) {
624 list.add(element);
625 }
626 return list.toArray(prototype);
627 }
628 }