1
17 package org.apache.tomcat.util.net;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.security.KeyStore;
24 import java.security.UnrecoverableKeyException;
25 import java.util.HashSet;
26 import java.util.LinkedHashSet;
27 import java.util.List;
28 import java.util.Set;
29
30 import javax.management.ObjectName;
31 import javax.net.ssl.KeyManagerFactory;
32 import javax.net.ssl.TrustManagerFactory;
33
34 import org.apache.juli.logging.Log;
35 import org.apache.juli.logging.LogFactory;
36 import org.apache.tomcat.util.net.openssl.OpenSSLConf;
37 import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
38 import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser;
39 import org.apache.tomcat.util.res.StringManager;
40
41
44 public class SSLHostConfig implements Serializable {
45
46 private static final long serialVersionUID = 1L;
47
48 private static final Log log = LogFactory.getLog(SSLHostConfig.class);
49 private static final StringManager sm = StringManager.getManager(SSLHostConfig.class);
50
51 protected static final String DEFAULT_SSL_HOST_NAME = "_default_";
52 protected static final Set<String> SSL_PROTO_ALL_SET = new HashSet<>();
53
54 static {
55
58 SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_SSLv2Hello);
59 SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1);
60 SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1_1);
61 SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1_2);
62 SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1_3);
63 }
64
65 private Type configType = null;
66
67 private String hostName = DEFAULT_SSL_HOST_NAME;
68
69 private transient Long openSslConfContext = Long.valueOf(0);
70
71
72
73 private transient Long openSslContext = Long.valueOf(0);
74
75
76
77
78 private String[] enabledCiphers;
79 private String[] enabledProtocols;
80 private ObjectName oname;
81
82
83
84 private Set<String> explicitlyRequestedProtocols = new HashSet<>();
85
86 private SSLHostConfigCertificate defaultCertificate = null;
87 private Set<SSLHostConfigCertificate> certificates = new LinkedHashSet<>(4);
88
89 private String certificateRevocationListFile;
90 private CertificateVerification certificateVerification = CertificateVerification.NONE;
91 private int certificateVerificationDepth = 10;
92
93 private boolean certificateVerificationDepthConfigured = false;
94 private String ciphers = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA";
95 private LinkedHashSet<Cipher> cipherList = null;
96 private List<String> jsseCipherNames = null;
97 private boolean honorCipherOrder = false;
98 private Set<String> protocols = new HashSet<>();
99
100 private int sessionCacheSize = -1;
101 private int sessionTimeout = 86400;
102
103 private String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
104 private boolean revocationEnabled = false;
105 private String sslProtocol = Constants.SSL_PROTO_TLS;
106 private String trustManagerClassName;
107 private String truststoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
108 private String truststoreFile = System.getProperty("javax.net.ssl.trustStore");
109 private String truststorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
110 private String truststoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
111 private String truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
112 private transient KeyStore truststore = null;
113
114 private String certificateRevocationListPath;
115 private String caCertificateFile;
116 private String caCertificatePath;
117 private boolean disableCompression = true;
118 private boolean disableSessionTickets = false;
119 private boolean insecureRenegotiation = false;
120 private OpenSSLConf openSslConf = null;
121
122 public SSLHostConfig() {
123
124 setProtocols(Constants.SSL_PROTO_ALL);
125 }
126
127
128 public Long getOpenSslConfContext() {
129 return openSslConfContext;
130 }
131
132
133 public void setOpenSslConfContext(Long openSslConfContext) {
134 this.openSslConfContext = openSslConfContext;
135 }
136
137
138 public Long getOpenSslContext() {
139 return openSslContext;
140 }
141
142
143 public void setOpenSslContext(Long openSslContext) {
144 this.openSslContext = openSslContext;
145 }
146
147
148
149 public String getConfigType() {
150 return configType.name();
151 }
152
153
154
161 boolean setProperty(String name, Type configType) {
162 if (this.configType == null) {
163 this.configType = configType;
164 } else {
165 if (configType != this.configType) {
166 log.warn(sm.getString("sslHostConfig.mismatch",
167 name, getHostName(), configType, this.configType));
168 return false;
169 }
170 }
171 return true;
172 }
173
174
175
176
177
182 public String[] getEnabledProtocols() {
183 return enabledProtocols;
184 }
185
186
187 public void setEnabledProtocols(String[] enabledProtocols) {
188 this.enabledProtocols = enabledProtocols;
189 }
190
191
192
197 public String[] getEnabledCiphers() {
198 return enabledCiphers;
199 }
200
201
202 public void setEnabledCiphers(String[] enabledCiphers) {
203 this.enabledCiphers = enabledCiphers;
204 }
205
206
207 public ObjectName getObjectName() {
208 return oname;
209 }
210
211
212 public void setObjectName(ObjectName oname) {
213 this.oname = oname;
214 }
215
216
217
218
219 private void registerDefaultCertificate() {
220 if (defaultCertificate == null) {
221 SSLHostConfigCertificate defaultCertificate = new SSLHostConfigCertificate(
222 this, SSLHostConfigCertificate.Type.UNDEFINED);
223 addCertificate(defaultCertificate);
224 this.defaultCertificate = defaultCertificate;
225 }
226 }
227
228
229 public void addCertificate(SSLHostConfigCertificate certificate) {
230
231
232 if (certificates.size() == 0) {
233 certificates.add(certificate);
234 return;
235 }
236
237 if (certificates.size() == 1 &&
238 certificates.iterator().next().getType() == SSLHostConfigCertificate.Type.UNDEFINED ||
239 certificate.getType() == SSLHostConfigCertificate.Type.UNDEFINED) {
240
241 throw new IllegalArgumentException(sm.getString("sslHostConfig.certificate.notype"));
242 }
243
244 certificates.add(certificate);
245 }
246
247
248 public OpenSSLConf getOpenSslConf() {
249 return openSslConf;
250 }
251
252
253 public void setOpenSslConf(OpenSSLConf conf) {
254 if (conf == null) {
255 throw new IllegalArgumentException(sm.getString("sslHostConfig.opensslconf.null"));
256 } else if (openSslConf != null) {
257 throw new IllegalArgumentException(sm.getString("sslHostConfig.opensslconf.alreadySet"));
258 }
259 setProperty("<OpenSSLConf>", Type.OPENSSL);
260 openSslConf = conf;
261 }
262
263
264 public Set<SSLHostConfigCertificate> getCertificates() {
265 return getCertificates(false);
266 }
267
268
269 public Set<SSLHostConfigCertificate> getCertificates(boolean createDefaultIfEmpty) {
270 if (certificates.size() == 0 && createDefaultIfEmpty) {
271 registerDefaultCertificate();
272 }
273 return certificates;
274 }
275
276
277
278
279
280
281
282 public String getCertificateKeyPassword() {
283 if (defaultCertificate == null) {
284 return null;
285 } else {
286 return defaultCertificate.getCertificateKeyPassword();
287 }
288 }
289 public void setCertificateKeyPassword(String certificateKeyPassword) {
290 registerDefaultCertificate();
291 defaultCertificate.setCertificateKeyPassword(certificateKeyPassword);
292 }
293
294
295 public void setCertificateRevocationListFile(String certificateRevocationListFile) {
296 this.certificateRevocationListFile = certificateRevocationListFile;
297 }
298
299
300 public String getCertificateRevocationListFile() {
301 return certificateRevocationListFile;
302 }
303
304
305 public void setCertificateVerification(String certificateVerification) {
306 try {
307 this.certificateVerification =
308 CertificateVerification.fromString(certificateVerification);
309 } catch (IllegalArgumentException iae) {
310
311
312 this.certificateVerification = CertificateVerification.REQUIRED;
313 throw iae;
314 }
315 }
316
317
318 public CertificateVerification getCertificateVerification() {
319 return certificateVerification;
320 }
321
322
323 public void setCertificateVerificationAsString(String certificateVerification) {
324 setCertificateVerification(certificateVerification);
325 }
326
327
328 public String getCertificateVerificationAsString() {
329 return certificateVerification.toString();
330 }
331
332
333 public void setCertificateVerificationDepth(int certificateVerificationDepth) {
334 this.certificateVerificationDepth = certificateVerificationDepth;
335 certificateVerificationDepthConfigured = true;
336 }
337
338
339 public int getCertificateVerificationDepth() {
340 return certificateVerificationDepth;
341 }
342
343
344 public boolean isCertificateVerificationDepthConfigured() {
345 return certificateVerificationDepthConfigured;
346 }
347
348
349
355 public void setCiphers(String ciphersList) {
356
357
358 if (ciphersList != null && !ciphersList.contains(":")) {
359 StringBuilder sb = new StringBuilder();
360
361
362 String ciphers[] = ciphersList.split(",");
363 for (String cipher : ciphers) {
364 String trimmed = cipher.trim();
365 if (trimmed.length() > 0) {
366 String openSSLName = OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed);
367 if (openSSLName == null) {
368
369 openSSLName = trimmed;
370 }
371 if (sb.length() > 0) {
372 sb.append(':');
373 }
374 sb.append(openSSLName);
375 }
376 }
377 this.ciphers = sb.toString();
378 } else {
379 this.ciphers = ciphersList;
380 }
381 this.cipherList = null;
382 this.jsseCipherNames = null;
383 }
384
385
386
389 public String getCiphers() {
390 return ciphers;
391 }
392
393
394 public LinkedHashSet<Cipher> getCipherList() {
395 if (cipherList == null) {
396 cipherList = OpenSSLCipherConfigurationParser.parse(getCiphers());
397 }
398 return cipherList;
399 }
400
401
402
409 public List<String> getJsseCipherNames() {
410 if (jsseCipherNames == null) {
411 jsseCipherNames = OpenSSLCipherConfigurationParser.convertForJSSE(getCipherList());
412 }
413 return jsseCipherNames;
414 }
415
416
417 public void setHonorCipherOrder(boolean honorCipherOrder) {
418 this.honorCipherOrder = honorCipherOrder;
419 }
420
421
422 public boolean getHonorCipherOrder() {
423 return honorCipherOrder;
424 }
425
426
427 public void setHostName(String hostName) {
428 this.hostName = hostName;
429 }
430
431
432 public String getHostName() {
433 return hostName;
434 }
435
436
437 public void setProtocols(String input) {
438 protocols.clear();
439 explicitlyRequestedProtocols.clear();
440
441
442
443
444
445
446
447
448
449
450
451
452 for (String value: input.split("(?=[-+,])")) {
453 String trimmed = value.trim();
454
455 if (trimmed.length() > 1) {
456 if (trimmed.charAt(0) == '+') {
457 trimmed = trimmed.substring(1).trim();
458 if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
459 protocols.addAll(SSL_PROTO_ALL_SET);
460 } else {
461 protocols.add(trimmed);
462 explicitlyRequestedProtocols.add(trimmed);
463 }
464 } else if (trimmed.charAt(0) == '-') {
465 trimmed = trimmed.substring(1).trim();
466 if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
467 protocols.removeAll(SSL_PROTO_ALL_SET);
468 } else {
469 protocols.remove(trimmed);
470 explicitlyRequestedProtocols.remove(trimmed);
471 }
472 } else {
473 if (trimmed.charAt(0) == ',') {
474 trimmed = trimmed.substring(1).trim();
475 }
476 if (!protocols.isEmpty()) {
477 log.warn(sm.getString("sslHostConfig.prefix_missing",
478 trimmed, getHostName()));
479 }
480 if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
481 protocols.addAll(SSL_PROTO_ALL_SET);
482 } else {
483 protocols.add(trimmed);
484 explicitlyRequestedProtocols.add(trimmed);
485 }
486 }
487 }
488 }
489 }
490
491
492 public Set<String> getProtocols() {
493 return protocols;
494 }
495
496
497 boolean isExplicitlyRequestedProtocol(String protocol) {
498 return explicitlyRequestedProtocols.contains(protocol);
499 }
500
501
502 public void setSessionCacheSize(int sessionCacheSize) {
503 this.sessionCacheSize = sessionCacheSize;
504 }
505
506
507 public int getSessionCacheSize() {
508 return sessionCacheSize;
509 }
510
511
512 public void setSessionTimeout(int sessionTimeout) {
513 this.sessionTimeout = sessionTimeout;
514 }
515
516
517 public int getSessionTimeout() {
518 return sessionTimeout;
519 }
520
521
522
523
524
525
526
527 public String getCertificateKeyAlias() {
528 if (defaultCertificate == null) {
529 return null;
530 } else {
531 return defaultCertificate.getCertificateKeyAlias();
532 }
533 }
534 public void setCertificateKeyAlias(String certificateKeyAlias) {
535 registerDefaultCertificate();
536 defaultCertificate.setCertificateKeyAlias(certificateKeyAlias);
537 }
538
539
540 public String getCertificateKeystoreFile() {
541 if (defaultCertificate == null) {
542 return null;
543 } else {
544 return defaultCertificate.getCertificateKeystoreFile();
545 }
546 }
547 public void setCertificateKeystoreFile(String certificateKeystoreFile) {
548 registerDefaultCertificate();
549 defaultCertificate.setCertificateKeystoreFile(certificateKeystoreFile);
550 }
551
552
553 public String getCertificateKeystorePassword() {
554 if (defaultCertificate == null) {
555 return null;
556 } else {
557 return defaultCertificate.getCertificateKeystorePassword();
558 }
559 }
560 public void setCertificateKeystorePassword(String certificateKeystorePassword) {
561 registerDefaultCertificate();
562 defaultCertificate.setCertificateKeystorePassword(certificateKeystorePassword);
563 }
564
565
566 public String getCertificateKeystoreProvider() {
567 if (defaultCertificate == null) {
568 return null;
569 } else {
570 return defaultCertificate.getCertificateKeystoreProvider();
571 }
572 }
573 public void setCertificateKeystoreProvider(String certificateKeystoreProvider) {
574 registerDefaultCertificate();
575 defaultCertificate.setCertificateKeystoreProvider(certificateKeystoreProvider);
576 }
577
578
579 public String getCertificateKeystoreType() {
580 if (defaultCertificate == null) {
581 return null;
582 } else {
583 return defaultCertificate.getCertificateKeystoreType();
584 }
585 }
586 public void setCertificateKeystoreType(String certificateKeystoreType) {
587 registerDefaultCertificate();
588 defaultCertificate.setCertificateKeystoreType(certificateKeystoreType);
589 }
590
591
592 public void setKeyManagerAlgorithm(String keyManagerAlgorithm) {
593 setProperty("keyManagerAlgorithm", Type.JSSE);
594 this.keyManagerAlgorithm = keyManagerAlgorithm;
595 }
596
597
598 public String getKeyManagerAlgorithm() {
599 return keyManagerAlgorithm;
600 }
601
602
603 public void setRevocationEnabled(boolean revocationEnabled) {
604 setProperty("revocationEnabled", Type.JSSE);
605 this.revocationEnabled = revocationEnabled;
606 }
607
608
609 public boolean getRevocationEnabled() {
610 return revocationEnabled;
611 }
612
613
614 public void setSslProtocol(String sslProtocol) {
615 setProperty("sslProtocol", Type.JSSE);
616 this.sslProtocol = sslProtocol;
617 }
618
619
620 public String getSslProtocol() {
621 return sslProtocol;
622 }
623
624
625 public void setTrustManagerClassName(String trustManagerClassName) {
626 setProperty("trustManagerClassName", Type.JSSE);
627 this.trustManagerClassName = trustManagerClassName;
628 }
629
630
631 public String getTrustManagerClassName() {
632 return trustManagerClassName;
633 }
634
635
636 public void setTruststoreAlgorithm(String truststoreAlgorithm) {
637 setProperty("truststoreAlgorithm", Type.JSSE);
638 this.truststoreAlgorithm = truststoreAlgorithm;
639 }
640
641
642 public String getTruststoreAlgorithm() {
643 return truststoreAlgorithm;
644 }
645
646
647 public void setTruststoreFile(String truststoreFile) {
648 setProperty("truststoreFile", Type.JSSE);
649 this.truststoreFile = truststoreFile;
650 }
651
652
653 public String getTruststoreFile() {
654 return truststoreFile;
655 }
656
657
658 public void setTruststorePassword(String truststorePassword) {
659 setProperty("truststorePassword", Type.JSSE);
660 this.truststorePassword = truststorePassword;
661 }
662
663
664 public String getTruststorePassword() {
665 return truststorePassword;
666 }
667
668
669 public void setTruststoreProvider(String truststoreProvider) {
670 setProperty("truststoreProvider", Type.JSSE);
671 this.truststoreProvider = truststoreProvider;
672 }
673
674
675 public String getTruststoreProvider() {
676 if (truststoreProvider == null) {
677 Set<SSLHostConfigCertificate> certificates = getCertificates();
678 if (certificates.size() == 1) {
679 return certificates.iterator().next().getCertificateKeystoreProvider();
680 }
681 return SSLHostConfigCertificate.DEFAULT_KEYSTORE_PROVIDER;
682 } else {
683 return truststoreProvider;
684 }
685 }
686
687
688 public void setTruststoreType(String truststoreType) {
689 setProperty("truststoreType", Type.JSSE);
690 this.truststoreType = truststoreType;
691 }
692
693
694 public String getTruststoreType() {
695 if (truststoreType == null) {
696 Set<SSLHostConfigCertificate> certificates = getCertificates();
697 if (certificates.size() == 1) {
698 String keystoreType = certificates.iterator().next().getCertificateKeystoreType();
699
700
701 if (!"PKCS12".equalsIgnoreCase(keystoreType)) {
702 return keystoreType;
703 }
704 }
705 return SSLHostConfigCertificate.DEFAULT_KEYSTORE_TYPE;
706 } else {
707 return truststoreType;
708 }
709 }
710
711
712 public void setTrustStore(KeyStore truststore) {
713 this.truststore = truststore;
714 }
715
716
717 public KeyStore getTruststore() throws IOException {
718 KeyStore result = truststore;
719 if (result == null) {
720 if (truststoreFile != null){
721 try {
722 result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(),
723 getTruststoreFile(), getTruststorePassword());
724 } catch (IOException ioe) {
725 Throwable cause = ioe.getCause();
726 if (cause instanceof UnrecoverableKeyException) {
727
728 log.warn(sm.getString("jsse.invalid_truststore_password"),
729 cause);
730
731 result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(),
732 getTruststoreFile(), null);
733 } else {
734
735 throw ioe;
736 }
737 }
738 }
739 }
740 return result;
741 }
742
743
744
745
746
747
748
749 public String getCertificateChainFile() {
750 if (defaultCertificate == null) {
751 return null;
752 } else {
753 return defaultCertificate.getCertificateChainFile();
754 }
755 }
756 public void setCertificateChainFile(String certificateChainFile) {
757 registerDefaultCertificate();
758 defaultCertificate.setCertificateChainFile(certificateChainFile);
759 }
760
761
762 public String getCertificateFile() {
763 if (defaultCertificate == null) {
764 return null;
765 } else {
766 return defaultCertificate.getCertificateFile();
767 }
768 }
769 public void setCertificateFile(String certificateFile) {
770 registerDefaultCertificate();
771 defaultCertificate.setCertificateFile(certificateFile);
772 }
773
774
775 public String getCertificateKeyFile() {
776 if (defaultCertificate == null) {
777 return null;
778 } else {
779 return defaultCertificate.getCertificateKeyFile();
780 }
781 }
782 public void setCertificateKeyFile(String certificateKeyFile) {
783 registerDefaultCertificate();
784 defaultCertificate.setCertificateKeyFile(certificateKeyFile);
785 }
786
787
788 public void setCertificateRevocationListPath(String certificateRevocationListPath) {
789 setProperty("certificateRevocationListPath", Type.OPENSSL);
790 this.certificateRevocationListPath = certificateRevocationListPath;
791 }
792
793
794 public String getCertificateRevocationListPath() {
795 return certificateRevocationListPath;
796 }
797
798
799 public void setCaCertificateFile(String caCertificateFile) {
800 if (setProperty("caCertificateFile", Type.OPENSSL)) {
801
802 if (truststoreFile != null) {
803 truststoreFile = null;
804 }
805 }
806 this.caCertificateFile = caCertificateFile;
807 }
808
809
810 public String getCaCertificateFile() {
811 return caCertificateFile;
812 }
813
814
815 public void setCaCertificatePath(String caCertificatePath) {
816 if (setProperty("caCertificatePath", Type.OPENSSL)) {
817
818 if (truststoreFile != null) {
819 truststoreFile = null;
820 }
821 }
822 this.caCertificatePath = caCertificatePath;
823 }
824
825
826 public String getCaCertificatePath() {
827 return caCertificatePath;
828 }
829
830
831 public void setDisableCompression(boolean disableCompression) {
832 setProperty("disableCompression", Type.OPENSSL);
833 this.disableCompression = disableCompression;
834 }
835
836
837 public boolean getDisableCompression() {
838 return disableCompression;
839 }
840
841
842 public void setDisableSessionTickets(boolean disableSessionTickets) {
843 setProperty("disableSessionTickets", Type.OPENSSL);
844 this.disableSessionTickets = disableSessionTickets;
845 }
846
847
848 public boolean getDisableSessionTickets() {
849 return disableSessionTickets;
850 }
851
852
853 public void setInsecureRenegotiation(boolean insecureRenegotiation) {
854 setProperty("insecureRenegotiation", Type.OPENSSL);
855 this.insecureRenegotiation = insecureRenegotiation;
856 }
857
858
859 public boolean getInsecureRenegotiation() {
860 return insecureRenegotiation;
861 }
862
863
864
865
866 public static String adjustRelativePath(String path) throws FileNotFoundException {
867
868
869 if (path == null || path.length() == 0) {
870 return path;
871 }
872 String newPath = path;
873 File f = new File(newPath);
874 if ( !f.isAbsolute()) {
875 newPath = System.getProperty(Constants.CATALINA_BASE_PROP) + File.separator + newPath;
876 f = new File(newPath);
877 }
878 if (!f.exists()) {
879 throw new FileNotFoundException(sm.getString("sslHostConfig.fileNotFound", newPath));
880 }
881 return newPath;
882 }
883
884
885
886
887 public enum Type {
888 JSSE,
889 OPENSSL
890 }
891
892
893 public enum CertificateVerification {
894 NONE,
895 OPTIONAL_NO_CA,
896 OPTIONAL,
897 REQUIRED;
898
899 public static CertificateVerification fromString(String value) {
900 if ("true".equalsIgnoreCase(value) ||
901 "yes".equalsIgnoreCase(value) ||
902 "require".equalsIgnoreCase(value) ||
903 "required".equalsIgnoreCase(value)) {
904 return REQUIRED;
905 } else if ("optional".equalsIgnoreCase(value) ||
906 "want".equalsIgnoreCase(value)) {
907 return OPTIONAL;
908 } else if ("optionalNoCA".equalsIgnoreCase(value) ||
909 "optional_no_ca".equalsIgnoreCase(value)) {
910 return OPTIONAL_NO_CA;
911 } else if ("false".equalsIgnoreCase(value) ||
912 "no".equalsIgnoreCase(value) ||
913 "none".equalsIgnoreCase(value)) {
914 return NONE;
915 } else {
916
917
918
919 throw new IllegalArgumentException(
920 sm.getString("sslHostConfig.certificateVerificationInvalid", value));
921 }
922 }
923 }
924 }
925