001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util.jsse; 018 019import java.io.IOException; 020import java.net.InetAddress; 021import java.net.ServerSocket; 022import java.net.Socket; 023import java.net.UnknownHostException; 024import java.security.GeneralSecurityException; 025import java.security.KeyManagementException; 026import java.security.SecureRandom; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.regex.Matcher; 034import java.util.regex.Pattern; 035 036import javax.net.ssl.KeyManager; 037import javax.net.ssl.SNIServerName; 038import javax.net.ssl.SSLContext; 039import javax.net.ssl.SSLContextSpi; 040import javax.net.ssl.SSLEngine; 041import javax.net.ssl.SSLParameters; 042import javax.net.ssl.SSLServerSocket; 043import javax.net.ssl.SSLServerSocketFactory; 044import javax.net.ssl.SSLSessionContext; 045import javax.net.ssl.SSLSocket; 046import javax.net.ssl.SSLSocketFactory; 047import javax.net.ssl.TrustManager; 048 049import org.apache.camel.util.jsse.FilterParameters.Patterns; 050 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053 054import static org.apache.camel.util.CollectionHelper.collectionAsCommaDelimitedString; 055 056/** 057 * Represents configuration options that can be applied in the client-side 058 * or server-side context depending on what they are applied to. 059 */ 060public abstract class BaseSSLContextParameters extends JsseParameters { 061 062 protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_INCLUDE = 063 Collections.unmodifiableList(Arrays.asList(".*")); 064 065 protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_EXCLUDE = 066 Collections.unmodifiableList(Arrays.asList(".*_NULL_.*", ".*_anon_.*", ".*_EXPORT_.*", ".*_DES_.*")); 067 068 protected static final List<String> DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_INCLUDE = 069 Collections.unmodifiableList(Arrays.asList(".*")); 070 071 protected static final List<String> DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_EXCLUDE = 072 Collections.unmodifiableList(Arrays.asList("SSL.*")); 073 074 private static final Logger LOG = LoggerFactory.getLogger(BaseSSLContextParameters.class); 075 076 private static final String LS = System.lineSeparator(); 077 078 private static final String SSL_ENGINE_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLEngine"); 079 080 private static final String SSL_SOCKET_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLSocket"); 081 082 private static final String SSL_SERVER_SOCKET_CIPHER_SUITE_LOG_MSG = createCipherSuiteLogMessage("SSLServerSocket"); 083 084 private static final String SSL_ENGINE_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLEngine"); 085 086 private static final String SSL_SOCKET_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLSocket"); 087 088 private static final String SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG = createProtocolLogMessage("SSLServerSocket"); 089 090 /** 091 * The optional explicitly configured cipher suites for this configuration. 092 */ 093 private CipherSuitesParameters cipherSuites; 094 095 /** 096 * The optional cipher suite filter configuration for this configuration. 097 */ 098 private FilterParameters cipherSuitesFilter; 099 100 /** 101 * The optional explicitly configured secure socket protocol names for this configuration. 102 */ 103 private SecureSocketProtocolsParameters secureSocketProtocols; 104 105 /** 106 * The option secure socket protocol name filter configuration for this configuration. 107 */ 108 private FilterParameters secureSocketProtocolsFilter; 109 110 /** 111 * The optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s in seconds. 112 */ 113 private String sessionTimeout; 114 115 protected List<SNIServerName> getSNIHostNames() { 116 return Collections.emptyList(); 117 } 118 119 /** 120 * Returns the optional explicitly configured cipher suites for this configuration. 121 * These options are used in the configuration of {@link SSLEngine}, 122 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 123 * on the context in which they are applied. 124 * <p/> 125 * These values override any filters supplied in {@link #setCipherSuitesFilter(FilterParameters)} 126 */ 127 public CipherSuitesParameters getCipherSuites() { 128 return cipherSuites; 129 } 130 131 /** 132 * Sets the optional explicitly configured cipher suites for this configuration. 133 * These options are used in the configuration of {@link SSLEngine}, 134 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 135 * on the context in which they are applied. 136 * <p/> 137 * These values override any filters supplied in {@link #setCipherSuitesFilter(FilterParameters)} 138 * 139 * @param cipherSuites the suite configuration 140 */ 141 public void setCipherSuites(CipherSuitesParameters cipherSuites) { 142 this.cipherSuites = cipherSuites; 143 } 144 145 /** 146 * Returns the optional cipher suite filter for this configuration. 147 * These options are used in the configuration of {@link SSLEngine}, 148 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 149 * on the context in which they are applied. 150 * <p/> 151 * These values are ignored if {@link #setCipherSuites(CipherSuitesParameters)} is 152 * called with a non {@code null} argument. 153 */ 154 public FilterParameters getCipherSuitesFilter() { 155 return cipherSuitesFilter; 156 } 157 158 /** 159 * Sets the optional cipher suite filter for this JSSE configuration. 160 * These options are used in the configuration of {@link SSLEngine}, 161 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 162 * on the context in which they are applied. 163 * <p/> 164 * These values are ignored if {@link #setCipherSuites(CipherSuitesParameters)} is 165 * called with a non {@code null} argument. 166 * 167 * @param cipherSuitesFilter the filter configuration 168 */ 169 public void setCipherSuitesFilter(FilterParameters cipherSuitesFilter) { 170 this.cipherSuitesFilter = cipherSuitesFilter; 171 } 172 173 /** 174 * Returns the explicitly configured secure socket protocol names for this configuration. 175 * These options are used in the configuration of {@link SSLEngine}, 176 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 177 * on the context in which they are applied. 178 * <p/> 179 * These values override any filters supplied in {@link #setSecureSocketProtocolsFilter(FilterParameters)} 180 */ 181 public SecureSocketProtocolsParameters getSecureSocketProtocols() { 182 return secureSocketProtocols; 183 } 184 185 /** 186 * Sets the explicitly configured secure socket protocol names for this configuration. 187 * These options are used in the configuration of {@link SSLEngine}, 188 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 189 * on the context in which they are applied. 190 * <p/> 191 * These values override any filters supplied in {@link #setSecureSocketProtocolsFilter(FilterParameters)} 192 */ 193 public void setSecureSocketProtocols(SecureSocketProtocolsParameters secureSocketProtocols) { 194 this.secureSocketProtocols = secureSocketProtocols; 195 } 196 197 /** 198 * Returns the optional secure socket protocol filter for this configuration. 199 * These options are used in the configuration of {@link SSLEngine}, 200 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 201 * on the context in which they are applied. 202 * <p/> 203 * These values are ignored if {@link #setSecureSocketProtocols(SecureSocketProtocolsParameters)} is 204 * called with a non-{@code null} argument. 205 */ 206 public FilterParameters getSecureSocketProtocolsFilter() { 207 return secureSocketProtocolsFilter; 208 } 209 210 /** 211 * Sets the optional secure socket protocol filter for this JSSE configuration. 212 * These options are used in the configuration of {@link SSLEngine}, 213 * {@link SSLSocketFactory} and {@link SSLServerSocketFactory} depending 214 * on the context in which they are applied. 215 * <p/> 216 * These values are ignored if {@link #setSecureSocketProtocols(SecureSocketProtocolsParameters)} is 217 * called with a non-{@code null} argument. 218 * 219 * @param secureSocketProtocolsFilter the filter configuration 220 */ 221 public void setSecureSocketProtocolsFilter(FilterParameters secureSocketProtocolsFilter) { 222 this.secureSocketProtocolsFilter = secureSocketProtocolsFilter; 223 } 224 225 /** 226 * Returns the optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s 227 * in seconds. 228 */ 229 public String getSessionTimeout() { 230 return sessionTimeout; 231 } 232 233 /** 234 * Sets the optional {@link SSLSessionContext} timeout time for {@link javax.net.ssl.SSLSession}s 235 * in seconds. 236 * 237 * @param sessionTimeout the timeout value or {@code null} to use the default 238 */ 239 public void setSessionTimeout(String sessionTimeout) { 240 this.sessionTimeout = sessionTimeout; 241 } 242 243 /** 244 * Returns a flag indicating if default values should be applied in the event that no other property 245 * of the instance configures a particular aspect of the entity produced by the instance. 246 * This flag is used to allow instances of this class to produce a configurer that simply 247 * passes through the current configuration of a configured entity when the instance of this 248 * class would otherwise only apply some default configuration. 249 * 250 * @see SSLContextClientParameters 251 * @see SSLContextServerParameters 252 */ 253 protected boolean getAllowPassthrough() { 254 return false; 255 } 256 257 /** 258 * Configures the actual {@link SSLContext} itself with direct setter calls. This method differs from 259 * configuration options that are handled by a configurer instance in that the options are part of the 260 * context itself and are not part of some factory or instance object returned by the context. 261 * 262 * @param context the context to configure 263 * 264 * @throws GeneralSecurityException if there is an error configuring the context 265 */ 266 protected void configureSSLContext(SSLContext context) throws GeneralSecurityException { 267 LOG.trace("Configuring client and server side SSLContext parameters on SSLContext [{}]...", context); 268 269 if (this.getSessionTimeout() != null) { 270 LOG.debug("Configuring client and server side SSLContext session timeout on SSLContext [{}] to [{}]", 271 context, this.getSessionTimeout()); 272 this.configureSessionContext(context.getClientSessionContext(), this.getSessionTimeout()); 273 this.configureSessionContext(context.getServerSessionContext(), this.getSessionTimeout()); 274 } 275 276 LOG.trace("Configured client and server side SSLContext parameters on SSLContext [{}].", context); 277 } 278 279 protected FilterParameters getDefaultCipherSuitesFilter() { 280 FilterParameters filter = new FilterParameters(); 281 282 filter.getInclude().addAll(DEFAULT_CIPHER_SUITES_FILTER_INCLUDE); 283 filter.getExclude().addAll(DEFAULT_CIPHER_SUITES_FILTER_EXCLUDE); 284 285 return filter; 286 } 287 288 protected FilterParameters getDefaultSecureSocketProcotolFilter() { 289 FilterParameters filter = new FilterParameters(); 290 291 filter.getInclude().addAll(DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_INCLUDE); 292 filter.getExclude().addAll(DEFAULT_SECURE_SOCKET_PROTOCOLS_FILTER_EXCLUDE); 293 294 return filter; 295 } 296 297 /** 298 * Returns the list of configurers to apply to an {@link SSLEngine} in order 299 * to fully configure it in compliance with the provided configuration options. 300 * The configurers are to be applied in the order in which they appear in the list. 301 * 302 * @param context the context that serves as the factory for {@code SSLEngine} instances 303 * 304 * @return the needed configurers 305 */ 306 protected List<Configurer<SSLEngine>> getSSLEngineConfigurers(SSLContext context) { 307 308 final List<String> enabledCipherSuites = this.getCipherSuites() == null 309 ? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite()); 310 311 final Patterns enabledCipherSuitePatterns; 312 final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns(); 313 314 if (this.getCipherSuitesFilter() != null) { 315 enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns(); 316 } else { 317 enabledCipherSuitePatterns = null; 318 } 319 320 /// 321 322 final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null 323 ? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol()); 324 325 final Patterns enabledSecureSocketProtocolsPatterns; 326 final Patterns defaultEnabledSecureSocketProtocolsPatterns = 327 this.getDefaultSecureSocketProcotolFilter().getPatterns(); 328 329 if (this.getSecureSocketProtocolsFilter() != null) { 330 enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns(); 331 } else { 332 enabledSecureSocketProtocolsPatterns = null; 333 } 334 335 // 336 337 final boolean allowPassthrough = getAllowPassthrough(); 338 339 ////// 340 341 Configurer<SSLEngine> sslEngineConfigurer = new Configurer<SSLEngine>() { 342 343 @Override 344 public SSLEngine configure(SSLEngine engine) { 345 346 Collection<String> filteredCipherSuites = BaseSSLContextParameters.this 347 .filter(enabledCipherSuites, Arrays.asList(engine.getSSLParameters().getCipherSuites()), 348 Arrays.asList(engine.getEnabledCipherSuites()), 349 enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns, 350 !allowPassthrough); 351 352 if (LOG.isDebugEnabled()) { 353 LOG.debug(SSL_ENGINE_CIPHER_SUITE_LOG_MSG, 354 new Object[] {engine, 355 enabledCipherSuites, 356 enabledCipherSuitePatterns, 357 engine.getSSLParameters().getCipherSuites(), 358 engine.getEnabledCipherSuites(), 359 defaultEnabledCipherSuitePatterns, 360 filteredCipherSuites}); 361 } 362 363 engine.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()])); 364 365 Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this 366 .filter(enabledSecureSocketProtocols, Arrays.asList(engine.getSSLParameters().getProtocols()), 367 Arrays.asList(engine.getEnabledProtocols()), 368 enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns, 369 !allowPassthrough); 370 371 if (LOG.isDebugEnabled()) { 372 LOG.debug(SSL_ENGINE_PROTOCOL_LOG_MSG, 373 new Object[] {engine, 374 enabledSecureSocketProtocols, 375 enabledSecureSocketProtocolsPatterns, 376 engine.getSSLParameters().getProtocols(), 377 engine.getEnabledProtocols(), 378 defaultEnabledSecureSocketProtocolsPatterns, 379 filteredSecureSocketProtocols}); 380 } 381 382 engine.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()])); 383 384 return engine; 385 } 386 }; 387 388 List<Configurer<SSLEngine>> sslEngineConfigurers = new LinkedList<Configurer<SSLEngine>>(); 389 sslEngineConfigurers.add(sslEngineConfigurer); 390 391 return sslEngineConfigurers; 392 } 393 394 /** 395 * Returns the list of configurers to apply to an {@link SSLSocketFactory} in order 396 * to fully configure it in compliance with the provided configuration options. 397 * The configurers are to be applied in the order in which they appear in the list. 398 * <p/> 399 * It is preferred to use {@link #getSSLSocketFactorySSLSocketConfigurers(SSLContext)} instead 400 * of this method as {@code SSLSocketFactory} does not contain any configuration options that 401 * are non-proprietary. 402 * 403 * @param context the context that serves as the factory for {@code SSLSocketFactory} instances 404 * 405 * @return the needed configurers 406 * 407 * @see #getSSLSocketFactorySSLSocketConfigurers(SSLContext) 408 */ 409 protected List<Configurer<SSLSocketFactory>> getSSLSocketFactoryConfigurers(SSLContext context) { 410 411 final List<Configurer<SSLSocket>> sslSocketConfigurers = 412 this.getSSLSocketFactorySSLSocketConfigurers(context); 413 414 Configurer<SSLSocketFactory> sslSocketFactoryConfigurer = new Configurer<SSLSocketFactory>() { 415 416 @Override 417 public SSLSocketFactory configure(SSLSocketFactory factory) { 418 return new SSLSocketFactoryDecorator( 419 factory, 420 sslSocketConfigurers); 421 } 422 }; 423 424 425 List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers = 426 new LinkedList<Configurer<SSLSocketFactory>>(); 427 sslSocketFactoryConfigurers.add(sslSocketFactoryConfigurer); 428 429 return sslSocketFactoryConfigurers; 430 } 431 432 /** 433 * Returns the list of configurers to apply to an {@link SSLServerSocketFactory} in order 434 * to fully configure it in compliance with the provided configuration options. 435 * The configurers are to be applied in the order in which they appear in the list. 436 * <p/> 437 * It is preferred to use {@link #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)} instead 438 * of this method as {@code SSLServerSocketFactory} does not contain any configuration options that 439 * are non-proprietary. 440 * 441 * @param context the context that serves as the factory for {@code SSLServerSocketFactory} instances 442 * 443 * @return the needed configurers 444 * 445 * @see #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext) 446 */ 447 protected List<Configurer<SSLServerSocketFactory>> getSSLServerSocketFactoryConfigurers(SSLContext context) { 448 449 final List<Configurer<SSLServerSocket>> sslServerSocketConfigurers = 450 this.getSSLServerSocketFactorySSLServerSocketConfigurers(context); 451 452 Configurer<SSLServerSocketFactory> sslServerSocketFactoryConfigurer = new Configurer<SSLServerSocketFactory>() { 453 454 @Override 455 public SSLServerSocketFactory configure(SSLServerSocketFactory factory) { 456 return new SSLServerSocketFactoryDecorator( 457 factory, 458 sslServerSocketConfigurers); 459 } 460 }; 461 462 463 List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers = 464 new LinkedList<Configurer<SSLServerSocketFactory>>(); 465 sslServerSocketFactoryConfigurers.add(sslServerSocketFactoryConfigurer); 466 467 return sslServerSocketFactoryConfigurers; 468 } 469 470 /** 471 * Returns the list of configurers to apply to an {@link SSLSocket} in order 472 * to fully configure it in compliance with the provided configuration 473 * options. These configurers are intended for sockets produced by a 474 * {@link SSLSocketFactory}, see 475 * {@link #getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext)} for 476 * configurers related to sockets produced by a 477 * {@link SSLServerSocketFactory}. The configurers are to be applied in 478 * the order in which they appear in the list. 479 * 480 * @param context the context that serves as the factory for 481 * {@code SSLSocketFactory} instances 482 * 483 * @return the needed configurers 484 */ 485 protected List<Configurer<SSLSocket>> getSSLSocketFactorySSLSocketConfigurers(SSLContext context) { 486 final List<String> enabledCipherSuites = this.getCipherSuites() == null 487 ? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite()); 488 489 final Patterns enabledCipherSuitePatterns; 490 final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns(); 491 492 if (this.getCipherSuitesFilter() != null) { 493 enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns(); 494 } else { 495 enabledCipherSuitePatterns = null; 496 } 497 498 /// 499 500 final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null 501 ? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol()); 502 503 final Patterns enabledSecureSocketProtocolsPatterns; 504 final Patterns defaultEnabledSecureSocketProtocolsPatterns = 505 this.getDefaultSecureSocketProcotolFilter().getPatterns(); 506 507 if (this.getSecureSocketProtocolsFilter() != null) { 508 enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns(); 509 } else { 510 enabledSecureSocketProtocolsPatterns = null; 511 } 512 513 // 514 515 final boolean allowPassthrough = getAllowPassthrough(); 516 517 ////// 518 519 Configurer<SSLSocket> sslSocketConfigurer = new Configurer<SSLSocket>() { 520 521 @Override 522 public SSLSocket configure(SSLSocket socket) { 523 524 if (!getSNIHostNames().isEmpty()) { 525 SSLParameters sslParameters = socket.getSSLParameters(); 526 sslParameters.setServerNames(getSNIHostNames()); 527 socket.setSSLParameters(sslParameters); 528 } 529 530 Collection<String> filteredCipherSuites = BaseSSLContextParameters.this 531 .filter(enabledCipherSuites, Arrays.asList(socket.getSSLParameters().getCipherSuites()), 532 Arrays.asList(socket.getEnabledCipherSuites()), 533 enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns, 534 !allowPassthrough); 535 if (LOG.isDebugEnabled()) { 536 LOG.debug(SSL_SOCKET_CIPHER_SUITE_LOG_MSG, 537 new Object[] {socket, 538 enabledCipherSuites, 539 enabledCipherSuitePatterns, 540 socket.getSSLParameters().getCipherSuites(), 541 socket.getEnabledCipherSuites(), 542 defaultEnabledCipherSuitePatterns, 543 filteredCipherSuites}); 544 } 545 546 socket.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()])); 547 548 Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this 549 .filter(enabledSecureSocketProtocols, Arrays.asList(socket.getSSLParameters().getProtocols()), 550 Arrays.asList(socket.getEnabledProtocols()), 551 enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns, 552 !allowPassthrough); 553 554 if (LOG.isDebugEnabled()) { 555 LOG.debug(SSL_SOCKET_PROTOCOL_LOG_MSG, 556 new Object[] {socket, 557 enabledSecureSocketProtocols, 558 enabledSecureSocketProtocolsPatterns, 559 socket.getSSLParameters().getProtocols(), 560 socket.getEnabledProtocols(), 561 defaultEnabledSecureSocketProtocolsPatterns, 562 filteredSecureSocketProtocols}); 563 } 564 565 socket.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()])); 566 return socket; 567 } 568 }; 569 570 List<Configurer<SSLSocket>> sslSocketConfigurers = new LinkedList<Configurer<SSLSocket>>(); 571 sslSocketConfigurers.add(sslSocketConfigurer); 572 573 return sslSocketConfigurers; 574 } 575 576 /** 577 * Returns the list of configurers to apply to an {@link SSLServerSocket} in order 578 * to fully configure it in compliance with the provided configuration 579 * options. These configurers are intended for sockets produced by a 580 * {@link SSLServerSocketFactory}, see 581 * {@link #getSSLSocketFactorySSLSocketConfigurers(SSLContext)} for 582 * configurers related to sockets produced by a 583 * {@link SSLSocketFactory}. The configurers are to be applied in 584 * the order in which they appear in the list. 585 * 586 * @param context the context that serves as the factory for 587 * {@code SSLServerSocketFactory} instances 588 * @return the needed configurers 589 */ 590 protected List<Configurer<SSLServerSocket>> getSSLServerSocketFactorySSLServerSocketConfigurers(SSLContext context) { 591 final List<String> enabledCipherSuites = this.getCipherSuites() == null 592 ? null : this.parsePropertyValues(this.getCipherSuites().getCipherSuite()); 593 594 final Patterns enabledCipherSuitePatterns; 595 final Patterns defaultEnabledCipherSuitePatterns = this.getDefaultCipherSuitesFilter().getPatterns(); 596 597 if (this.getCipherSuitesFilter() != null) { 598 enabledCipherSuitePatterns = this.getCipherSuitesFilter().getPatterns(); 599 } else { 600 enabledCipherSuitePatterns = null; 601 } 602 603 /// 604 605 final List<String> enabledSecureSocketProtocols = this.getSecureSocketProtocols() == null 606 ? null : this.parsePropertyValues(this.getSecureSocketProtocols().getSecureSocketProtocol()); 607 608 final Patterns enabledSecureSocketProtocolsPatterns; 609 final Patterns defaultEnabledSecureSocketProtocolsPatterns = 610 this.getDefaultSecureSocketProcotolFilter().getPatterns(); 611 612 if (this.getSecureSocketProtocolsFilter() != null) { 613 enabledSecureSocketProtocolsPatterns = this.getSecureSocketProtocolsFilter().getPatterns(); 614 } else { 615 enabledSecureSocketProtocolsPatterns = null; 616 } 617 618 // 619 620 final boolean allowPassthrough = getAllowPassthrough(); 621 622 ////// 623 624 Configurer<SSLServerSocket> sslServerSocketConfigurer = new Configurer<SSLServerSocket>() { 625 626 @Override 627 public SSLServerSocket configure(SSLServerSocket socket) { 628 629 Collection<String> filteredCipherSuites = BaseSSLContextParameters.this 630 .filter(enabledCipherSuites, Arrays.asList(socket.getSupportedCipherSuites()), 631 Arrays.asList(socket.getEnabledCipherSuites()), 632 enabledCipherSuitePatterns, defaultEnabledCipherSuitePatterns, 633 !allowPassthrough); 634 635 if (LOG.isDebugEnabled()) { 636 LOG.debug(SSL_SERVER_SOCKET_CIPHER_SUITE_LOG_MSG, 637 new Object[] {socket, 638 enabledCipherSuites, 639 enabledCipherSuitePatterns, 640 socket.getSupportedCipherSuites(), 641 socket.getEnabledCipherSuites(), 642 defaultEnabledCipherSuitePatterns, 643 filteredCipherSuites}); 644 } 645 646 socket.setEnabledCipherSuites(filteredCipherSuites.toArray(new String[filteredCipherSuites.size()])); 647 648 Collection<String> filteredSecureSocketProtocols = BaseSSLContextParameters.this 649 .filter(enabledSecureSocketProtocols, Arrays.asList(socket.getSupportedProtocols()), 650 Arrays.asList(socket.getEnabledProtocols()), 651 enabledSecureSocketProtocolsPatterns, defaultEnabledSecureSocketProtocolsPatterns, 652 !allowPassthrough); 653 654 if (LOG.isDebugEnabled()) { 655 LOG.debug(SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG, 656 new Object[] {socket, 657 enabledSecureSocketProtocols, 658 enabledSecureSocketProtocolsPatterns, 659 socket.getSupportedProtocols(), 660 socket.getEnabledProtocols(), 661 defaultEnabledSecureSocketProtocolsPatterns, 662 filteredSecureSocketProtocols}); 663 } 664 665 socket.setEnabledProtocols(filteredSecureSocketProtocols.toArray(new String[filteredSecureSocketProtocols.size()])); 666 return socket; 667 } 668 }; 669 670 List<Configurer<SSLServerSocket>> sslServerSocketConfigurers = new LinkedList<Configurer<SSLServerSocket>>(); 671 sslServerSocketConfigurers.add(sslServerSocketConfigurer); 672 673 return sslServerSocketConfigurers; 674 } 675 676 /** 677 * Configures a {@link SSLSessionContext}, client or server, with the supplied session timeout. 678 * 679 * @param sessionContext the context to configure 680 * @param sessionTimeout the timeout time period 681 * @throws GeneralSecurityException if {@code sessionContext} is {@code null} 682 */ 683 protected void configureSessionContext( 684 SSLSessionContext sessionContext, String sessionTimeout) throws GeneralSecurityException { 685 686 int sessionTimeoutInt = Integer.parseInt(this.parsePropertyValue(sessionTimeout)); 687 688 if (sessionContext != null) { 689 sessionContext.setSessionTimeout(sessionTimeoutInt); 690 } else { 691 throw new GeneralSecurityException( 692 "The SSLContext does not support SSLSessionContext, " 693 + "but a session timeout is configured. Set sessionTimeout to null " 694 + "to avoid this error."); 695 } 696 } 697 698 /** 699 * Filters the values in {@code availableValues} returning only the values that 700 * are explicitly listed in {@code explicitValues} (returns them regardless 701 * of if they appear in {@code availableValues} or not) if {@code explicitValues} is not 702 * {@code null} or according to the following rules: 703 * <ol> 704 * <li>Match the include patterns in {@code patterns} and don't match the exclude patterns in {@code patterns} 705 * if patterns is not {@code null}.</li> 706 * <li>Match the include patterns in {@code defaultPatterns} and don't match the exclude patterns in {@code defaultPatterns} 707 * if patterns is {@code null} and {@code applyDefaults} is true.</li> 708 * <li>Are provided in currentValues if if patterns is {@code null} and {@code applyDefaults} is false.</li> 709 * </ol> 710 * 711 * @param explicitValues the optional explicit values to use 712 * @param availableValues the available values to filter from 713 * @param patterns the optional patterns to use when {@code explicitValues} is not used 714 * @param defaultPatterns the required patterns to use when {@code explicitValues} and {@code patterns} are not used 715 * @param applyDefaults flag indicating whether or not to apply defaults in the event that no explicit values and no 716 * patterns apply 717 * 718 * @return the filtered values 719 * 720 * @see #filter(Collection, Collection, List, List) 721 */ 722 protected Collection<String> filter( 723 Collection<String> explicitValues, Collection<String> availableValues, 724 Collection<String> currentValues, Patterns patterns, Patterns defaultPatterns, 725 boolean applyDefaults) { 726 727 final List<Pattern> enabledIncludePatterns; 728 final List<Pattern> enabledExcludePatterns; 729 730 if (explicitValues == null && patterns == null && !applyDefaults) { 731 return currentValues; 732 } 733 734 if (patterns != null) { 735 enabledIncludePatterns = patterns.getIncludes(); 736 enabledExcludePatterns = patterns.getExcludes(); 737 } else { 738 enabledIncludePatterns = defaultPatterns.getIncludes(); 739 enabledExcludePatterns = defaultPatterns.getExcludes(); 740 } 741 742 return this.filter( 743 explicitValues, 744 availableValues, 745 enabledIncludePatterns, enabledExcludePatterns); 746 } 747 748 /** 749 * Filters the values in {@code availableValues} returning only the values that 750 * are explicitly listed in {@code explicitValues} (returns them regardless 751 * of if they appear in {@code availableValues} or not) if {@code explicitValues} is not 752 * {@code null} or as match the patterns in {@code includePatterns} and do 753 * not match the patterns in {@code excludePatterns} if {@code explicitValues} is {@code null}. 754 * 755 * @param explicitValues the optional explicit values to use 756 * @param availableValues the available values to filter from if {@code explicitValues} is {@code null} 757 * @param includePatterns the patterns to use for inclusion filtering, required if {@code explicitValues} is {@code null} 758 * @param excludePatterns the patterns to use for exclusion filtering, required if {@code explicitValues} is {@code null} 759 * 760 * @return the filtered values 761 */ 762 protected Collection<String> filter(Collection<String> explicitValues, Collection<String> availableValues, 763 List<Pattern> includePatterns, List<Pattern> excludePatterns) { 764 Collection<String> returnValues; 765 766 // Explicit list has precedence over filters, even when the list is 767 // empty. 768 if (explicitValues != null) { 769 returnValues = new ArrayList<String>(explicitValues); 770 } else { 771 returnValues = new LinkedList<String>(); 772 773 for (String value : availableValues) { 774 if (this.matchesOneOf(value, includePatterns) 775 && !this.matchesOneOf(value, excludePatterns)) { 776 returnValues.add(value); 777 } 778 } 779 } 780 781 return returnValues; 782 } 783 784 /** 785 * Returns true if and only if the value is matched by one or more of the supplied patterns. 786 * 787 * @param value the value to match 788 * @param patterns the patterns to try to match against 789 */ 790 protected boolean matchesOneOf(String value, List<Pattern> patterns) { 791 boolean matches = false; 792 793 for (Pattern pattern : patterns) { 794 Matcher matcher = pattern.matcher(value); 795 if (matcher.matches()) { 796 matches = true; 797 break; 798 } 799 } 800 801 return matches; 802 } 803 804 /** 805 * Configures a {@code T} based on the related configuration options. 806 */ 807 interface Configurer<T> { 808 809 /** 810 * Configures a {@code T} based on the related configuration options. 811 * The return value from this method may be {@code object} or it 812 * may be a decorated instance there of. Consequently, any subsequent 813 * actions on {@code object} must be performed using the returned value. 814 * 815 * @param object the object to configure 816 * @return {@code object} or a decorated instance there of 817 */ 818 T configure(T object); 819 } 820 821 /** 822 * Makes a decorated {@link SSLContext} appear as a normal {@code SSLContext}. 823 */ 824 protected static final class SSLContextDecorator extends SSLContext { 825 826 public SSLContextDecorator(SSLContextSpiDecorator decorator) { 827 super(decorator, decorator.getDelegate().getProvider(), decorator.getDelegate().getProtocol()); 828 LOG.debug("SSLContextDecorator [{}] decorating SSLContext [{}].", this, decorator.getDelegate()); 829 } 830 831 @Override 832 public String toString() { 833 return String.format("SSLContext[hash=%h, provider=%s, protocol=%s, needClientAuth=%s, " 834 + "wantClientAuth=%s\n\tdefaultProtocols=%s\n\tdefaultChiperSuites=%s\n\tsupportedProtocols=%s\n\tsupportedChiperSuites=%s\n]", 835 hashCode(), getProvider(), getProtocol(), getDefaultSSLParameters().getNeedClientAuth(), getDefaultSSLParameters().getWantClientAuth(), 836 collectionAsCommaDelimitedString(getDefaultSSLParameters().getProtocols()), 837 collectionAsCommaDelimitedString(getDefaultSSLParameters().getCipherSuites()), 838 collectionAsCommaDelimitedString(getSupportedSSLParameters().getProtocols()), 839 collectionAsCommaDelimitedString(getSupportedSSLParameters().getCipherSuites())); 840 } 841 } 842 843 /** 844 * Class needed to provide decoration of an existing {@link SSLContext}. 845 * Since {@code SSLContext} is an abstract class and requires an instance of 846 * {@link SSLContextSpi}, this class effectively wraps an 847 * {@code SSLContext} as if it were an {@code SSLContextSpi}, allowing us to 848 * achieve decoration. 849 */ 850 protected static final class SSLContextSpiDecorator extends SSLContextSpi { 851 852 private final SSLContext context; 853 854 private final List<Configurer<SSLEngine>> sslEngineConfigurers; 855 856 private final List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers; 857 858 private final List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers; 859 860 public SSLContextSpiDecorator(SSLContext context, 861 List<Configurer<SSLEngine>> sslEngineConfigurers, 862 List<Configurer<SSLSocketFactory>> sslSocketFactoryConfigurers, 863 List<Configurer<SSLServerSocketFactory>> sslServerSocketFactoryConfigurers) { 864 this.context = context; 865 this.sslEngineConfigurers = sslEngineConfigurers; 866 this.sslSocketFactoryConfigurers = sslSocketFactoryConfigurers; 867 this.sslServerSocketFactoryConfigurers = sslServerSocketFactoryConfigurers; 868 } 869 870 @Override 871 protected SSLEngine engineCreateSSLEngine() { 872 SSLEngine engine = this.context.createSSLEngine(); 873 LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, context); 874 this.configureSSLEngine(engine); 875 return engine; 876 } 877 878 @Override 879 protected SSLEngine engineCreateSSLEngine(String peerHost, int peerPort) { 880 SSLEngine engine = this.context.createSSLEngine(peerHost, peerPort); 881 LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, context); 882 return this.configureSSLEngine(engine); 883 } 884 885 @Override 886 protected SSLSessionContext engineGetClientSessionContext() { 887 return this.context.getClientSessionContext(); 888 } 889 890 @Override 891 protected SSLSessionContext engineGetServerSessionContext() { 892 return this.context.getServerSessionContext(); 893 } 894 895 @Override 896 protected SSLServerSocketFactory engineGetServerSocketFactory() { 897 SSLServerSocketFactory factory = this.context.getServerSocketFactory(); 898 LOG.debug("SSLServerSocketFactoryEngine [{}] created from SSLContext [{}].", factory, context); 899 return this.configureSSLServerSocketFactory(factory); 900 } 901 902 @Override 903 protected SSLSocketFactory engineGetSocketFactory() { 904 SSLSocketFactory factory = this.context.getSocketFactory(); 905 LOG.debug("SSLSocketFactory [{}] created from SSLContext [{}].", factory, context); 906 return this.configureSSLSocketFactory(factory); 907 } 908 909 @Override 910 protected void engineInit(KeyManager[] km, 911 TrustManager[] tm, 912 SecureRandom random) throws KeyManagementException { 913 this.context.init(km, tm, random); 914 } 915 916 protected SSLContext getDelegate() { 917 return this.context; 918 } 919 920 /** 921 * Configures an {@link SSLEngine} based on the configurers in instance. 922 * The return value from this method may be {@code engine} or it may be 923 * a decorated instance there of. Consequently, any subsequent actions 924 * on {@code engine} must be performed using the returned value. 925 * 926 * @param engine the engine to configure 927 * @return {@code engine} or a decorated instance there of 928 */ 929 protected SSLEngine configureSSLEngine(SSLEngine engine) { 930 SSLEngine workingEngine = engine; 931 932 for (Configurer<SSLEngine> configurer : this.sslEngineConfigurers) { 933 workingEngine = configurer.configure(workingEngine); 934 } 935 936 return workingEngine; 937 } 938 939 /** 940 * Configures an {@link SSLSocketFactory} based on the configurers in 941 * this instance. The return value from this method may be 942 * {@code factory} or it may be a decorated instance there of. 943 * Consequently, any subsequent actions on {@code factory} must be 944 * performed using the returned value. 945 * 946 * @param factory the factory to configure 947 * @return {@code factory} or a decorated instance there of 948 */ 949 protected SSLSocketFactory configureSSLSocketFactory(SSLSocketFactory factory) { 950 SSLSocketFactory workingFactory = factory; 951 952 for (Configurer<SSLSocketFactory> configurer : this.sslSocketFactoryConfigurers) { 953 workingFactory = configurer.configure(workingFactory); 954 } 955 956 return workingFactory; 957 } 958 959 /** 960 * Configures an {@link SSLServerSocketFactory} based on the 961 * configurers in this instance. The return value from this method may be 962 * {@code factory} or it may be a decorated instance there of. 963 * Consequently, any subsequent actions on {@code factory} must be 964 * performed using the returned value. 965 * 966 * @param factory the factory to configure 967 * @return {@code factory} or a decorated instance there of 968 */ 969 protected SSLServerSocketFactory configureSSLServerSocketFactory( 970 SSLServerSocketFactory factory) { 971 SSLServerSocketFactory workingFactory = factory; 972 973 for (Configurer<SSLServerSocketFactory> configurer : this.sslServerSocketFactoryConfigurers) { 974 workingFactory = configurer.configure(workingFactory); 975 } 976 977 return workingFactory; 978 } 979 } 980 981 /** 982 * A decorator that enables the application of configuration options to be 983 * applied to created sockets even after this factory has been created and 984 * turned over to client code. 985 */ 986 protected static final class SSLServerSocketFactoryDecorator extends SSLServerSocketFactory { 987 988 private final SSLServerSocketFactory sslServerSocketFactory; 989 private final List<Configurer<SSLServerSocket>> sslServerSocketConfigurers; 990 991 public SSLServerSocketFactoryDecorator(SSLServerSocketFactory sslServerSocketFactory, 992 List<Configurer<SSLServerSocket>> sslServerSocketConfigurers) { 993 this.sslServerSocketFactory = sslServerSocketFactory; 994 this.sslServerSocketConfigurers = sslServerSocketConfigurers; 995 } 996 997 @Override 998 public String[] getDefaultCipherSuites() { 999 return this.sslServerSocketFactory.getDefaultCipherSuites(); 1000 } 1001 1002 @Override 1003 public String[] getSupportedCipherSuites() { 1004 return this.sslServerSocketFactory.getSupportedCipherSuites(); 1005 } 1006 1007 @Override 1008 public ServerSocket createServerSocket() throws IOException { 1009 return this.configureSocket(this.sslServerSocketFactory.createServerSocket()); 1010 } 1011 1012 @Override 1013 public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException { 1014 return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port, backlog, ifAddress)); 1015 } 1016 1017 @Override 1018 public ServerSocket createServerSocket(int port, int backlog) throws IOException { 1019 return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port, backlog)); 1020 } 1021 1022 @Override 1023 public ServerSocket createServerSocket(int port) throws IOException { 1024 return this.configureSocket(this.sslServerSocketFactory.createServerSocket(port)); 1025 } 1026 1027 public SSLServerSocketFactory getDelegate() { 1028 return this.sslServerSocketFactory; 1029 } 1030 1031 private ServerSocket configureSocket(ServerSocket s) { 1032 SSLServerSocket workingSocket = (SSLServerSocket) s; 1033 1034 LOG.debug("Created ServerSocket [{}] from SslServerSocketFactory [{}].", s, sslServerSocketFactory); 1035 1036 for (Configurer<SSLServerSocket> configurer : this.sslServerSocketConfigurers) { 1037 workingSocket = configurer.configure(workingSocket); 1038 } 1039 1040 return workingSocket; 1041 } 1042 } 1043 1044 /** 1045 * A decorator that enables the application of configuration options to be 1046 * applied to created sockets even after this factory has been created and 1047 * turned over to client code. 1048 */ 1049 protected static final class SSLSocketFactoryDecorator extends SSLSocketFactory { 1050 1051 private final SSLSocketFactory sslSocketFactory; 1052 private final List<Configurer<SSLSocket>> sslSocketConfigurers; 1053 1054 public SSLSocketFactoryDecorator(SSLSocketFactory sslSocketFactory, 1055 List<Configurer<SSLSocket>> sslSocketConfigurers) { 1056 this.sslSocketFactory = sslSocketFactory; 1057 this.sslSocketConfigurers = sslSocketConfigurers; 1058 } 1059 1060 @Override 1061 public String[] getDefaultCipherSuites() { 1062 return sslSocketFactory.getDefaultCipherSuites(); 1063 } 1064 1065 @Override 1066 public String[] getSupportedCipherSuites() { 1067 return sslSocketFactory.getSupportedCipherSuites(); 1068 } 1069 1070 @Override 1071 public Socket createSocket() throws IOException { 1072 return configureSocket(sslSocketFactory.createSocket()); 1073 } 1074 1075 @Override 1076 public Socket createSocket(Socket s, String host, 1077 int port, boolean autoClose) throws IOException, UnknownHostException { 1078 return configureSocket(sslSocketFactory.createSocket(s, host, port, autoClose)); 1079 } 1080 1081 @Override 1082 public Socket createSocket(String host, int port) throws IOException, UnknownHostException { 1083 return configureSocket(sslSocketFactory.createSocket(host, port)); 1084 } 1085 1086 @Override 1087 public Socket createSocket(String host, int port, 1088 InetAddress localHost, int localPort) throws IOException, UnknownHostException { 1089 return configureSocket(sslSocketFactory.createSocket(host, port, localHost, localPort)); 1090 } 1091 1092 @Override 1093 public Socket createSocket(InetAddress host, int port) throws IOException { 1094 return configureSocket(sslSocketFactory.createSocket(host, port)); 1095 } 1096 1097 @Override 1098 public Socket createSocket(InetAddress address, int port, 1099 InetAddress localAddress, int localPort) throws IOException { 1100 return configureSocket(sslSocketFactory.createSocket(address, port, localAddress, localPort)); 1101 } 1102 1103 public SSLSocketFactory getDelegate() { 1104 return this.sslSocketFactory; 1105 } 1106 1107 private Socket configureSocket(Socket s) { 1108 SSLSocket workingSocket = (SSLSocket) s; 1109 1110 LOG.debug("Created Socket [{}] from SocketFactory [{}].", s, sslSocketFactory); 1111 1112 for (Configurer<SSLSocket> configurer : this.sslSocketConfigurers) { 1113 workingSocket = configurer.configure(workingSocket); 1114 } 1115 1116 return workingSocket; 1117 } 1118 } 1119 1120 private static String createCipherSuiteLogMessage(String entityName) { 1121 return "Configuring " + entityName + " [{}] with " + LS 1122 + "\t explicitly set cipher suites [{}]," + LS 1123 + "\t cipher suite patterns [{}]," + LS 1124 + "\t available cipher suites [{}]," + LS 1125 + "\t currently enabled cipher suites [{}]," + LS 1126 + "\t and default cipher suite patterns [{}]." + LS 1127 + "\t Resulting enabled cipher suites are [{}]."; 1128 } 1129 1130 private static String createProtocolLogMessage(String entityName) { 1131 return "Configuring " + entityName + " [{}] with " + LS 1132 + "\t explicitly set protocols [{}]," + LS 1133 + "\t protocol patterns [{}]," + LS 1134 + "\t available protocols [{}]," + LS 1135 + "\t currently enabled protocols [{}]," + LS 1136 + "\t and default protocol patterns [{}]." + LS 1137 + "\t Resulting enabled protocols are [{}]."; 1138 } 1139}