001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2017-2022 Florian Schmaus. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 */ 017 018package org.jivesoftware.smack; 019 020import java.io.ByteArrayInputStream; 021import java.io.FileInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.InvocationTargetException; 026import java.net.InetAddress; 027import java.net.UnknownHostException; 028import java.nio.charset.StandardCharsets; 029import java.security.KeyManagementException; 030import java.security.KeyStore; 031import java.security.KeyStoreException; 032import java.security.NoSuchAlgorithmException; 033import java.security.NoSuchProviderException; 034import java.security.Provider; 035import java.security.SecureRandom; 036import java.security.Security; 037import java.security.UnrecoverableKeyException; 038import java.security.cert.CertificateException; 039import java.util.Arrays; 040import java.util.Collection; 041import java.util.Collections; 042import java.util.HashSet; 043import java.util.Locale; 044import java.util.Set; 045import java.util.logging.Level; 046import java.util.logging.Logger; 047 048import javax.net.SocketFactory; 049import javax.net.ssl.HostnameVerifier; 050import javax.net.ssl.KeyManager; 051import javax.net.ssl.KeyManagerFactory; 052import javax.net.ssl.SSLContext; 053import javax.net.ssl.TrustManager; 054import javax.net.ssl.X509TrustManager; 055import javax.security.auth.callback.Callback; 056import javax.security.auth.callback.CallbackHandler; 057import javax.security.auth.callback.PasswordCallback; 058import javax.security.auth.callback.UnsupportedCallbackException; 059 060import org.jivesoftware.smack.datatypes.UInt16; 061import org.jivesoftware.smack.debugger.SmackDebuggerFactory; 062import org.jivesoftware.smack.internal.SmackTlsContext; 063import org.jivesoftware.smack.packet.id.StandardStanzaIdSource; 064import org.jivesoftware.smack.packet.id.StanzaIdSource; 065import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory; 066import org.jivesoftware.smack.proxy.ProxyInfo; 067import org.jivesoftware.smack.sasl.SASLMechanism; 068import org.jivesoftware.smack.sasl.core.SASLAnonymous; 069import org.jivesoftware.smack.util.CloseableUtil; 070import org.jivesoftware.smack.util.CollectionUtil; 071import org.jivesoftware.smack.util.DNSUtil; 072import org.jivesoftware.smack.util.Objects; 073import org.jivesoftware.smack.util.SslContextFactory; 074import org.jivesoftware.smack.util.StringUtils; 075import org.jivesoftware.smack.util.TLSUtils; 076import org.jivesoftware.smack.util.dns.SmackDaneProvider; 077import org.jivesoftware.smack.util.dns.SmackDaneVerifier; 078 079import org.jxmpp.jid.DomainBareJid; 080import org.jxmpp.jid.EntityBareJid; 081import org.jxmpp.jid.impl.JidCreate; 082import org.jxmpp.jid.parts.Resourcepart; 083import org.jxmpp.stringprep.XmppStringprepException; 084import org.minidns.dnsname.DnsName; 085import org.minidns.dnsname.InvalidDnsNameException; 086import org.minidns.util.InetAddressUtil; 087 088/** 089 * The connection configuration used for XMPP client-to-server connections. A well configured XMPP service will 090 * typically only require you to provide two parameters: The XMPP address, also known as the JID, of the user and the 091 * password. All other configuration parameters could ideally be determined automatically by Smack. Hence it is often 092 * enough to call {@link Builder#setXmppAddressAndPassword(CharSequence, String)}. 093 * <p> 094 * Technically there are typically at least two parameters required: Some kind of credentials for authentication. And 095 * the XMPP service domain. The credentials often consists of a username and password use for the SASL authentication. 096 * But there are also other authentication mechanisms, like client side certificates, which do not require a particular 097 * username and password. 098 * </p> 099 * <p> 100 * There are some misconceptions about XMPP client-to-server parameters: The first is that the username used for 101 * authentication will be equal to the localpart of the bound XMPP address after authentication. While this is usually 102 * true, it is not required. Technically the username used for authentication and the resulting XMPP address are 103 * completely independent from each other. The second common misconception steers from the terms "XMPP host" and "XMPP 104 * service domain": An XMPP service host is a system which hosts one or multiple XMPP domains. The "XMPP service domain" 105 * will be usually the domainpart of the bound JID. This domain is used to verify the remote endpoint, typically using 106 * TLS. This third misconception is that the XMPP service domain is required to become the domainpart of the bound JID. 107 * Again, while this is very common to be true, it is not strictly required. 108 * </p> 109 * 110 * @author Gaston Dombiak 111 * @author Florian Schmaus 112 */ 113public abstract class ConnectionConfiguration { 114 115 static { 116 Smack.ensureInitialized(); 117 } 118 119 private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName()); 120 121 /** 122 * The XMPP domain of the XMPP Service. Usually servers use the same service name as the name 123 * of the server. However, there are some servers like google where host would be 124 * talk.google.com and the serviceName would be gmail.com. 125 */ 126 protected final DomainBareJid xmppServiceDomain; 127 128 protected final DnsName xmppServiceDomainDnsName; 129 130 protected final InetAddress hostAddress; 131 protected final DnsName host; 132 protected final UInt16 port; 133 134 /** 135 * Used to get information from the user 136 */ 137 private final CallbackHandler callbackHandler; 138 139 private final SmackDebuggerFactory debuggerFactory; 140 141 // Holds the socket factory that is used to generate the socket in the connection 142 private final SocketFactory socketFactory; 143 144 private final CharSequence username; 145 private final String password; 146 private final Resourcepart resource; 147 148 private final Locale language; 149 150 /** 151 * The optional SASL authorization identity (see RFC 6120 § 6.3.8). 152 */ 153 private final EntityBareJid authzid; 154 155 /** 156 * Initial presence as of RFC 6121 § 4.2 157 * @see <a href="http://xmpp.org/rfcs/rfc6121.html#presence-initial">RFC 6121 § 4.2 Initial Presence</a> 158 */ 159 private final boolean sendPresence; 160 161 private final SecurityMode securityMode; 162 163 final SmackTlsContext smackTlsContext; 164 165 private final DnssecMode dnssecMode; 166 167 /** 168 * 169 */ 170 private final String[] enabledSSLProtocols; 171 172 /** 173 * 174 */ 175 private final String[] enabledSSLCiphers; 176 177 private final HostnameVerifier hostnameVerifier; 178 179 // Holds the proxy information (such as proxyhost, proxyport, username, password etc) 180 protected final ProxyInfo proxy; 181 182 protected final boolean allowNullOrEmptyUsername; 183 184 private final Set<String> enabledSaslMechanisms; 185 186 private final boolean compressionEnabled; 187 188 private final StanzaIdSourceFactory stanzaIdSourceFactory; 189 190 protected ConnectionConfiguration(Builder<?, ?> builder) { 191 try { 192 smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory, 193 builder.customX509TrustManager, builder.keyManagers, builder.sslContextSecureRandom, builder.keystoreType, builder.keystorePath, 194 builder.callbackHandler, builder.pkcs11Library); 195 } catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException 196 | KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException 197 | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException 198 | IllegalArgumentException | InvocationTargetException | UnsupportedCallbackException e) { 199 throw new IllegalArgumentException(e); 200 } 201 202 authzid = builder.authzid; 203 username = builder.username; 204 password = builder.password; 205 callbackHandler = builder.callbackHandler; 206 207 // Resource can be null, this means that the server must provide one 208 resource = builder.resource; 209 210 language = builder.language; 211 212 xmppServiceDomain = builder.xmppServiceDomain; 213 if (xmppServiceDomain == null) { 214 throw new IllegalArgumentException("Must define the XMPP domain"); 215 } 216 217 DnsName xmppServiceDomainDnsName; 218 try { 219 xmppServiceDomainDnsName = DnsName.from(xmppServiceDomain); 220 } catch (InvalidDnsNameException e) { 221 LOGGER.log(Level.INFO, 222 "Could not transform XMPP service domain '" + xmppServiceDomain 223 + "' to a DNS name. TLS X.509 certificate validiation may not be possible.", 224 e); 225 xmppServiceDomainDnsName = null; 226 } 227 this.xmppServiceDomainDnsName = xmppServiceDomainDnsName; 228 229 hostAddress = builder.hostAddress; 230 host = builder.host; 231 port = builder.port; 232 233 proxy = builder.proxy; 234 socketFactory = builder.socketFactory; 235 236 dnssecMode = builder.dnssecMode; 237 238 securityMode = builder.securityMode; 239 enabledSSLProtocols = builder.enabledSSLProtocols; 240 enabledSSLCiphers = builder.enabledSSLCiphers; 241 hostnameVerifier = builder.hostnameVerifier; 242 sendPresence = builder.sendPresence; 243 debuggerFactory = builder.debuggerFactory; 244 allowNullOrEmptyUsername = builder.allowEmptyOrNullUsername; 245 enabledSaslMechanisms = builder.enabledSaslMechanisms; 246 247 compressionEnabled = builder.compressionEnabled; 248 249 stanzaIdSourceFactory = builder.stanzaIdSourceFactory; 250 251 // If the enabledSaslmechanisms are set, then they must not be empty 252 assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty(); 253 } 254 255 private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory, 256 X509TrustManager trustManager, KeyManager[] keyManagers, SecureRandom secureRandom, String keystoreType, String keystorePath, 257 CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException, 258 CertificateException, IOException, KeyStoreException, NoSuchProviderException, 259 UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException, 260 NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, 261 IllegalAccessException, IllegalArgumentException, InvocationTargetException { 262 final SSLContext context; 263 if (sslContextFactory != null) { 264 context = sslContextFactory.createSslContext(); 265 } else { 266 // If the user didn't specify a SslContextFactory, use the default one 267 context = SSLContext.getInstance("TLS"); 268 } 269 270 // TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHanlder() and 271 // setPKCS11Library() in the builder, and all related fields and the parameters of this function. 272 if (keyManagers == null) { 273 keyManagers = Builder.getKeyManagersFrom(keystoreType, keystorePath, callbackHandler, pkcs11Library); 274 } 275 276 SmackDaneVerifier daneVerifier = null; 277 if (dnssecMode == DnssecMode.needsDnssecAndDane) { 278 SmackDaneProvider daneProvider = DNSUtil.getDaneProvider(); 279 if (daneProvider == null) { 280 throw new UnsupportedOperationException("DANE enabled but no SmackDaneProvider configured"); 281 } 282 daneVerifier = daneProvider.newInstance(); 283 if (daneVerifier == null) { 284 throw new IllegalStateException("DANE requested but DANE provider did not return a DANE verifier"); 285 } 286 287 // User requested DANE verification. 288 daneVerifier.init(context, keyManagers, trustManager, secureRandom); 289 } else { 290 final TrustManager[] trustManagers; 291 if (trustManager != null) { 292 trustManagers = new TrustManager[] { trustManager }; 293 } else { 294 // Ensure trustManagers is null in case there was no explicit trust manager provided, so that the 295 // default one is used. 296 trustManagers = null; 297 } 298 299 context.init(keyManagers, trustManagers, secureRandom); 300 } 301 302 return new SmackTlsContext(context, daneVerifier); 303 } 304 305 public String getHostString() { 306 if (hostAddress != null) { 307 return hostAddress.toString(); 308 } 309 if (host != null) { 310 return host.toString(); 311 } 312 return xmppServiceDomain.toString(); 313 } 314 315 public DnsName getHost() { 316 return host; 317 } 318 319 public InetAddress getHostAddress() { 320 return hostAddress; 321 } 322 323 public UInt16 getPort() { 324 return port; 325 } 326 327 /** 328 * Returns the server name of the target server. 329 * 330 * @return the server name of the target server. 331 * @deprecated use {@link #getXMPPServiceDomain()} instead. 332 */ 333 @Deprecated 334 public DomainBareJid getServiceName() { 335 return xmppServiceDomain; 336 } 337 338 /** 339 * Returns the XMPP domain used by this configuration. 340 * 341 * @return the XMPP domain. 342 */ 343 public DomainBareJid getXMPPServiceDomain() { 344 return xmppServiceDomain; 345 } 346 347 /** 348 * Returns the XMPP service domain as DNS name if possible. Note that since not every XMPP address domainpart is a 349 * valid DNS name, this method may return <code>null</code>. 350 * 351 * @return the XMPP service domain as DNS name or <code>null</code>. 352 * @since 4.3.4 353 */ 354 public DnsName getXmppServiceDomainAsDnsNameIfPossible() { 355 return xmppServiceDomainDnsName; 356 } 357 358 /** 359 * Returns the TLS security mode used when making the connection. By default, 360 * the mode is {@link SecurityMode#required}. 361 * 362 * @return the security mode. 363 */ 364 public SecurityMode getSecurityMode() { 365 return securityMode; 366 } 367 368 public DnssecMode getDnssecMode() { 369 return dnssecMode; 370 } 371 372 /** 373 * Return the enabled SSL/TLS protocols. 374 * 375 * @return the enabled SSL/TLS protocols 376 */ 377 public String[] getEnabledSSLProtocols() { 378 return enabledSSLProtocols; 379 } 380 381 /** 382 * Return the enabled SSL/TLS ciphers. 383 * 384 * @return the enabled SSL/TLS ciphers 385 */ 386 public String[] getEnabledSSLCiphers() { 387 return enabledSSLCiphers; 388 } 389 390 /** 391 * Returns the configured HostnameVerifier of this ConnectionConfiguration or the Smack default 392 * HostnameVerifier configured with 393 * {@link SmackConfiguration#setDefaultHostnameVerifier(HostnameVerifier)}. 394 * 395 * @return a configured HostnameVerifier or <code>null</code> 396 */ 397 public HostnameVerifier getHostnameVerifier() { 398 if (hostnameVerifier != null) 399 return hostnameVerifier; 400 return SmackConfiguration.getDefaultHostnameVerifier(); 401 } 402 403 /** 404 * Returns the Smack debugger factory. 405 * 406 * @return the Smack debugger factory or <code>null</code> 407 */ 408 public SmackDebuggerFactory getDebuggerFactory() { 409 return debuggerFactory; 410 } 411 412 /** 413 * Returns a CallbackHandler to obtain information, such as the password or 414 * principal information during the SASL authentication. A CallbackHandler 415 * will be used <b>ONLY</b> if no password was specified during the login while 416 * using SASL authentication. 417 * 418 * @return a CallbackHandler to obtain information, such as the password or 419 * principal information during the SASL authentication. 420 */ 421 public CallbackHandler getCallbackHandler() { 422 return callbackHandler; 423 } 424 425 /** 426 * Returns the socket factory used to create new xmppConnection sockets. 427 * This is useful when connecting through SOCKS5 proxies. 428 * 429 * @return socketFactory used to create new sockets. 430 */ 431 public SocketFactory getSocketFactory() { 432 return this.socketFactory; 433 } 434 435 /** 436 * Get the configured proxy information (if any). 437 * 438 * @return the configured proxy information or <code>null</code>. 439 */ 440 public ProxyInfo getProxyInfo() { 441 return proxy; 442 } 443 444 /** 445 * An enumeration for TLS security modes that are available when making a connection 446 * to the XMPP server. 447 */ 448 public enum SecurityMode { 449 450 /** 451 * Security via TLS encryption is required in order to connect. If the server 452 * does not offer TLS or if the TLS negotiation fails, the connection to the server 453 * will fail. 454 */ 455 required, 456 457 /** 458 * Security via TLS encryption is used whenever it's available. This is the 459 * default setting. 460 * <p> 461 * <b>Do not use this setting</b> unless you can't use {@link #required}. An attacker could easily perform a 462 * Man-in-the-middle attack and prevent TLS from being used, leaving you with an unencrypted (and 463 * unauthenticated) connection. 464 * </p> 465 */ 466 ifpossible, 467 468 /** 469 * Security via TLS encryption is disabled and only un-encrypted connections will 470 * be used. If only TLS encryption is available from the server, the connection 471 * will fail. 472 */ 473 disabled 474 } 475 476 /** 477 * Determines the requested DNSSEC security mode. 478 * <b>Note that Smack's support for DNSSEC/DANE is experimental!</b> 479 * <p> 480 * The default '{@link #disabled}' means that neither DNSSEC nor DANE verification will be performed. When 481 * '{@link #needsDnssec}' is used, then the connection will not be established if the resource records used to connect 482 * to the XMPP service are not authenticated by DNSSEC. Additionally, if '{@link #needsDnssecAndDane}' is used, then 483 * the XMPP service's TLS certificate is verified using DANE. 484 * 485 */ 486 public enum DnssecMode { 487 488 /** 489 * Do not perform any DNSSEC authentication or DANE verification. 490 */ 491 disabled, 492 493 /** 494 * <b>Experimental!</b> 495 * Require all DNS information to be authenticated by DNSSEC. 496 */ 497 needsDnssec, 498 499 /** 500 * <b>Experimental!</b> 501 * Require all DNS information to be authenticated by DNSSEC and require the XMPP service's TLS certificate to be verified using DANE. 502 */ 503 needsDnssecAndDane, 504 505 } 506 507 /** 508 * Returns the username to use when trying to reconnect to the server. 509 * 510 * @return the username to use when trying to reconnect to the server. 511 */ 512 public CharSequence getUsername() { 513 return this.username; 514 } 515 516 /** 517 * Returns the password to use when trying to reconnect to the server. 518 * 519 * @return the password to use when trying to reconnect to the server. 520 */ 521 public String getPassword() { 522 return this.password; 523 } 524 525 /** 526 * Returns the resource to use when trying to reconnect to the server. 527 * 528 * @return the resource to use when trying to reconnect to the server. 529 */ 530 public Resourcepart getResource() { 531 return resource; 532 } 533 534 /** 535 * Returns the stream language to use when connecting to the server. 536 * 537 * @return the stream language to use when connecting to the server. 538 */ 539 public Locale getLanguage() { 540 return language; 541 } 542 543 /** 544 * Returns the xml:lang string of the stream language to use when connecting to the server. 545 * 546 * <p>If the developer sets the language to null, this will also return null, leading to 547 * the removal of the xml:lang tag from the stream. If a Locale("") is configured, this will 548 * return "", which can be used as an override.</p> 549 * 550 * @return the stream language to use when connecting to the server. 551 */ 552 public String getXmlLang() { 553 // TODO: Change to Locale.toLanguageTag() once Smack's minimum Android API level is 21 or higher. 554 // This will need a workaround for new Locale("").getLanguageTag() returning "und". Expected 555 // behavior of this function: 556 // - returns null if language is null 557 // - returns "" if language.getLanguage() returns the empty string 558 // - returns language.toLanguageTag() otherwise 559 return language != null ? language.toString().replace("_", "-") : null; 560 } 561 562 /** 563 * Returns the optional XMPP address to be requested as the SASL authorization identity. 564 * 565 * @return the authorization identifier. 566 * @see <a href="http://tools.ietf.org/html/rfc6120#section-6.3.8">RFC 6120 § 6.3.8. Authorization Identity</a> 567 * @since 4.2 568 */ 569 public EntityBareJid getAuthzid() { 570 return authzid; 571 } 572 573 /** 574 * Returns true if an available presence should be sent when logging in while reconnecting. 575 * 576 * @return true if an available presence should be sent when logging in while reconnecting 577 */ 578 public boolean isSendPresence() { 579 return sendPresence; 580 } 581 582 /** 583 * Returns true if the connection is going to use stream compression. Stream compression 584 * will be requested after TLS was established (if TLS was enabled) and only if the server 585 * offered stream compression. With stream compression network traffic can be reduced 586 * up to 90%. By default compression is disabled. 587 * 588 * @return true if the connection is going to use stream compression. 589 */ 590 public boolean isCompressionEnabled() { 591 return compressionEnabled; 592 } 593 594 /** 595 * Check if the given SASL mechansism is enabled in this connection configuration. 596 * 597 * @param saslMechanism TODO javadoc me please 598 * @return true if the given SASL mechanism is enabled, false otherwise. 599 */ 600 public boolean isEnabledSaslMechanism(String saslMechanism) { 601 // If enabledSaslMechanisms is not set, then all mechanisms which are not blacklisted are enabled per default. 602 if (enabledSaslMechanisms == null) { 603 return !SASLAuthentication.getBlacklistedSASLMechanisms().contains(saslMechanism); 604 } 605 return enabledSaslMechanisms.contains(saslMechanism); 606 } 607 608 /** 609 * Return the explicitly enabled SASL mechanisms. May return <code>null</code> if no SASL mechanisms where 610 * explicitly enabled, i.e. all SALS mechanisms supported and announced by the service will be considered. 611 * 612 * @return the enabled SASL mechanisms or <code>null</code>. 613 */ 614 public Set<String> getEnabledSaslMechanisms() { 615 if (enabledSaslMechanisms == null) { 616 return null; 617 } 618 return Collections.unmodifiableSet(enabledSaslMechanisms); 619 } 620 621 StanzaIdSource constructStanzaIdSource() { 622 return stanzaIdSourceFactory.constructStanzaIdSource(); 623 } 624 625 /** 626 * A builder for XMPP connection configurations. 627 * <p> 628 * This is an abstract class that uses the builder design pattern and the "getThis() trick" to recover the type of 629 * the builder in a class hierarchies with a self-referential generic supertype. Otherwise chaining of build 630 * instructions from the superclasses followed by build instructions of a subclass would not be possible, because 631 * the superclass build instructions would return the builder of the superclass and not the one of the subclass. You 632 * can read more about it a Angelika Langer's Generics FAQ, especially the entry <a 633 * href="http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206">What is the 634 * "getThis()" trick?</a>. 635 * </p> 636 * 637 * @param <B> the builder type parameter. 638 * @param <C> the resulting connection configuration type parameter. 639 */ 640 public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> { 641 private SecurityMode securityMode = SecurityMode.required; 642 private DnssecMode dnssecMode = DnssecMode.disabled; 643 private KeyManager[] keyManagers; 644 private SecureRandom sslContextSecureRandom; 645 private String keystorePath; 646 private String keystoreType; 647 private String pkcs11Library = "pkcs11.config"; 648 private SslContextFactory sslContextFactory; 649 private String[] enabledSSLProtocols; 650 private String[] enabledSSLCiphers; 651 private HostnameVerifier hostnameVerifier; 652 private EntityBareJid authzid; 653 private CharSequence username; 654 private String password; 655 private Resourcepart resource; 656 private Locale language = Locale.getDefault(); 657 private boolean sendPresence = true; 658 private ProxyInfo proxy; 659 private CallbackHandler callbackHandler; 660 private SmackDebuggerFactory debuggerFactory; 661 private SocketFactory socketFactory; 662 private DomainBareJid xmppServiceDomain; 663 private InetAddress hostAddress; 664 private DnsName host; 665 private UInt16 port = UInt16.from(5222); 666 private boolean allowEmptyOrNullUsername = false; 667 private boolean saslMechanismsSealed; 668 private Set<String> enabledSaslMechanisms; 669 private X509TrustManager customX509TrustManager; 670 private boolean compressionEnabled = false; 671 private StanzaIdSourceFactory stanzaIdSourceFactory = new StandardStanzaIdSource.Factory(); 672 673 protected Builder() { 674 if (SmackConfiguration.DEBUG) { 675 enableDefaultDebugger(); 676 } 677 } 678 679 /** 680 * Convenience method to configure the username, password and XMPP service domain. 681 * 682 * @param jid the XMPP address of the user. 683 * @param password the password of the user. 684 * @return a reference to this builder. 685 * @throws XmppStringprepException in case the XMPP address is not valid. 686 * @see #setXmppAddressAndPassword(EntityBareJid, String) 687 * @since 4.4.0 688 */ 689 public B setXmppAddressAndPassword(CharSequence jid, String password) throws XmppStringprepException { 690 return setXmppAddressAndPassword(JidCreate.entityBareFrom(jid), password); 691 } 692 693 /** 694 * Convenience method to configure the username, password and XMPP service domain. The localpart of the provided 695 * JID is used as username and the domanipart is used as XMPP service domain. 696 * <p> 697 * Please note that this does and can not configure the client XMPP address. XMPP services are not required to 698 * assign bound JIDs where the localpart matches the username and the domainpart matches the verified domainpart. 699 * Although most services will follow that pattern. 700 * </p> 701 * 702 * @param jid TODO javadoc me please 703 * @param password TODO javadoc me please 704 * @return a reference to this builder. 705 * @since 4.4.0 706 */ 707 public B setXmppAddressAndPassword(EntityBareJid jid, String password) { 708 setUsernameAndPassword(jid.getLocalpart(), password); 709 return setXmppDomain(jid.asDomainBareJid()); 710 } 711 712 /** 713 * Set the XMPP entities username and password. 714 * <p> 715 * The username is usually the localpart of the clients JID. But some SASL mechanisms or services may require a different 716 * format (e.g. the full JID) as used authorization identity. 717 * </p> 718 * 719 * @param username the username or authorization identity 720 * @param password the password or token used to authenticate 721 * @return a reference to this builder. 722 */ 723 public B setUsernameAndPassword(CharSequence username, String password) { 724 this.username = username; 725 this.password = password; 726 return getThis(); 727 } 728 729 /** 730 * Set the XMPP domain. The XMPP domain is what follows after the '@' sign in XMPP addresses (JIDs). 731 * 732 * @param serviceName the service name 733 * @return a reference to this builder. 734 * @deprecated use {@link #setXmppDomain(DomainBareJid)} instead. 735 */ 736 @Deprecated 737 public B setServiceName(DomainBareJid serviceName) { 738 return setXmppDomain(serviceName); 739 } 740 741 /** 742 * Set the XMPP domain. The XMPP domain is what follows after the '@' sign in XMPP addresses (JIDs). 743 * 744 * @param xmppDomain the XMPP domain. 745 * @return a reference to this builder. 746 */ 747 public B setXmppDomain(DomainBareJid xmppDomain) { 748 this.xmppServiceDomain = xmppDomain; 749 return getThis(); 750 } 751 752 /** 753 * Set the XMPP domain. The XMPP domain is what follows after the '@' sign in XMPP addresses (JIDs). 754 * 755 * @param xmppServiceDomain the XMPP domain. 756 * @return a reference to this builder. 757 * @throws XmppStringprepException if the given string is not a domain bare JID. 758 */ 759 public B setXmppDomain(String xmppServiceDomain) throws XmppStringprepException { 760 this.xmppServiceDomain = JidCreate.domainBareFrom(xmppServiceDomain); 761 return getThis(); 762 } 763 764 /** 765 * Set the resource we are requesting from the server. 766 * <p> 767 * If <code>resource</code> is <code>null</code>, the default, then the server will automatically create a 768 * resource for the client. Note that XMPP clients only suggest this resource to the server. XMPP servers are 769 * allowed to ignore the client suggested resource and instead assign a completely different 770 * resource (see RFC 6120 § 7.7.1). 771 * </p> 772 * 773 * @param resource the resource to use. 774 * @return a reference to this builder. 775 * @see <a href="https://tools.ietf.org/html/rfc6120#section-7.7.1">RFC 6120 § 7.7.1</a> 776 */ 777 public B setResource(Resourcepart resource) { 778 this.resource = resource; 779 return getThis(); 780 } 781 782 /** 783 * Set the stream language. 784 * 785 * @param language the language to use. 786 * @return a reference to this builder. 787 * @see <a href="https://tools.ietf.org/html/rfc6120#section-4.7.4">RFC 6120 § 4.7.4</a> 788 * @see <a href="https://www.w3.org/TR/xml/#sec-lang-tag">XML 1.0 § 2.12 Language Identification</a> 789 */ 790 public B setLanguage(Locale language) { 791 this.language = language; 792 return getThis(); 793 } 794 795 /** 796 * Set the resource we are requesting from the server. 797 * 798 * @param resource the non-null CharSequence to use a resource. 799 * @return a reference ot this builder. 800 * @throws XmppStringprepException if the CharSequence is not a valid resourcepart. 801 * @see #setResource(Resourcepart) 802 */ 803 public B setResource(CharSequence resource) throws XmppStringprepException { 804 Objects.requireNonNull(resource, "resource must not be null"); 805 return setResource(Resourcepart.from(resource.toString())); 806 } 807 808 /** 809 * Set the Internet address of the host providing the XMPP service. If set, then this will overwrite anything 810 * set via {@link #setHost(CharSequence)}. 811 * 812 * @param address the Internet address of the host providing the XMPP service. 813 * @return a reference to this builder. 814 * @since 4.2 815 */ 816 public B setHostAddress(InetAddress address) { 817 this.hostAddress = address; 818 return getThis(); 819 } 820 821 /** 822 * Set the name of the host providing the XMPP service. This method takes DNS names and 823 * IP addresses. 824 * 825 * @param host the DNS name of the host providing the XMPP service. 826 * @return a reference to this builder. 827 */ 828 public B setHost(CharSequence host) { 829 String fqdnOrIpString = host.toString(); 830 if (InetAddressUtil.isIpAddress(fqdnOrIpString)) { 831 InetAddress hostInetAddress; 832 try { 833 hostInetAddress = InetAddress.getByName(fqdnOrIpString); 834 } 835 catch (UnknownHostException e) { 836 // Should never happen. 837 throw new AssertionError(e); 838 } 839 setHostAddress(hostInetAddress); 840 } else { 841 DnsName dnsName = DnsName.from(fqdnOrIpString); 842 setHost(dnsName); 843 } 844 return getThis(); 845 } 846 847 /** 848 * Set the name of the host providing the XMPP service. Note that this method does only allow DNS names and not 849 * IP addresses. Use {@link #setHostAddress(InetAddress)} if you want to explicitly set the Internet address of 850 * the host providing the XMPP service. 851 * 852 * @param host the DNS name of the host providing the XMPP service. 853 * @return a reference to this builder. 854 */ 855 public B setHost(DnsName host) { 856 this.host = host; 857 return getThis(); 858 } 859 860 /** 861 * Set the host to connect to by either its fully qualified domain name (FQDN) or its IP. 862 * 863 * @param fqdnOrIp a CharSequence either representing the FQDN or the IP of the host. 864 * @return a reference to this builder. 865 * @see #setHost(DnsName) 866 * @see #setHostAddress(InetAddress) 867 * @since 4.3.2 868 * @deprecated use {@link #setHost(CharSequence)} instead. 869 */ 870 @Deprecated 871 // TODO: Remove in Smack 4.5. 872 public B setHostAddressByNameOrIp(CharSequence fqdnOrIp) { 873 return setHost(fqdnOrIp); 874 } 875 876 public B setPort(int port) { 877 if (port < 0 || port > 65535) { 878 throw new IllegalArgumentException( 879 "Port must be a 16-bit unsigned integer (i.e. between 0-65535. Port was: " + port); 880 } 881 UInt16 portUint16 = UInt16.from(port); 882 return setPort(portUint16); 883 } 884 885 public B setPort(UInt16 port) { 886 this.port = Objects.requireNonNull(port); 887 return getThis(); 888 } 889 890 /** 891 * Sets a CallbackHandler to obtain information, such as the password or 892 * principal information during the SASL authentication. A CallbackHandler 893 * will be used <b>ONLY</b> if no password was specified during the login while 894 * using SASL authentication. 895 * 896 * @param callbackHandler to obtain information, such as the password or 897 * principal information during the SASL authentication. 898 * @return a reference to this builder. 899 * @deprecated set a callback-handler aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or 900 * {@link #setKeyManagers(KeyManager[])}, created by 901 * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. 902 */ 903 // TODO: Remove in Smack 4.6. 904 @Deprecated 905 public B setCallbackHandler(CallbackHandler callbackHandler) { 906 this.callbackHandler = callbackHandler; 907 return getThis(); 908 } 909 910 public B setDnssecMode(DnssecMode dnssecMode) { 911 this.dnssecMode = Objects.requireNonNull(dnssecMode, "DNSSEC mode must not be null"); 912 return getThis(); 913 } 914 915 public B setCustomX509TrustManager(X509TrustManager x509TrustManager) { 916 this.customX509TrustManager = x509TrustManager; 917 return getThis(); 918 } 919 920 /** 921 * Sets the TLS security mode used when making the connection. By default, 922 * the mode is {@link SecurityMode#required}. 923 * 924 * @param securityMode the security mode. 925 * @return a reference to this builder. 926 */ 927 public B setSecurityMode(SecurityMode securityMode) { 928 this.securityMode = securityMode; 929 return getThis(); 930 } 931 932 /** 933 * Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection. 934 * 935 * @param keyManagers an array of {@link KeyManager}s to initialize the {@link SSLContext} with. 936 * @return a reference to this builder. 937 * @since 4.4.5 938 */ 939 public B setKeyManagers(KeyManager[] keyManagers) { 940 this.keyManagers = keyManagers; 941 return getThis(); 942 } 943 944 /** 945 * Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection. 946 * 947 * @param keyManager the {@link KeyManager}s to initialize the {@link SSLContext} with. 948 * @return a reference to this builder. 949 * @see #setKeyManagers(KeyManager[]) 950 * @since 4.4.5 951 */ 952 public B setKeyManager(KeyManager keyManager) { 953 KeyManager[] keyManagers = new KeyManager[] { keyManager }; 954 return setKeyManagers(keyManagers); 955 } 956 957 /** 958 * Set the {@link SecureRandom} used to initialize the {@link SSLContext} used by Smack to establish the XMPP 959 * connection. Note that you usually do not need (nor want) to set this. Because if the {@link SecureRandom} is 960 * not explicitly set, Smack will initialize the {@link SSLContext} with <code>null</code> as 961 * {@link SecureRandom} argument. And all sane {@link SSLContext} implementations will then select a safe secure 962 * random source by default. 963 * 964 * @param secureRandom the {@link SecureRandom} to initialize the {@link SSLContext} with. 965 * @return a reference to this builder. 966 * @since 4.4.5 967 */ 968 public B setSslContextSecureRandom(SecureRandom secureRandom) { 969 this.sslContextSecureRandom = secureRandom; 970 return getThis(); 971 } 972 973 /** 974 * Sets the path to the keystore file. The key store file contains the 975 * certificates that may be used to authenticate the client to the server, 976 * in the event the server requests or requires it. 977 * 978 * @param keystorePath the path to the keystore file. 979 * @return a reference to this builder. 980 * @deprecated set a keystore-path aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or 981 * {@link #setKeyManagers(KeyManager[])}, created by 982 * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. 983 */ 984 // TODO: Remove in Smack 4.6. 985 @Deprecated 986 public B setKeystorePath(String keystorePath) { 987 this.keystorePath = keystorePath; 988 return getThis(); 989 } 990 991 /** 992 * Sets the keystore type. 993 * 994 * @param keystoreType the keystore type. 995 * @return a reference to this builder. 996 * @deprecated set a key-type aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or 997 * {@link #setKeyManagers(KeyManager[])}, created by 998 * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. 999 */ 1000 // TODO: Remove in Smack 4.6. 1001 @Deprecated 1002 public B setKeystoreType(String keystoreType) { 1003 this.keystoreType = keystoreType; 1004 return getThis(); 1005 } 1006 1007 /** 1008 * Sets the PKCS11 library file location, needed when the 1009 * Keystore type is PKCS11. 1010 * 1011 * @param pkcs11Library the path to the PKCS11 library file. 1012 * @return a reference to this builder. 1013 * @deprecated set a PKCS11-library aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or 1014 * {@link #setKeyManagers(KeyManager[])}, created by 1015 * {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead. 1016 */ 1017 // TODO: Remove in Smack 4.6. 1018 @Deprecated 1019 public B setPKCS11Library(String pkcs11Library) { 1020 this.pkcs11Library = pkcs11Library; 1021 return getThis(); 1022 } 1023 1024 /** 1025 * Sets a custom SSLContext for creating SSL sockets. 1026 * <p> 1027 * For more information on how to create a SSLContext see <a href= 1028 * "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager" 1029 * >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a> 1030 * 1031 * @param context the custom SSLContext for new sockets. 1032 * @return a reference to this builder. 1033 * @deprecated use {@link #setSslContextFactory(SslContextFactory)} instead}. 1034 */ 1035 // TODO: Remove in Smack 4.5. 1036 @Deprecated 1037 public B setCustomSSLContext(SSLContext context) { 1038 return setSslContextFactory(() -> { 1039 return context; 1040 }); 1041 } 1042 1043 /** 1044 * Sets a custom SSLContext for creating SSL sockets. 1045 * <p> 1046 * For more information on how to create a SSLContext see <a href= 1047 * "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager" 1048 * >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a> 1049 * 1050 * @param sslContextFactory the custom SSLContext for new sockets. 1051 * @return a reference to this builder. 1052 */ 1053 public B setSslContextFactory(SslContextFactory sslContextFactory) { 1054 this.sslContextFactory = Objects.requireNonNull(sslContextFactory, "The provided SslContextFactory must not be null"); 1055 return getThis(); 1056 } 1057 1058 /** 1059 * Set the enabled SSL/TLS protocols. 1060 * 1061 * @param enabledSSLProtocols TODO javadoc me please 1062 * @return a reference to this builder. 1063 */ 1064 public B setEnabledSSLProtocols(String[] enabledSSLProtocols) { 1065 this.enabledSSLProtocols = enabledSSLProtocols; 1066 return getThis(); 1067 } 1068 1069 /** 1070 * Set the enabled SSL/TLS ciphers. 1071 * 1072 * @param enabledSSLCiphers the enabled SSL/TLS ciphers 1073 * @return a reference to this builder. 1074 */ 1075 public B setEnabledSSLCiphers(String[] enabledSSLCiphers) { 1076 this.enabledSSLCiphers = enabledSSLCiphers; 1077 return getThis(); 1078 } 1079 1080 /** 1081 * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections 1082 * created with this ConnectionConfiguration. 1083 * 1084 * @param verifier TODO javadoc me please 1085 * @return a reference to this builder. 1086 */ 1087 public B setHostnameVerifier(HostnameVerifier verifier) { 1088 hostnameVerifier = verifier; 1089 return getThis(); 1090 } 1091 1092 /** 1093 * Sets if an initial available presence will be sent to the server. By default 1094 * an available presence will be sent to the server indicating that this presence 1095 * is not online and available to receive messages. If you want to log in without 1096 * being 'noticed' then pass a <code>false</code> value. 1097 * 1098 * @param sendPresence true if an initial available presence will be sent while logging in. 1099 * @return a reference to this builder. 1100 */ 1101 public B setSendPresence(boolean sendPresence) { 1102 this.sendPresence = sendPresence; 1103 return getThis(); 1104 } 1105 1106 public B enableDefaultDebugger() { 1107 this.debuggerFactory = SmackConfiguration.getDefaultSmackDebuggerFactory(); 1108 assert this.debuggerFactory != null; 1109 return getThis(); 1110 } 1111 1112 /** 1113 * Set the Smack debugger factory used to construct Smack debuggers. 1114 * 1115 * @param debuggerFactory the Smack debugger factory. 1116 * @return a reference to this builder. 1117 */ 1118 public B setDebuggerFactory(SmackDebuggerFactory debuggerFactory) { 1119 this.debuggerFactory = debuggerFactory; 1120 return getThis(); 1121 } 1122 1123 /** 1124 * Sets the socket factory used to create new xmppConnection sockets. 1125 * This is useful when connecting through SOCKS5 proxies. 1126 * 1127 * @param socketFactory used to create new sockets. 1128 * @return a reference to this builder. 1129 */ 1130 public B setSocketFactory(SocketFactory socketFactory) { 1131 this.socketFactory = socketFactory; 1132 return getThis(); 1133 } 1134 1135 /** 1136 * Set the information about the Proxy used for the connection. 1137 * 1138 * @param proxyInfo the Proxy information. 1139 * @return a reference to this builder. 1140 */ 1141 public B setProxyInfo(ProxyInfo proxyInfo) { 1142 this.proxy = proxyInfo; 1143 return getThis(); 1144 } 1145 1146 /** 1147 * Allow <code>null</code> or the empty String as username. 1148 * 1149 * Some SASL mechanisms (e.g. SASL External) may also signal the username (as "authorization identity"), in 1150 * which case Smack should not throw an IllegalArgumentException when the username is not set. 1151 * 1152 * @return a reference to this builder. 1153 */ 1154 public B allowEmptyOrNullUsernames() { 1155 allowEmptyOrNullUsername = true; 1156 return getThis(); 1157 } 1158 1159 /** 1160 * Perform anonymous authentication using SASL ANONYMOUS. Your XMPP service must support this authentication 1161 * mechanism. This method also calls {@link #addEnabledSaslMechanism(String)} with "ANONYMOUS" as argument. 1162 * 1163 * @return a reference to this builder. 1164 */ 1165 public B performSaslAnonymousAuthentication() { 1166 if (!SASLAuthentication.isSaslMechanismRegistered(SASLAnonymous.NAME)) { 1167 throw new IllegalArgumentException("SASL " + SASLAnonymous.NAME + " is not registered"); 1168 } 1169 throwIfEnabledSaslMechanismsSet(); 1170 1171 allowEmptyOrNullUsernames(); 1172 addEnabledSaslMechanism(SASLAnonymous.NAME); 1173 saslMechanismsSealed = true; 1174 return getThis(); 1175 } 1176 1177 /** 1178 * Perform authentication using SASL EXTERNAL. Your XMPP service must support this 1179 * authentication mechanism. This method also calls {@link #addEnabledSaslMechanism(String)} with "EXTERNAL" as 1180 * argument. It also calls {@link #allowEmptyOrNullUsernames()} and {@link #setSecurityMode(ConnectionConfiguration.SecurityMode)} to 1181 * {@link SecurityMode#required}. 1182 * 1183 * @param sslContext custom SSLContext to be used. 1184 * @return a reference to this builder. 1185 */ 1186 public B performSaslExternalAuthentication(SSLContext sslContext) { 1187 if (!SASLAuthentication.isSaslMechanismRegistered(SASLMechanism.EXTERNAL)) { 1188 throw new IllegalArgumentException("SASL " + SASLMechanism.EXTERNAL + " is not registered"); 1189 } 1190 setCustomSSLContext(sslContext); 1191 throwIfEnabledSaslMechanismsSet(); 1192 1193 allowEmptyOrNullUsernames(); 1194 setSecurityMode(SecurityMode.required); 1195 addEnabledSaslMechanism(SASLMechanism.EXTERNAL); 1196 saslMechanismsSealed = true; 1197 return getThis(); 1198 } 1199 1200 private void throwIfEnabledSaslMechanismsSet() { 1201 if (enabledSaslMechanisms != null) { 1202 throw new IllegalStateException("Enabled SASL mechanisms found"); 1203 } 1204 } 1205 1206 /** 1207 * Add the given mechanism to the enabled ones. See {@link #addEnabledSaslMechanism(Collection)} for a discussion about enabled SASL mechanisms. 1208 * 1209 * @param saslMechanism the name of the mechanism to enable. 1210 * @return a reference to this builder. 1211 */ 1212 public B addEnabledSaslMechanism(String saslMechanism) { 1213 return addEnabledSaslMechanism(Arrays.asList(StringUtils.requireNotNullNorEmpty(saslMechanism, 1214 "saslMechanism must not be null nor empty"))); 1215 } 1216 1217 /** 1218 * Enable the given SASL mechanisms. If you never add a mechanism to the set of enabled ones, <b>all mechanisms 1219 * known to Smack</b> will be enabled. Only explicitly enable particular SASL mechanisms if you want to limit 1220 * the used mechanisms to the enabled ones. 1221 * 1222 * @param saslMechanisms a collection of names of mechanisms to enable. 1223 * @return a reference to this builder. 1224 */ 1225 public B addEnabledSaslMechanism(Collection<String> saslMechanisms) { 1226 if (saslMechanismsSealed) { 1227 throw new IllegalStateException("The enabled SASL mechanisms are sealed, you can not add new ones"); 1228 } 1229 CollectionUtil.requireNotEmpty(saslMechanisms, "saslMechanisms"); 1230 Set<String> blacklistedMechanisms = SASLAuthentication.getBlacklistedSASLMechanisms(); 1231 for (String mechanism : saslMechanisms) { 1232 if (!SASLAuthentication.isSaslMechanismRegistered(mechanism)) { 1233 throw new IllegalArgumentException("SASL " + mechanism + " is not available. Consider registering it with Smack"); 1234 } 1235 if (blacklistedMechanisms.contains(mechanism)) { 1236 throw new IllegalArgumentException("SALS " + mechanism + " is blacklisted."); 1237 } 1238 } 1239 if (enabledSaslMechanisms == null) { 1240 enabledSaslMechanisms = new HashSet<>(saslMechanisms.size()); 1241 } 1242 enabledSaslMechanisms.addAll(saslMechanisms); 1243 return getThis(); 1244 } 1245 1246 /** 1247 * Set the XMPP address to be used as authorization identity. 1248 * <p> 1249 * In XMPP, authorization identities are bare jids. In general, callers should allow the server to select the 1250 * authorization identifier automatically, and not call this. Note that setting the authzid does not set the XMPP 1251 * service domain, which should typically match. 1252 * Calling this will also SASL CRAM, since this mechanism does not support authzid. 1253 * </p> 1254 * 1255 * @param authzid The BareJid to be requested as the authorization identifier. 1256 * @return a reference to this builder. 1257 * @see <a href="http://tools.ietf.org/html/rfc6120#section-6.3.8">RFC 6120 § 6.3.8. Authorization Identity</a> 1258 * @since 4.2 1259 */ 1260 public B setAuthzid(EntityBareJid authzid) { 1261 this.authzid = authzid; 1262 return getThis(); 1263 } 1264 1265 /** 1266 * Sets if the connection is going to use compression (default false). 1267 * 1268 * Compression is only activated if the server offers compression. With compression network 1269 * traffic can be reduced up to 90%. By default compression is disabled. 1270 * 1271 * @param compressionEnabled if the connection is going to use compression on the HTTP level. 1272 * @return a reference to this object. 1273 */ 1274 public B setCompressionEnabled(boolean compressionEnabled) { 1275 this.compressionEnabled = compressionEnabled; 1276 return getThis(); 1277 } 1278 1279 /** 1280 * Set the factory for stanza ID sources to use. 1281 * 1282 * @param stanzaIdSourceFactory the factory for stanza ID sources to use. 1283 * @return a reference to this builder. 1284 * @since 4.4 1285 */ 1286 public B setStanzaIdSourceFactory(StanzaIdSourceFactory stanzaIdSourceFactory) { 1287 this.stanzaIdSourceFactory = Objects.requireNonNull(stanzaIdSourceFactory); 1288 return getThis(); 1289 } 1290 1291 public abstract C build(); 1292 1293 protected abstract B getThis(); 1294 1295 public static KeyManager[] getKeyManagersFrom(String keystoreType, String keystorePath, 1296 CallbackHandler callbackHandler, String pkcs11Library) 1297 throws NoSuchMethodException, SecurityException, ClassNotFoundException, KeyStoreException, 1298 NoSuchProviderException, NoSuchAlgorithmException, CertificateException, IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, UnsupportedCallbackException, UnrecoverableKeyException { 1299 KeyManager[] keyManagers = null; 1300 KeyStore ks = null; 1301 PasswordCallback pcb = null; 1302 1303 if ("PKCS11".equals(keystoreType)) { 1304 Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class); 1305 String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library; 1306 ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8)); 1307 Provider p = (Provider) c.newInstance(config); 1308 Security.addProvider(p); 1309 ks = KeyStore.getInstance("PKCS11", p); 1310 pcb = new PasswordCallback("PKCS11 Password: ", false); 1311 callbackHandler.handle(new Callback[] { pcb }); 1312 ks.load(null, pcb.getPassword()); 1313 } else if ("Apple".equals(keystoreType)) { 1314 ks = KeyStore.getInstance("KeychainStore", "Apple"); 1315 ks.load(null, null); 1316 // pcb = new PasswordCallback("Apple Keychain",false); 1317 // pcb.setPassword(null); 1318 } else if (keystoreType != null) { 1319 ks = KeyStore.getInstance(keystoreType); 1320 if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) { 1321 pcb = new PasswordCallback("Keystore Password: ", false); 1322 callbackHandler.handle(new Callback[] { pcb }); 1323 ks.load(new FileInputStream(keystorePath), pcb.getPassword()); 1324 } else { 1325 InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); 1326 try { 1327 // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous 1328 // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 1329 char[] password = "changeit".toCharArray(); 1330 try { 1331 ks.load(stream, password); 1332 } finally { 1333 CloseableUtil.maybeClose(stream); 1334 } 1335 } catch (IOException e) { 1336 LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); 1337 1338 ks = KeyStore.getInstance("jks"); 1339 // Open the stream again, so that we read it from the beginning. 1340 stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); 1341 try { 1342 ks.load(stream, null); 1343 } finally { 1344 CloseableUtil.maybeClose(stream); 1345 } 1346 } 1347 } 1348 } 1349 1350 if (ks != null) { 1351 String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); 1352 KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); 1353 if (kmf != null) { 1354 if (pcb == null) { 1355 kmf.init(ks, null); 1356 } else { 1357 kmf.init(ks, pcb.getPassword()); 1358 pcb.clearPassword(); 1359 } 1360 keyManagers = kmf.getKeyManagers(); 1361 } 1362 } 1363 1364 return keyManagers; 1365 } 1366 } 1367}