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}