1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, 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 v1.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;
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          if (hostnameVerification != null) {
60              addInfo("hostnameVerification=" + hostnameVerification);
61              socket.setHostnameVerification(hostnameVerification);
62          }
63      }
64  
65      public boolean getHostnameVerification() {
66          if (hostnameVerification == null)
67              return false;
68          return hostnameVerification;
69      }
70  
71      public void setHostnameVerification(boolean hostnameVerification) {
72          this.hostnameVerification = hostnameVerification;
73      }
74  
75      /**
76       * Gets the set of enabled protocols based on the configuration.
77       * 
78       * @param supportedProtocols protocols supported by the SSL engine
79       * @param defaultProtocols   default protocols enabled by the SSL engine
80       * @return enabled protocols
81       */
82      private String[] enabledProtocols(String[] supportedProtocols, String[] defaultProtocols) {
83          if (enabledProtocols == null) {
84              // we're assuming that the same engine is used for all configurables
85              // so once we determine the enabled set, we won't do it again
86              if (OptionHelper.isNullOrEmptyOrAllSpaces(getIncludedProtocols())
87                      && OptionHelper.isNullOrEmptyOrAllSpaces(getExcludedProtocols())) {
88                  enabledProtocols = Arrays.copyOf(defaultProtocols, defaultProtocols.length);
89              } else {
90                  enabledProtocols = includedStrings(supportedProtocols, getIncludedProtocols(), getExcludedProtocols());
91              }
92              for (String protocol : enabledProtocols) {
93                  addInfo("enabled protocol: " + protocol);
94              }
95          }
96          return enabledProtocols;
97      }
98  
99      /**
100      * Gets the set of enabled cipher suites based on the configuration.
101      * 
102      * @param supportedCipherSuites cipher suites supported by the SSL engine
103      * @param defaultCipherSuites   default cipher suites enabled by the SSL engine
104      * @return enabled cipher suites
105      */
106     private String[] enabledCipherSuites(String[] supportedCipherSuites, String[] defaultCipherSuites) {
107         if (enabledCipherSuites == null) {
108             // we're assuming that the same engine is used for all configurables
109             // so once we determine the enabled set, we won't do it again
110             if (OptionHelper.isNullOrEmptyOrAllSpaces(getIncludedCipherSuites())
111                     && OptionHelper.isNullOrEmptyOrAllSpaces(getExcludedCipherSuites())) {
112                 enabledCipherSuites = Arrays.copyOf(defaultCipherSuites, defaultCipherSuites.length);
113             } else {
114                 enabledCipherSuites = includedStrings(supportedCipherSuites, getIncludedCipherSuites(),
115                         getExcludedCipherSuites());
116             }
117             for (String cipherSuite : enabledCipherSuites) {
118                 addInfo("enabled cipher suite: " + cipherSuite);
119             }
120         }
121         return enabledCipherSuites;
122     }
123 
124     /**
125      * Applies include and exclude patterns to an array of default string values to
126      * produce an array of strings included by the patterns.
127      * 
128      * @param defaults default list of string values
129      * @param included comma-separated patterns that identity values to include
130      * @param excluded comma-separated patterns that identity string to exclude
131      * @return an array of strings containing those strings from {@code defaults}
132      *         that match at least one pattern in {@code included} that are not
133      *         matched by any pattern in {@code excluded}
134      */
135     private String[] includedStrings(String[] defaults, String included, String excluded) {
136         List<String> values = new ArrayList<String>(defaults.length);
137         values.addAll(Arrays.asList(defaults));
138         if (included != null) {
139             StringCollectionUtil.retainMatching(values, stringToArray(included));
140         }
141         if (excluded != null) {
142             StringCollectionUtil.removeMatching(values, stringToArray(excluded));
143         }
144         return values.toArray(new String[values.size()]);
145     }
146 
147     /**
148      * Splits a string containing comma-separated values into an array.
149      * 
150      * @param s the subject string
151      * @return array of values contained in {@code s}
152      */
153     private String[] stringToArray(String s) {
154         return s.split("\\s*,\\s*");
155     }
156 
157     /**
158      * Gets the JSSE secure transport protocols to include.
159      * 
160      * @return a string containing comma-separated JSSE secure transport protocol
161      *         names (e.g. {@code TLSv1})
162      */
163     public String getIncludedProtocols() {
164         return includedProtocols;
165     }
166 
167     /**
168      * Sets the JSSE secure transport protocols to include.
169      * 
170      * @param protocols a string containing comma-separated JSSE secure transport
171      *                  protocol names
172      * @see Java Cryptography Architecture Standard Algorithm Name Documentation
173      */
174     public void setIncludedProtocols(String protocols) {
175         this.includedProtocols = protocols;
176     }
177 
178     /**
179      * Gets the JSSE secure transport protocols to exclude.
180      * 
181      * @return a string containing comma-separated JSSE secure transport protocol
182      *         names (e.g. {@code TLSv1})
183      */
184     public String getExcludedProtocols() {
185         return excludedProtocols;
186     }
187 
188     /**
189      * Sets the JSSE secure transport protocols to exclude.
190      * 
191      * @param protocols a string containing comma-separated JSSE secure transport
192      *                  protocol names
193      * @see Java Cryptography Architecture Standard Algorithm Name Documentation
194      */
195     public void setExcludedProtocols(String protocols) {
196         this.excludedProtocols = protocols;
197     }
198 
199     /**
200      * Gets the JSSE cipher suite names to include.
201      * 
202      * @return a string containing comma-separated JSSE cipher suite names (e.g.
203      *         {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA})
204      */
205     public String getIncludedCipherSuites() {
206         return includedCipherSuites;
207     }
208 
209     /**
210      * Sets the JSSE cipher suite names to include.
211      * 
212      * @param cipherSuites a string containing comma-separated JSSE cipher suite
213      *                     names
214      * @see Java Cryptography Architecture Standard Algorithm Name Documentation
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      * @param cipherSuites a string containing comma-separated JSSE cipher suite
234      *                     names
235      * @see Java Cryptography Architecture Standard Algorithm Name Documentation
236      */
237     public void setExcludedCipherSuites(String cipherSuites) {
238         this.excludedCipherSuites = cipherSuites;
239     }
240 
241     /**
242      * Gets a flag indicating whether client authentication is required.
243      * 
244      * @return flag state
245      */
246     public Boolean isNeedClientAuth() {
247         return needClientAuth;
248     }
249 
250     /**
251      * Sets a flag indicating whether client authentication is required.
252      * 
253      * @param needClientAuth the flag state to set
254      */
255     public void setNeedClientAuth(Boolean needClientAuth) {
256         this.needClientAuth = needClientAuth;
257     }
258 
259     /**
260      * Gets a flag indicating whether client authentication is desired.
261      * 
262      * @return flag state
263      */
264     public Boolean isWantClientAuth() {
265         return wantClientAuth;
266     }
267 
268     /**
269      * Sets a flag indicating whether client authentication is desired.
270      * 
271      * @param wantClientAuth the flag state to set
272      */
273     public void setWantClientAuth(Boolean wantClientAuth) {
274         this.wantClientAuth = wantClientAuth;
275     }
276 
277 }