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