View Javadoc
1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.net.ssl;
15  
16  import java.util.ArrayList;
17  import java.util.Arrays;
18  import java.util.List;
19  
20  import javax.net.ssl.SSLEngine;
21  
22  import ch.qos.logback.core.spi.ContextAwareBase;
23  import ch.qos.logback.core.util.OptionHelper;
24  import ch.qos.logback.core.util.StringCollectionUtil;
25  
26  /**
27   * A configuration of SSL parameters for an {@link SSLEngine}.
28   *
29   * @author Carl Harris
30   * @author Bruno Harbulot
31   */
32  public class SSLParametersConfiguration extends ContextAwareBase {
33  
34      private String includedProtocols;
35      private String excludedProtocols;
36      private String includedCipherSuites;
37      private String excludedCipherSuites;
38      private Boolean needClientAuth;
39      private Boolean wantClientAuth;
40      private String[] enabledProtocols;
41      private String[] enabledCipherSuites;
42      private boolean hostnameVerification = true;
43  
44      /**
45       * Configures SSL parameters on an {@link SSLConfigurable}.
46       * 
47       * @param socket the subject configurable
48       */
49      public void configure(SSLConfigurable socket) {
50          socket.setEnabledProtocols(enabledProtocols(socket.getSupportedProtocols(), socket.getDefaultProtocols()));
51          socket.setEnabledCipherSuites(
52                  enabledCipherSuites(socket.getSupportedCipherSuites(), socket.getDefaultCipherSuites()));
53          if (isNeedClientAuth() != null) {
54              socket.setNeedClientAuth(isNeedClientAuth());
55          }
56          if (isWantClientAuth() != null) {
57              socket.setWantClientAuth(isWantClientAuth());
58          }
59  
60          addInfo("hostnameVerification=" + hostnameVerification);
61          socket.setHostnameVerification(hostnameVerification);
62      }
63  
64      public boolean getHostnameVerification() {
65          return hostnameVerification;
66      }
67  
68      public void setHostnameVerification(boolean hostnameVerification) {
69          this.hostnameVerification = hostnameVerification;
70      }
71  
72      /**
73       * Gets the set of enabled protocols based on the configuration.
74       * 
75       * @param supportedProtocols protocols supported by the SSL engine
76       * @param defaultProtocols   default protocols enabled by the SSL engine
77       * @return enabled protocols
78       */
79      private String[] enabledProtocols(String[] supportedProtocols, String[] defaultProtocols) {
80          if (enabledProtocols == null) {
81              // we're assuming that the same engine is used for all configurables
82              // so once we determine the enabled set, we won't do it again
83              if (OptionHelper.isNullOrEmptyOrAllSpaces(getIncludedProtocols())
84                      && OptionHelper.isNullOrEmptyOrAllSpaces(getExcludedProtocols())) {
85                  enabledProtocols = Arrays.copyOf(defaultProtocols, defaultProtocols.length);
86              } else {
87                  enabledProtocols = includedStrings(supportedProtocols, getIncludedProtocols(), getExcludedProtocols());
88              }
89              for (String protocol : enabledProtocols) {
90                  addInfo("enabled protocol: " + protocol);
91              }
92          }
93          return enabledProtocols;
94      }
95  
96      /**
97       * Gets the set of enabled cipher suites based on the configuration.
98       * 
99       * @param supportedCipherSuites cipher suites supported by the SSL engine
100      * @param defaultCipherSuites   default cipher suites enabled by the SSL engine
101      * @return enabled cipher suites
102      */
103     private String[] enabledCipherSuites(String[] supportedCipherSuites, String[] defaultCipherSuites) {
104         if (enabledCipherSuites == null) {
105             // we're assuming that the same engine is used for all configurables
106             // so once we determine the enabled set, we won't do it again
107             if (OptionHelper.isNullOrEmptyOrAllSpaces(getIncludedCipherSuites())
108                     && OptionHelper.isNullOrEmptyOrAllSpaces(getExcludedCipherSuites())) {
109                 enabledCipherSuites = Arrays.copyOf(defaultCipherSuites, defaultCipherSuites.length);
110             } else {
111                 enabledCipherSuites = includedStrings(supportedCipherSuites, getIncludedCipherSuites(),
112                         getExcludedCipherSuites());
113             }
114             for (String cipherSuite : enabledCipherSuites) {
115                 addInfo("enabled cipher suite: " + cipherSuite);
116             }
117         }
118         return enabledCipherSuites;
119     }
120 
121     /**
122      * Applies include and exclude patterns to an array of default string values to
123      * produce an array of strings included by the patterns.
124      * 
125      * @param defaults default list of string values
126      * @param included comma-separated patterns that identity values to include
127      * @param excluded comma-separated patterns that identity string to exclude
128      * @return an array of strings containing those strings from {@code defaults}
129      *         that match at least one pattern in {@code included} that are not
130      *         matched by any pattern in {@code excluded}
131      */
132     private String[] includedStrings(String[] defaults, String included, String excluded) {
133         List<String> values = new ArrayList<String>(defaults.length);
134         values.addAll(Arrays.asList(defaults));
135         if (included != null) {
136             StringCollectionUtil.retainMatching(values, stringToArray(included));
137         }
138         if (excluded != null) {
139             StringCollectionUtil.removeMatching(values, stringToArray(excluded));
140         }
141         return values.toArray(new String[values.size()]);
142     }
143 
144     /**
145      * Splits a string containing comma-separated values into an array.
146      * 
147      * @param s the subject string
148      * @return array of values contained in {@code s}
149      */
150     private String[] stringToArray(String s) {
151         return s.split("\\s*,\\s*");
152     }
153 
154     /**
155      * Gets the JSSE secure transport protocols to include.
156      * 
157      * @return a string containing comma-separated JSSE secure transport protocol
158      *         names (e.g. {@code TLSv1})
159      */
160     public String getIncludedProtocols() {
161         return includedProtocols;
162     }
163 
164     /**
165      * Sets the JSSE secure transport protocols to include.
166      *
167      * <p>See Java Cryptography Architecture Standard Algorithm Name Documentation</p>
168      *
169      * @param protocols a string containing comma-separated JSSE secure transport
170      *                  protocol names
171      */
172     public void setIncludedProtocols(String protocols) {
173         this.includedProtocols = protocols;
174     }
175 
176     /**
177      * Gets the JSSE secure transport protocols to exclude.
178      * 
179      * @return a string containing comma-separated JSSE secure transport protocol
180      *         names (e.g. {@code TLSv1})
181      */
182     public String getExcludedProtocols() {
183         return excludedProtocols;
184     }
185 
186     /**
187      * Sets the JSSE secure transport protocols to exclude.
188      *
189      * <p>See Java Cryptography Architecture Standard Algorithm Name Documentation</p>
190      *
191      * @param protocols a string containing comma-separated JSSE secure transport
192      *                  protocol names
193      */
194     public void setExcludedProtocols(String protocols) {
195         this.excludedProtocols = protocols;
196     }
197 
198     /**
199      * Gets the JSSE cipher suite names to include.
200      * 
201      * @return a string containing comma-separated JSSE cipher suite names (e.g.
202      *         {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA})
203      */
204     public String getIncludedCipherSuites() {
205         return includedCipherSuites;
206     }
207 
208     /**
209      * Sets the JSSE cipher suite names to include.
210      *
211      * <p>See Java Cryptography Architecture Standard Algorithm Name Documentation</p>
212      *
213      * @param cipherSuites a string containing comma-separated JSSE cipher suite
214      *                     names
215      */
216     public void setIncludedCipherSuites(String cipherSuites) {
217         this.includedCipherSuites = cipherSuites;
218     }
219 
220     /**
221      * Gets the JSSE cipher suite names to exclude.
222      * 
223      * @return a string containing comma-separated JSSE cipher suite names (e.g.
224      *         {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA})
225      */
226     public String getExcludedCipherSuites() {
227         return excludedCipherSuites;
228     }
229 
230     /**
231      * Sets the JSSE cipher suite names to exclude.
232      *
233      * <p>See Java Cryptography Architecture Standard Algorithm Name Documentation</p>
234      *
235      * @param cipherSuites a string containing comma-separated JSSE cipher suite
236      *                     names
237      *
238      */
239     public void setExcludedCipherSuites(String cipherSuites) {
240         this.excludedCipherSuites = cipherSuites;
241     }
242 
243     /**
244      * Gets a flag indicating whether client authentication is required.
245      * 
246      * @return flag state
247      */
248     public Boolean isNeedClientAuth() {
249         return needClientAuth;
250     }
251 
252     /**
253      * Sets a flag indicating whether client authentication is required.
254      * 
255      * @param needClientAuth the flag state to set
256      */
257     public void setNeedClientAuth(Boolean needClientAuth) {
258         this.needClientAuth = needClientAuth;
259     }
260 
261     /**
262      * Gets a flag indicating whether client authentication is desired.
263      * 
264      * @return flag state
265      */
266     public Boolean isWantClientAuth() {
267         return wantClientAuth;
268     }
269 
270     /**
271      * Sets a flag indicating whether client authentication is desired.
272      * 
273      * @param wantClientAuth the flag state to set
274      */
275     public void setWantClientAuth(Boolean wantClientAuth) {
276         this.wantClientAuth = wantClientAuth;
277     }
278 
279 }