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