1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */

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 /**
42  * Represents the TLS configuration for a virtual host.
43  */

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         /* Default used if protocols are not configured, also used if
56          * protocols="All"
57          */

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     // OpenSSL can handle multiple certs in a single config so the reference to
71     // the context is here at the virtual host level. JSSE can't so the
72     // reference is held on the certificate.
73     private transient Long openSslContext = Long.valueOf(0);
74
75     // Configuration properties
76
77     // Internal
78     private String[] enabledCiphers;
79     private String[] enabledProtocols;
80     private ObjectName oname;
81     // Need to know if TLS 1.3 has been explicitly requested as a warning needs
82     // to generated if it is explicitly requested for a JVM that does not
83     // support it. Uses a set so it is extensible for TLS 1.4 etc.
84     private Set<String> explicitlyRequestedProtocols = new HashSet<>();
85     // Nested
86     private SSLHostConfigCertificate defaultCertificate = null;
87     private Set<SSLHostConfigCertificate> certificates = new LinkedHashSet<>(4);
88     // Common
89     private String certificateRevocationListFile;
90     private CertificateVerification certificateVerification = CertificateVerification.NONE;
91     private int certificateVerificationDepth = 10;
92     // Used to track if certificateVerificationDepth has been explicitly set
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     // Values <0 mean use the implementation default
100     private int sessionCacheSize = -1;
101     private int sessionTimeout = 86400;
102     // JSSE
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     // OpenSSL
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         // Set defaults that can't be (easily) set when defining the fields.
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     // Expose in String form for JMX
149     public String getConfigType() {
150         return configType.name();
151     }
152
153
154     /**
155      * Set property which belongs to the specified configuration type.
156      * @param name the property name
157      * @param configType the configuration type
158      * @return true if the property belongs to the current configuration,
159      *   and false otherwise
160      */

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     // ----------------------------------------------------- Internal properties
176
177     /**
178      * @see SSLUtil#getEnabledProtocols()
179      *
180      * @return The protocols enabled for this TLS virtual host
181      */

182     public String[] getEnabledProtocols() {
183         return enabledProtocols;
184     }
185
186
187     public void setEnabledProtocols(String[] enabledProtocols) {
188         this.enabledProtocols = enabledProtocols;
189     }
190
191
192     /**
193      * @see SSLUtil#getEnabledCiphers()
194      *
195      * @return The ciphers enabled for this TLS virtual host
196      */

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     // ------------------------------------------- Nested configuration elements
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         // Need to make sure that if there is more than one certificate, none of
231         // them have a type of undefined.
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             // Invalid config
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     // ----------------------------------------- Common configuration properties
278
279     // TODO: This certificate setter can be removed once it is no longer
280     // necessary to support the old configuration attributes (Tomcat 10?).
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             // If the specified value is not recognised, default to the
311             // strictest possible option.
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     /**
350      * Set the new cipher configuration. Note: Regardless of the format used to
351      * set the configuration, it is always stored in OpenSSL format.
352      *
353      * @param ciphersList The new cipher configuration in OpenSSL or JSSE format
354      */

355     public void setCiphers(String ciphersList) {
356         // Ciphers is stored in OpenSSL format. Convert the provided value if
357         // necessary.
358         if (ciphersList != null && !ciphersList.contains(":")) {
359             StringBuilder sb = new StringBuilder();
360             // Not obviously in OpenSSL format. May be a single OpenSSL or JSSE
361             // cipher name. May be a comma separated list of cipher names
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                         // Not a JSSE name. Maybe an OpenSSL name or alias
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     /**
387      * @return An OpenSSL cipher string for the current configuration.
388      */

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     /**
403      * Obtain the list of JSSE cipher names for the current configuration.
404      * Ciphers included in the configuration but not supported by JSSE will be
405      * excluded from this list.
406      *
407      * @return A list of the JSSE cipher names
408      */

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         // List of protocol names, separated by ",""+" or "-".
442         // Semantics is adding ("+") or removing ("-") from left
443         // to right, starting with an empty protocol set.
444         // Tokens are individual protocol names or "all" for a
445         // default set of supported protocols.
446         // Separator "," is only kept for compatibility and has the
447         // same semantics as "+", except that it warns about a potentially
448         // missing "+" or "-".
449
450         // Split using a positive lookahead to keep the separator in
451         // the capture so we can check which case it is.
452         for (String value: input.split("(?=[-+,])")) {
453             String trimmed = value.trim();
454             // Ignore token which only consists of prefix character
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     // ---------------------------------- JSSE specific configuration properties
523
524     // TODO: These certificate setters can be removed once it is no longer
525     // necessary to support the old configuration attributes (Tomcat 10?).
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                 // Don't use keystore type as the default if we know it is not
700                 // going to be used as a trust store type
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                         // Log a warning we had a password issue
728                         log.warn(sm.getString("jsse.invalid_truststore_password"),
729                                 cause);
730                         // Re-try
731                         result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(),
732                                 getTruststoreFile(), null);
733                     } else {
734                         // Something else went wrong - re-throw
735                         throw ioe;
736                     }
737                 }
738             }
739         }
740         return result;
741     }
742
743
744     // ------------------------------- OpenSSL specific configuration properties
745
746     // TODO: These certificate setters can be removed once it is no longer
747     // necessary to support the old configuration attributes (Tomcat 10?).
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             // Reset default JSSE trust store if not a JSSE configuration
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             // Reset default JSSE trust store if not a JSSE configuration
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     // --------------------------------------------------------- Support methods
865
866     public static String adjustRelativePath(String path) throws FileNotFoundException {
867         // Empty or null path can't point to anything useful. The assumption is
868         // that the value is deliberately empty / null so leave it that way.
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     // ----------------------------------------------------------- Inner classes
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                 // Could be a typo. Don't default to NONE since that is not
917                 // secure. Force user to fix config. Could default to REQUIRED
918                 // instead.
919                 throw new IllegalArgumentException(
920                         sm.getString("sslHostConfig.certificateVerificationInvalid", value));
921             }
922         }
923     }
924 }
925