001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.ssl; 029 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.IOException; 033import java.io.InputStream; 034import java.net.Socket; 035import java.net.URL; 036import java.security.KeyManagementException; 037import java.security.KeyStore; 038import java.security.KeyStoreException; 039import java.security.NoSuchAlgorithmException; 040import java.security.Principal; 041import java.security.PrivateKey; 042import java.security.Provider; 043import java.security.SecureRandom; 044import java.security.Security; 045import java.security.UnrecoverableKeyException; 046import java.security.cert.CertificateException; 047import java.security.cert.X509Certificate; 048import java.util.Collection; 049import java.util.HashMap; 050import java.util.LinkedHashSet; 051import java.util.Map; 052import java.util.Set; 053 054import javax.net.ssl.KeyManager; 055import javax.net.ssl.KeyManagerFactory; 056import javax.net.ssl.SSLContext; 057import javax.net.ssl.SSLEngine; 058import javax.net.ssl.TrustManager; 059import javax.net.ssl.TrustManagerFactory; 060import javax.net.ssl.X509ExtendedKeyManager; 061import javax.net.ssl.X509TrustManager; 062 063import org.apache.http.util.Args; 064 065/** 066 * Builder for {@link javax.net.ssl.SSLContext} instances. 067 * <p> 068 * Please note: the default Oracle JSSE implementation of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} 069 * accepts multiple key and trust managers, however only only first matching type is ever used. 070 * See for example: 071 * <a href="http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html#init%28javax.net.ssl.KeyManager[],%20javax.net.ssl.TrustManager[],%20java.security.SecureRandom%29"> 072 * SSLContext.html#init 073 * </a> 074 * <p> 075 * TODO Specify which Oracle JSSE versions the above has been verified. 076 * </p> 077 * @since 4.4 078 */ 079public class SSLContextBuilder { 080 081 static final String TLS = "TLS"; 082 083 private String protocol; 084 private final Set<KeyManager> keyManagers; 085 private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); 086 private String keyStoreType = KeyStore.getDefaultType(); 087 private final Set<TrustManager> trustManagers; 088 private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 089 private SecureRandom secureRandom; 090 private Provider provider; 091 092 public static SSLContextBuilder create() { 093 return new SSLContextBuilder(); 094 } 095 096 public SSLContextBuilder() { 097 super(); 098 this.keyManagers = new LinkedHashSet<KeyManager>(); 099 this.trustManagers = new LinkedHashSet<TrustManager>(); 100 } 101 102 /** 103 * Sets the SSLContext protocol algorithm name. 104 * 105 * @param protocol 106 * the SSLContext protocol algorithm name of the requested protocol. See 107 * the SSLContext section in the <a href= 108 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java 109 * Cryptography Architecture Standard Algorithm Name 110 * Documentation</a> for more information. 111 * @return this builder 112 * @see <a href= 113 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java 114 * Cryptography Architecture Standard Algorithm Name Documentation</a> 115 * @deprecated Use {@link #setProtocol(String)}. 116 */ 117 @Deprecated 118 public SSLContextBuilder useProtocol(final String protocol) { 119 this.protocol = protocol; 120 return this; 121 } 122 123 /** 124 * Sets the SSLContext protocol algorithm name. 125 * 126 * @param protocol 127 * the SSLContext protocol algorithm name of the requested protocol. See 128 * the SSLContext section in the <a href= 129 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java 130 * Cryptography Architecture Standard Algorithm Name 131 * Documentation</a> for more information. 132 * @return this builder 133 * @see <a href= 134 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">Java 135 * Cryptography Architecture Standard Algorithm Name Documentation</a> 136 * @since 4.4.7 137 */ 138 public SSLContextBuilder setProtocol(final String protocol) { 139 this.protocol = protocol; 140 return this; 141 } 142 143 public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) { 144 this.secureRandom = secureRandom; 145 return this; 146 } 147 148 public SSLContextBuilder setProvider(final Provider provider) { 149 this.provider = provider; 150 return this; 151 } 152 153 public SSLContextBuilder setProvider(final String name) { 154 this.provider = Security.getProvider(name); 155 return this; 156 } 157 158 /** 159 * Sets the key store type. 160 * 161 * @param keyStoreType 162 * the SSLkey store type. See 163 * the KeyStore section in the <a href= 164 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java 165 * Cryptography Architecture Standard Algorithm Name 166 * Documentation</a> for more information. 167 * @return this builder 168 * @see <a href= 169 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">Java 170 * Cryptography Architecture Standard Algorithm Name Documentation</a> 171 * @since 4.4.7 172 */ 173 public SSLContextBuilder setKeyStoreType(final String keyStoreType) { 174 this.keyStoreType = keyStoreType; 175 return this; 176 } 177 178 /** 179 * Sets the key manager factory algorithm name. 180 * 181 * @param keyManagerFactoryAlgorithm 182 * the key manager factory algorithm name of the requested protocol. See 183 * the KeyManagerFactory section in the <a href= 184 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java 185 * Cryptography Architecture Standard Algorithm Name 186 * Documentation</a> for more information. 187 * @return this builder 188 * @see <a href= 189 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyManagerFactory">Java 190 * Cryptography Architecture Standard Algorithm Name Documentation</a> 191 * @since 4.4.7 192 */ 193 public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) { 194 this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm; 195 return this; 196 } 197 198 /** 199 * Sets the trust manager factory algorithm name. 200 * 201 * @param trustManagerFactoryAlgorithm 202 * the trust manager algorithm name of the requested protocol. See 203 * the TrustManagerFactory section in the <a href= 204 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java 205 * Cryptography Architecture Standard Algorithm Name 206 * Documentation</a> for more information. 207 * @return this builder 208 * @see <a href= 209 * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory">Java 210 * Cryptography Architecture Standard Algorithm Name Documentation</a> 211 * @since 4.4.7 212 */ 213 public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) { 214 this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm; 215 return this; 216 } 217 218 public SSLContextBuilder loadTrustMaterial( 219 final KeyStore truststore, 220 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { 221 final TrustManagerFactory tmfactory = TrustManagerFactory 222 .getInstance(trustManagerFactoryAlgorithm == null ? TrustManagerFactory.getDefaultAlgorithm() 223 : trustManagerFactoryAlgorithm); 224 tmfactory.init(truststore); 225 final TrustManager[] tms = tmfactory.getTrustManagers(); 226 if (tms != null) { 227 if (trustStrategy != null) { 228 for (int i = 0; i < tms.length; i++) { 229 final TrustManager tm = tms[i]; 230 if (tm instanceof X509TrustManager) { 231 tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy); 232 } 233 } 234 } 235 for (final TrustManager tm : tms) { 236 this.trustManagers.add(tm); 237 } 238 } 239 return this; 240 } 241 242 public SSLContextBuilder loadTrustMaterial( 243 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { 244 return loadTrustMaterial(null, trustStrategy); 245 } 246 247 public SSLContextBuilder loadTrustMaterial( 248 final File file, 249 final char[] storePassword, 250 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { 251 Args.notNull(file, "Truststore file"); 252 final KeyStore trustStore = KeyStore.getInstance(keyStoreType); 253 final FileInputStream instream = new FileInputStream(file); 254 try { 255 trustStore.load(instream, storePassword); 256 } finally { 257 instream.close(); 258 } 259 return loadTrustMaterial(trustStore, trustStrategy); 260 } 261 262 public SSLContextBuilder loadTrustMaterial( 263 final File file, 264 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { 265 return loadTrustMaterial(file, storePassword, null); 266 } 267 268 public SSLContextBuilder loadTrustMaterial( 269 final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { 270 return loadTrustMaterial(file, null); 271 } 272 273 public SSLContextBuilder loadTrustMaterial( 274 final URL url, 275 final char[] storePassword, 276 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { 277 Args.notNull(url, "Truststore URL"); 278 final KeyStore trustStore = KeyStore.getInstance(keyStoreType); 279 final InputStream instream = url.openStream(); 280 try { 281 trustStore.load(instream, storePassword); 282 } finally { 283 instream.close(); 284 } 285 return loadTrustMaterial(trustStore, trustStrategy); 286 } 287 288 public SSLContextBuilder loadTrustMaterial( 289 final URL url, 290 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { 291 return loadTrustMaterial(url, storePassword, null); 292 } 293 294 public SSLContextBuilder loadKeyMaterial( 295 final KeyStore keystore, 296 final char[] keyPassword, 297 final PrivateKeyStrategy aliasStrategy) 298 throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { 299 final KeyManagerFactory kmfactory = KeyManagerFactory 300 .getInstance(keyManagerFactoryAlgorithm == null ? KeyManagerFactory.getDefaultAlgorithm() 301 : keyManagerFactoryAlgorithm); 302 kmfactory.init(keystore, keyPassword); 303 final KeyManager[] kms = kmfactory.getKeyManagers(); 304 if (kms != null) { 305 if (aliasStrategy != null) { 306 for (int i = 0; i < kms.length; i++) { 307 final KeyManager km = kms[i]; 308 if (km instanceof X509ExtendedKeyManager) { 309 kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy); 310 } 311 } 312 } 313 for (final KeyManager km : kms) { 314 keyManagers.add(km); 315 } 316 } 317 return this; 318 } 319 320 public SSLContextBuilder loadKeyMaterial( 321 final KeyStore keystore, 322 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { 323 return loadKeyMaterial(keystore, keyPassword, null); 324 } 325 326 public SSLContextBuilder loadKeyMaterial( 327 final File file, 328 final char[] storePassword, 329 final char[] keyPassword, 330 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { 331 Args.notNull(file, "Keystore file"); 332 final KeyStore identityStore = KeyStore.getInstance(keyStoreType); 333 final FileInputStream instream = new FileInputStream(file); 334 try { 335 identityStore.load(instream, storePassword); 336 } finally { 337 instream.close(); 338 } 339 return loadKeyMaterial(identityStore, keyPassword, aliasStrategy); 340 } 341 342 public SSLContextBuilder loadKeyMaterial( 343 final File file, 344 final char[] storePassword, 345 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { 346 return loadKeyMaterial(file, storePassword, keyPassword, null); 347 } 348 349 public SSLContextBuilder loadKeyMaterial( 350 final URL url, 351 final char[] storePassword, 352 final char[] keyPassword, 353 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { 354 Args.notNull(url, "Keystore URL"); 355 final KeyStore identityStore = KeyStore.getInstance(keyStoreType); 356 final InputStream instream = url.openStream(); 357 try { 358 identityStore.load(instream, storePassword); 359 } finally { 360 instream.close(); 361 } 362 return loadKeyMaterial(identityStore, keyPassword, aliasStrategy); 363 } 364 365 public SSLContextBuilder loadKeyMaterial( 366 final URL url, 367 final char[] storePassword, 368 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { 369 return loadKeyMaterial(url, storePassword, keyPassword, null); 370 } 371 372 protected void initSSLContext( 373 final SSLContext sslContext, 374 final Collection<KeyManager> keyManagers, 375 final Collection<TrustManager> trustManagers, 376 final SecureRandom secureRandom) throws KeyManagementException { 377 sslContext.init( 378 !keyManagers.isEmpty() ? keyManagers.toArray(new KeyManager[keyManagers.size()]) : null, 379 !trustManagers.isEmpty() ? trustManagers.toArray(new TrustManager[trustManagers.size()]) : null, 380 secureRandom); 381 } 382 383 public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { 384 final SSLContext sslContext; 385 final String protocolStr = this.protocol != null ? this.protocol : TLS; 386 if (this.provider != null) { 387 sslContext = SSLContext.getInstance(protocolStr, this.provider); 388 } else { 389 sslContext = SSLContext.getInstance(protocolStr); 390 } 391 initSSLContext(sslContext, keyManagers, trustManagers, secureRandom); 392 return sslContext; 393 } 394 395 static class TrustManagerDelegate implements X509TrustManager { 396 397 private final X509TrustManager trustManager; 398 private final TrustStrategy trustStrategy; 399 400 TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) { 401 super(); 402 this.trustManager = trustManager; 403 this.trustStrategy = trustStrategy; 404 } 405 406 @Override 407 public void checkClientTrusted( 408 final X509Certificate[] chain, final String authType) throws CertificateException { 409 this.trustManager.checkClientTrusted(chain, authType); 410 } 411 412 @Override 413 public void checkServerTrusted( 414 final X509Certificate[] chain, final String authType) throws CertificateException { 415 if (!this.trustStrategy.isTrusted(chain, authType)) { 416 this.trustManager.checkServerTrusted(chain, authType); 417 } 418 } 419 420 @Override 421 public X509Certificate[] getAcceptedIssuers() { 422 return this.trustManager.getAcceptedIssuers(); 423 } 424 425 } 426 427 static class KeyManagerDelegate extends X509ExtendedKeyManager { 428 429 private final X509ExtendedKeyManager keyManager; 430 private final PrivateKeyStrategy aliasStrategy; 431 432 KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) { 433 super(); 434 this.keyManager = keyManager; 435 this.aliasStrategy = aliasStrategy; 436 } 437 438 @Override 439 public String[] getClientAliases( 440 final String keyType, final Principal[] issuers) { 441 return this.keyManager.getClientAliases(keyType, issuers); 442 } 443 444 public Map<String, PrivateKeyDetails> getClientAliasMap( 445 final String[] keyTypes, final Principal[] issuers) { 446 final Map<String, PrivateKeyDetails> validAliases = new HashMap<String, PrivateKeyDetails>(); 447 for (final String keyType: keyTypes) { 448 final String[] aliases = this.keyManager.getClientAliases(keyType, issuers); 449 if (aliases != null) { 450 for (final String alias: aliases) { 451 validAliases.put(alias, 452 new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); 453 } 454 } 455 } 456 return validAliases; 457 } 458 459 public Map<String, PrivateKeyDetails> getServerAliasMap( 460 final String keyType, final Principal[] issuers) { 461 final Map<String, PrivateKeyDetails> validAliases = new HashMap<String, PrivateKeyDetails>(); 462 final String[] aliases = this.keyManager.getServerAliases(keyType, issuers); 463 if (aliases != null) { 464 for (final String alias: aliases) { 465 validAliases.put(alias, 466 new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); 467 } 468 } 469 return validAliases; 470 } 471 472 @Override 473 public String chooseClientAlias( 474 final String[] keyTypes, final Principal[] issuers, final Socket socket) { 475 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers); 476 return this.aliasStrategy.chooseAlias(validAliases, socket); 477 } 478 479 @Override 480 public String[] getServerAliases( 481 final String keyType, final Principal[] issuers) { 482 return this.keyManager.getServerAliases(keyType, issuers); 483 } 484 485 @Override 486 public String chooseServerAlias( 487 final String keyType, final Principal[] issuers, final Socket socket) { 488 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers); 489 return this.aliasStrategy.chooseAlias(validAliases, socket); 490 } 491 492 @Override 493 public X509Certificate[] getCertificateChain(final String alias) { 494 return this.keyManager.getCertificateChain(alias); 495 } 496 497 @Override 498 public PrivateKey getPrivateKey(final String alias) { 499 return this.keyManager.getPrivateKey(alias); 500 } 501 502 @Override 503 public String chooseEngineClientAlias( 504 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) { 505 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers); 506 return this.aliasStrategy.chooseAlias(validAliases, null); 507 } 508 509 @Override 510 public String chooseEngineServerAlias( 511 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) { 512 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers); 513 return this.aliasStrategy.chooseAlias(validAliases, null); 514 } 515 516 } 517 518 @Override 519 public String toString() { 520 return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType 521 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers 522 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers 523 + ", secureRandom=" + secureRandom + "]"; 524 } 525 526}