mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-22 10:37:59 +01:00
[core] Rework TLS logic
This moves the logic in AbstractXMPPConnection.getSmackTlsContext() into the ConnectionConfiguration constructor. Also introduce SslContextFactory and use it in ConnectionConfiguration.
This commit is contained in:
parent
7156849c77
commit
f5448c5faa
14 changed files with 252 additions and 314 deletions
|
@ -16,24 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack;
|
package org.jivesoftware.smack;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.Security;
|
|
||||||
import java.security.UnrecoverableKeyException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -56,18 +41,9 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManager;
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
import javax.security.auth.callback.Callback;
|
|
||||||
import javax.security.auth.callback.CallbackHandler;
|
|
||||||
import javax.security.auth.callback.PasswordCallback;
|
|
||||||
import javax.xml.namespace.QName;
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||||
import org.jivesoftware.smack.SmackConfiguration.UnknownIqRequestReplyMode;
|
import org.jivesoftware.smack.SmackConfiguration.UnknownIqRequestReplyMode;
|
||||||
import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
|
import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
|
||||||
|
@ -92,6 +68,7 @@ import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
||||||
import org.jivesoftware.smack.filter.IQReplyFilter;
|
import org.jivesoftware.smack.filter.IQReplyFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaIdFilter;
|
import org.jivesoftware.smack.filter.StanzaIdFilter;
|
||||||
|
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler;
|
||||||
import org.jivesoftware.smack.packet.Bind;
|
import org.jivesoftware.smack.packet.Bind;
|
||||||
import org.jivesoftware.smack.packet.ErrorIQ;
|
import org.jivesoftware.smack.packet.ErrorIQ;
|
||||||
|
@ -126,19 +103,14 @@ import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
||||||
import org.jivesoftware.smack.util.Async;
|
import org.jivesoftware.smack.util.Async;
|
||||||
import org.jivesoftware.smack.util.CloseableUtil;
|
|
||||||
import org.jivesoftware.smack.util.CollectionUtil;
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
import org.jivesoftware.smack.util.Consumer;
|
import org.jivesoftware.smack.util.Consumer;
|
||||||
import org.jivesoftware.smack.util.DNSUtil;
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
import org.jivesoftware.smack.util.MultiMap;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smack.util.Predicate;
|
import org.jivesoftware.smack.util.Predicate;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.TLSUtils;
|
|
||||||
import org.jivesoftware.smack.util.dns.SmackDaneProvider;
|
|
||||||
import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
|
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
|
@ -2203,148 +2175,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
outgoingStreamXmlEnvironment = xmlEnvironmentBuilder.build();
|
outgoingStreamXmlEnvironment = xmlEnvironmentBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class SmackTlsContext {
|
protected final SmackTlsContext getSmackTlsContext() {
|
||||||
public final SSLContext sslContext;
|
return config.smackTlsContext;
|
||||||
public final SmackDaneVerifier daneVerifier;
|
|
||||||
|
|
||||||
private SmackTlsContext(SSLContext sslContext, SmackDaneVerifier daneVerifier) {
|
|
||||||
assert sslContext != null;
|
|
||||||
this.sslContext = sslContext;
|
|
||||||
this.daneVerifier = daneVerifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final SmackTlsContext getSmackTlsContext() throws KeyManagementException, NoSuchAlgorithmException,
|
|
||||||
CertificateException, IOException, UnrecoverableKeyException, KeyStoreException, NoSuchProviderException {
|
|
||||||
SmackDaneVerifier daneVerifier = null;
|
|
||||||
|
|
||||||
if (config.getDnssecMode() == DnssecMode.needsDnssecAndDane) {
|
|
||||||
SmackDaneProvider daneProvider = DNSUtil.getDaneProvider();
|
|
||||||
if (daneProvider == null) {
|
|
||||||
throw new UnsupportedOperationException("DANE enabled but no SmackDaneProvider configured");
|
|
||||||
}
|
|
||||||
daneVerifier = daneProvider.newInstance();
|
|
||||||
if (daneVerifier == null) {
|
|
||||||
throw new IllegalStateException("DANE requested but DANE provider did not return a DANE verifier");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SSLContext context = this.config.getCustomSSLContext();
|
|
||||||
KeyStore ks = null;
|
|
||||||
PasswordCallback pcb = null;
|
|
||||||
|
|
||||||
if (context == null) {
|
|
||||||
final String keyStoreType = config.getKeystoreType();
|
|
||||||
final CallbackHandler callbackHandler = config.getCallbackHandler();
|
|
||||||
final String keystorePath = config.getKeystorePath();
|
|
||||||
if ("PKCS11".equals(keyStoreType)) {
|
|
||||||
try {
|
|
||||||
Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class);
|
|
||||||
String pkcs11Config = "name = SmartCard\nlibrary = " + config.getPKCS11Library();
|
|
||||||
ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8));
|
|
||||||
Provider p = (Provider) c.newInstance(config);
|
|
||||||
Security.addProvider(p);
|
|
||||||
ks = KeyStore.getInstance("PKCS11", p);
|
|
||||||
pcb = new PasswordCallback("PKCS11 Password: ", false);
|
|
||||||
callbackHandler.handle(new Callback[] {pcb});
|
|
||||||
ks.load(null, pcb.getPassword());
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Exception", e);
|
|
||||||
ks = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ("Apple".equals(keyStoreType)) {
|
|
||||||
ks = KeyStore.getInstance("KeychainStore", "Apple");
|
|
||||||
ks.load(null, null);
|
|
||||||
// pcb = new PasswordCallback("Apple Keychain",false);
|
|
||||||
// pcb.setPassword(null);
|
|
||||||
}
|
|
||||||
else if (keyStoreType != null) {
|
|
||||||
ks = KeyStore.getInstance(keyStoreType);
|
|
||||||
if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) {
|
|
||||||
try {
|
|
||||||
pcb = new PasswordCallback("Keystore Password: ", false);
|
|
||||||
callbackHandler.handle(new Callback[] { pcb });
|
|
||||||
ks.load(new FileInputStream(keystorePath), pcb.getPassword());
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Exception", e);
|
|
||||||
ks = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
|
||||||
try {
|
|
||||||
// Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous
|
|
||||||
// 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702
|
|
||||||
char[] password = "changeit".toCharArray();
|
|
||||||
try {
|
|
||||||
ks.load(stream, password);
|
|
||||||
} finally {
|
|
||||||
CloseableUtil.maybeClose(stream);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e);
|
|
||||||
|
|
||||||
ks = KeyStore.getInstance("jks");
|
|
||||||
// Open the stream again, so that we read it from the beginning.
|
|
||||||
stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
|
||||||
try {
|
|
||||||
ks.load(stream, null);
|
|
||||||
} finally {
|
|
||||||
CloseableUtil.maybeClose(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyManager[] kms = null;
|
|
||||||
|
|
||||||
if (ks != null) {
|
|
||||||
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
|
||||||
KeyManagerFactory kmf = null;
|
|
||||||
try {
|
|
||||||
kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
|
|
||||||
}
|
|
||||||
catch (NoSuchAlgorithmException e) {
|
|
||||||
LOGGER.log(Level.FINE, "Could get the default KeyManagerFactory for the '"
|
|
||||||
+ keyManagerFactoryAlgorithm + "' algorithm", e);
|
|
||||||
}
|
|
||||||
if (kmf != null) {
|
|
||||||
try {
|
|
||||||
if (pcb == null) {
|
|
||||||
kmf.init(ks, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
kmf.init(ks, pcb.getPassword());
|
|
||||||
pcb.clearPassword();
|
|
||||||
}
|
|
||||||
kms = kmf.getKeyManagers();
|
|
||||||
}
|
|
||||||
catch (NullPointerException npe) {
|
|
||||||
LOGGER.log(Level.WARNING, "NullPointerException", npe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user didn't specify a SSLContext, use the default one
|
|
||||||
context = SSLContext.getInstance("TLS");
|
|
||||||
|
|
||||||
final SecureRandom secureRandom = new java.security.SecureRandom();
|
|
||||||
X509TrustManager trustManager = config.getCustomX509TrustManager();
|
|
||||||
if (trustManager == null) {
|
|
||||||
trustManager = TLSUtils.getDefaultX509TrustManager(ks);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (daneVerifier != null) {
|
|
||||||
// User requested DANE verification.
|
|
||||||
daneVerifier.init(context, kms, trustManager, secureRandom);
|
|
||||||
} else {
|
|
||||||
TrustManager[] customTrustManagers = new TrustManager[] { trustManager };
|
|
||||||
context.init(kms, customTrustManagers, secureRandom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SmackTlsContext(context, daneVerifier);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,24 @@
|
||||||
|
|
||||||
package org.jivesoftware.smack;
|
package org.jivesoftware.smack;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -31,21 +46,34 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.KeyManager;
|
||||||
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import javax.security.auth.callback.Callback;
|
||||||
import javax.security.auth.callback.CallbackHandler;
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
|
import javax.security.auth.callback.PasswordCallback;
|
||||||
|
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.datatypes.UInt16;
|
import org.jivesoftware.smack.datatypes.UInt16;
|
||||||
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
||||||
|
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||||
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
|
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
|
||||||
import org.jivesoftware.smack.packet.id.StanzaIdSource;
|
import org.jivesoftware.smack.packet.id.StanzaIdSource;
|
||||||
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
|
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
|
||||||
import org.jivesoftware.smack.proxy.ProxyInfo;
|
import org.jivesoftware.smack.proxy.ProxyInfo;
|
||||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||||
|
import org.jivesoftware.smack.util.CloseableUtil;
|
||||||
import org.jivesoftware.smack.util.CollectionUtil;
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
|
import org.jivesoftware.smack.util.DNSUtil;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
import org.jivesoftware.smack.util.SslContextFactory;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smack.util.TLSUtils;
|
||||||
|
import org.jivesoftware.smack.util.dns.SmackDaneProvider;
|
||||||
|
import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
|
||||||
|
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
@ -104,11 +132,6 @@ public abstract class ConnectionConfiguration {
|
||||||
protected final DnsName host;
|
protected final DnsName host;
|
||||||
protected final UInt16 port;
|
protected final UInt16 port;
|
||||||
|
|
||||||
private final String keystorePath;
|
|
||||||
private final String keystoreType;
|
|
||||||
private final String pkcs11Library;
|
|
||||||
private final SSLContext customSSLContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to get information from the user
|
* Used to get information from the user
|
||||||
*/
|
*/
|
||||||
|
@ -138,9 +161,9 @@ public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
private final SecurityMode securityMode;
|
private final SecurityMode securityMode;
|
||||||
|
|
||||||
private final DnssecMode dnssecMode;
|
final SmackTlsContext smackTlsContext;
|
||||||
|
|
||||||
private final X509TrustManager customX509TrustManager;
|
private final DnssecMode dnssecMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -166,6 +189,17 @@ public abstract class ConnectionConfiguration {
|
||||||
private final StanzaIdSourceFactory stanzaIdSourceFactory;
|
private final StanzaIdSourceFactory stanzaIdSourceFactory;
|
||||||
|
|
||||||
protected ConnectionConfiguration(Builder<?, ?> builder) {
|
protected ConnectionConfiguration(Builder<?, ?> builder) {
|
||||||
|
try {
|
||||||
|
smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory,
|
||||||
|
builder.customX509TrustManager, builder.keystoreType, builder.keystorePath,
|
||||||
|
builder.callbackHandler, builder.pkcs11Library);
|
||||||
|
} catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException
|
||||||
|
| KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException
|
||||||
|
| SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException
|
||||||
|
| IllegalArgumentException | InvocationTargetException | UnsupportedCallbackException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
|
||||||
authzid = builder.authzid;
|
authzid = builder.authzid;
|
||||||
username = builder.username;
|
username = builder.username;
|
||||||
password = builder.password;
|
password = builder.password;
|
||||||
|
@ -202,13 +236,7 @@ public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
dnssecMode = builder.dnssecMode;
|
dnssecMode = builder.dnssecMode;
|
||||||
|
|
||||||
customX509TrustManager = builder.customX509TrustManager;
|
|
||||||
|
|
||||||
securityMode = builder.securityMode;
|
securityMode = builder.securityMode;
|
||||||
keystoreType = builder.keystoreType;
|
|
||||||
keystorePath = builder.keystorePath;
|
|
||||||
pkcs11Library = builder.pkcs11Library;
|
|
||||||
customSSLContext = builder.customSSLContext;
|
|
||||||
enabledSSLProtocols = builder.enabledSSLProtocols;
|
enabledSSLProtocols = builder.enabledSSLProtocols;
|
||||||
enabledSSLCiphers = builder.enabledSSLCiphers;
|
enabledSSLCiphers = builder.enabledSSLCiphers;
|
||||||
hostnameVerifier = builder.hostnameVerifier;
|
hostnameVerifier = builder.hostnameVerifier;
|
||||||
|
@ -223,11 +251,115 @@ public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
// If the enabledSaslmechanisms are set, then they must not be empty
|
// If the enabledSaslmechanisms are set, then they must not be empty
|
||||||
assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty();
|
assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty();
|
||||||
|
|
||||||
if (dnssecMode != DnssecMode.disabled && customSSLContext != null) {
|
|
||||||
throw new IllegalStateException("You can not use a custom SSL context with DNSSEC enabled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
|
||||||
|
X509TrustManager trustManager, String keystoreType, String keystorePath,
|
||||||
|
CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException,
|
||||||
|
CertificateException, IOException, KeyStoreException, NoSuchProviderException,
|
||||||
|
UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException,
|
||||||
|
NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException,
|
||||||
|
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||||
|
final SSLContext context;
|
||||||
|
if (sslContextFactory != null) {
|
||||||
|
context = sslContextFactory.createSslContext();
|
||||||
|
} else {
|
||||||
|
// If the user didn't specify a SslContextFactory, use the default one
|
||||||
|
context = SSLContext.getInstance("TLS");
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyStore ks = null;
|
||||||
|
PasswordCallback pcb = null;
|
||||||
|
KeyManager[] kms = null;
|
||||||
|
|
||||||
|
if ("PKCS11".equals(keystoreType)) {
|
||||||
|
Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class);
|
||||||
|
String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library;
|
||||||
|
ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8));
|
||||||
|
Provider p = (Provider) c.newInstance(config);
|
||||||
|
Security.addProvider(p);
|
||||||
|
ks = KeyStore.getInstance("PKCS11", p);
|
||||||
|
pcb = new PasswordCallback("PKCS11 Password: ", false);
|
||||||
|
callbackHandler.handle(new Callback[] { pcb });
|
||||||
|
ks.load(null, pcb.getPassword());
|
||||||
|
} else if ("Apple".equals(keystoreType)) {
|
||||||
|
ks = KeyStore.getInstance("KeychainStore", "Apple");
|
||||||
|
ks.load(null, null);
|
||||||
|
// pcb = new PasswordCallback("Apple Keychain",false);
|
||||||
|
// pcb.setPassword(null);
|
||||||
|
} else if (keystoreType != null) {
|
||||||
|
ks = KeyStore.getInstance(keystoreType);
|
||||||
|
if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) {
|
||||||
|
pcb = new PasswordCallback("Keystore Password: ", false);
|
||||||
|
callbackHandler.handle(new Callback[] { pcb });
|
||||||
|
ks.load(new FileInputStream(keystorePath), pcb.getPassword());
|
||||||
|
} else {
|
||||||
|
InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
||||||
|
try {
|
||||||
|
// Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous
|
||||||
|
// 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702
|
||||||
|
char[] password = "changeit".toCharArray();
|
||||||
|
try {
|
||||||
|
ks.load(stream, password);
|
||||||
|
} finally {
|
||||||
|
CloseableUtil.maybeClose(stream);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e);
|
||||||
|
|
||||||
|
ks = KeyStore.getInstance("jks");
|
||||||
|
// Open the stream again, so that we read it from the beginning.
|
||||||
|
stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
||||||
|
try {
|
||||||
|
ks.load(stream, null);
|
||||||
|
} finally {
|
||||||
|
CloseableUtil.maybeClose(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ks != null) {
|
||||||
|
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
|
||||||
|
if (kmf != null) {
|
||||||
|
if (pcb == null) {
|
||||||
|
kmf.init(ks, null);
|
||||||
|
} else {
|
||||||
|
kmf.init(ks, pcb.getPassword());
|
||||||
|
pcb.clearPassword();
|
||||||
|
}
|
||||||
|
kms = kmf.getKeyManagers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SmackDaneVerifier daneVerifier = null;
|
||||||
|
if (dnssecMode == DnssecMode.needsDnssecAndDane) {
|
||||||
|
SmackDaneProvider daneProvider = DNSUtil.getDaneProvider();
|
||||||
|
if (daneProvider == null) {
|
||||||
|
throw new UnsupportedOperationException("DANE enabled but no SmackDaneProvider configured");
|
||||||
|
}
|
||||||
|
daneVerifier = daneProvider.newInstance();
|
||||||
|
if (daneVerifier == null) {
|
||||||
|
throw new IllegalStateException("DANE requested but DANE provider did not return a DANE verifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
// User requested DANE verification.
|
||||||
|
daneVerifier.init(context, kms, trustManager, null);
|
||||||
|
} else {
|
||||||
|
final TrustManager[] trustManagers;
|
||||||
|
if (trustManager != null) {
|
||||||
|
trustManagers = new TrustManager[] { trustManager };
|
||||||
|
} else {
|
||||||
|
// Ensure trustManagers is null in case there was no explicit trust manager provided, so that the
|
||||||
|
// default one is used.
|
||||||
|
trustManagers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.init(kms, trustManagers, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SmackTlsContext(context, daneVerifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DnsName getHost() {
|
public DnsName getHost() {
|
||||||
|
@ -287,50 +419,6 @@ public abstract class ConnectionConfiguration {
|
||||||
return dnssecMode;
|
return dnssecMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public X509TrustManager getCustomX509TrustManager() {
|
|
||||||
return customX509TrustManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retuns the path to the keystore file. The key store file contains the
|
|
||||||
* certificates that may be used to authenticate the client to the server,
|
|
||||||
* in the event the server requests or requires it.
|
|
||||||
*
|
|
||||||
* @return the path to the keystore file.
|
|
||||||
*/
|
|
||||||
public String getKeystorePath() {
|
|
||||||
return keystorePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the keystore type, or <code>null</code> if it's not set.
|
|
||||||
*
|
|
||||||
* @return the keystore type.
|
|
||||||
*/
|
|
||||||
public String getKeystoreType() {
|
|
||||||
return keystoreType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the PKCS11 library file location, needed when the
|
|
||||||
* Keystore type is PKCS11.
|
|
||||||
*
|
|
||||||
* @return the path to the PKCS11 library file
|
|
||||||
*/
|
|
||||||
public String getPKCS11Library() {
|
|
||||||
return pkcs11Library;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the custom SSLContext previously set with {@link ConnectionConfiguration.Builder#setCustomSSLContext(SSLContext)} for
|
|
||||||
* SSL sockets. This is null by default.
|
|
||||||
*
|
|
||||||
* @return the custom SSLContext or null.
|
|
||||||
*/
|
|
||||||
public SSLContext getCustomSSLContext() {
|
|
||||||
return this.customSSLContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the enabled SSL/TLS protocols.
|
* Return the enabled SSL/TLS protocols.
|
||||||
*
|
*
|
||||||
|
@ -602,10 +690,10 @@ public abstract class ConnectionConfiguration {
|
||||||
public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
|
public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
|
||||||
private SecurityMode securityMode = SecurityMode.required;
|
private SecurityMode securityMode = SecurityMode.required;
|
||||||
private DnssecMode dnssecMode = DnssecMode.disabled;
|
private DnssecMode dnssecMode = DnssecMode.disabled;
|
||||||
private String keystorePath = System.getProperty("javax.net.ssl.keyStore");
|
private String keystorePath;
|
||||||
private String keystoreType = KeyStore.getDefaultType();
|
private String keystoreType;
|
||||||
private String pkcs11Library = "pkcs11.config";
|
private String pkcs11Library = "pkcs11.config";
|
||||||
private SSLContext customSSLContext;
|
private SslContextFactory sslContextFactory;
|
||||||
private String[] enabledSSLProtocols;
|
private String[] enabledSSLProtocols;
|
||||||
private String[] enabledSSLCiphers;
|
private String[] enabledSSLCiphers;
|
||||||
private HostnameVerifier hostnameVerifier;
|
private HostnameVerifier hostnameVerifier;
|
||||||
|
@ -929,9 +1017,28 @@ public abstract class ConnectionConfiguration {
|
||||||
*
|
*
|
||||||
* @param context the custom SSLContext for new sockets.
|
* @param context the custom SSLContext for new sockets.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
|
* @deprecated use {@link #setSslContextFactory(SslContextFactory)} instead}.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove in Smack 4.5.
|
||||||
|
@Deprecated
|
||||||
public B setCustomSSLContext(SSLContext context) {
|
public B setCustomSSLContext(SSLContext context) {
|
||||||
this.customSSLContext = Objects.requireNonNull(context, "The SSLContext must not be null");
|
return setSslContextFactory(() -> {
|
||||||
|
return context;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom SSLContext for creating SSL sockets.
|
||||||
|
* <p>
|
||||||
|
* For more information on how to create a SSLContext see <a href=
|
||||||
|
* "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager"
|
||||||
|
* >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a>
|
||||||
|
*
|
||||||
|
* @param sslContextFactory the custom SSLContext for new sockets.
|
||||||
|
* @return a reference to this builder.
|
||||||
|
*/
|
||||||
|
public B setSslContextFactory(SslContextFactory sslContextFactory) {
|
||||||
|
this.sslContextFactory = Objects.requireNonNull(sslContextFactory, "The provided SslContextFactory must not be null");
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1172,5 +1279,4 @@ public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
protected abstract B getThis();
|
protected abstract B getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,6 @@
|
||||||
package org.jivesoftware.smack.c2s;
|
package org.jivesoftware.smack.c2s;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.UnrecoverableKeyException;
|
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -66,6 +61,7 @@ import org.jivesoftware.smack.fsm.StateMachineException;
|
||||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||||
import org.jivesoftware.smack.fsm.StateTransitionResult.AttemptResult;
|
import org.jivesoftware.smack.fsm.StateTransitionResult.AttemptResult;
|
||||||
import org.jivesoftware.smack.internal.AbstractStats;
|
import org.jivesoftware.smack.internal.AbstractStats;
|
||||||
|
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Nonza;
|
import org.jivesoftware.smack.packet.Nonza;
|
||||||
|
@ -186,9 +182,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SmackTlsContext getSmackTlsContext()
|
public SmackTlsContext getSmackTlsContext() {
|
||||||
throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException,
|
|
||||||
UnrecoverableKeyException, KeyStoreException, NoSuchProviderException {
|
|
||||||
return ModularXmppClientToServerConnection.this.getSmackTlsContext();
|
return ModularXmppClientToServerConnection.this.getSmackTlsContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,20 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.c2s.internal;
|
package org.jivesoftware.smack.c2s.internal;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.channels.SelectableChannel;
|
import java.nio.channels.SelectableChannel;
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.UnrecoverableKeyException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext;
|
|
||||||
import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException;
|
import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
|
@ -41,6 +33,7 @@ import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||||
import org.jivesoftware.smack.c2s.XmppClientToServerTransport;
|
import org.jivesoftware.smack.c2s.XmppClientToServerTransport;
|
||||||
import org.jivesoftware.smack.debugger.SmackDebugger;
|
import org.jivesoftware.smack.debugger.SmackDebugger;
|
||||||
import org.jivesoftware.smack.fsm.ConnectionStateEvent;
|
import org.jivesoftware.smack.fsm.ConnectionStateEvent;
|
||||||
|
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||||
import org.jivesoftware.smack.packet.Nonza;
|
import org.jivesoftware.smack.packet.Nonza;
|
||||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
|
@ -107,9 +100,7 @@ public abstract class ModularXmppClientToServerConnectionInternal {
|
||||||
public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException,
|
public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException,
|
||||||
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException;
|
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException;
|
||||||
|
|
||||||
public abstract SmackTlsContext getSmackTlsContext()
|
public abstract SmackTlsContext getSmackTlsContext();
|
||||||
throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException,
|
|
||||||
UnrecoverableKeyException, KeyStoreException, NoSuchProviderException;
|
|
||||||
|
|
||||||
public abstract <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(Nonza nonza,
|
public abstract <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(Nonza nonza,
|
||||||
Class<SN> successNonzaClass, Class<FN> failedNonzaClass)
|
Class<SN> successNonzaClass, Class<FN> failedNonzaClass)
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2020 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smack.internal;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
|
||||||
|
|
||||||
|
public final class SmackTlsContext {
|
||||||
|
public final SSLContext sslContext;
|
||||||
|
public final SmackDaneVerifier daneVerifier;
|
||||||
|
|
||||||
|
public SmackTlsContext(SSLContext sslContext, SmackDaneVerifier daneVerifier) {
|
||||||
|
assert sslContext != null;
|
||||||
|
this.sslContext = sslContext;
|
||||||
|
this.daneVerifier = daneVerifier;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2020 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smack.util;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
|
public interface SslContextFactory {
|
||||||
|
|
||||||
|
SSLContext createSslContext();
|
||||||
|
|
||||||
|
}
|
|
@ -22,12 +22,8 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
@ -38,12 +34,9 @@ import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||||
|
@ -131,14 +124,10 @@ public class TLSUtils {
|
||||||
*
|
*
|
||||||
* @param builder a connection configuration builder.
|
* @param builder a connection configuration builder.
|
||||||
* @param <B> Type of the ConnectionConfiguration builder.
|
* @param <B> Type of the ConnectionConfiguration builder.
|
||||||
* @throws NoSuchAlgorithmException if no such algorithm is available.
|
|
||||||
* @throws KeyManagementException if there was a key mangement error.
|
|
||||||
* @return the given builder.
|
* @return the given builder.
|
||||||
*/
|
*/
|
||||||
public static <B extends ConnectionConfiguration.Builder<B, ?>> B acceptAllCertificates(B builder) throws NoSuchAlgorithmException, KeyManagementException {
|
public static <B extends ConnectionConfiguration.Builder<B, ?>> B acceptAllCertificates(B builder) {
|
||||||
SSLContext context = SSLContext.getInstance(TLS);
|
builder.setCustomX509TrustManager(new AcceptAllTrustManager());
|
||||||
context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom());
|
|
||||||
builder.setCustomSSLContext(context);
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,24 +256,6 @@ public class TLSUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static X509TrustManager getDefaultX509TrustManager(KeyStore keyStore) {
|
|
||||||
String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
|
||||||
TrustManagerFactory trustManagerFactory;
|
|
||||||
try {
|
|
||||||
trustManagerFactory = TrustManagerFactory.getInstance(defaultAlgorithm);
|
|
||||||
trustManagerFactory.init(keyStore);
|
|
||||||
} catch (NoSuchAlgorithmException | KeyStoreException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
|
|
||||||
if (trustManager instanceof X509TrustManager) {
|
|
||||||
return (X509TrustManager) trustManager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new AssertionError("No trust manager for the default algorithm " + defaultAlgorithm + " found");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final File DEFAULT_TRUSTSTORE_PATH;
|
private static final File DEFAULT_TRUSTSTORE_PATH;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -37,7 +37,6 @@ import javax.net.ssl.HttpsURLConnection;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
|
||||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
import org.jivesoftware.smack.ConnectionListener;
|
import org.jivesoftware.smack.ConnectionListener;
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
@ -428,11 +427,6 @@ public final class HttpFileUploadManager extends Manager {
|
||||||
this.tlsSocketFactory = tlsContext.getSocketFactory();
|
this.tlsSocketFactory = tlsContext.getSocketFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void useTlsSettingsFrom(ConnectionConfiguration connectionConfiguration) {
|
|
||||||
SSLContext sslContext = connectionConfiguration.getCustomSSLContext();
|
|
||||||
setTlsContext(sslContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void upload(InputStream iStream, long fileSize, Slot slot, UploadProgressListener listener) throws IOException {
|
private void upload(InputStream iStream, long fileSize, Slot slot, UploadProgressListener listener) throws IOException {
|
||||||
final URL putUrl = slot.getPutUrl();
|
final URL putUrl = slot.getPutUrl();
|
||||||
|
|
||||||
|
|
|
@ -84,9 +84,9 @@ public abstract class AbstractSmackIntTest {
|
||||||
|
|
||||||
protected HttpURLConnection getHttpUrlConnectionFor(URL url) throws IOException {
|
protected HttpURLConnection getHttpUrlConnectionFor(URL url) throws IOException {
|
||||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
if (sinttestConfiguration.tlsContext != null && urlConnection instanceof HttpsURLConnection) {
|
if (sinttestConfiguration.sslContextFactory != null && urlConnection instanceof HttpsURLConnection) {
|
||||||
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
|
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
|
||||||
httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.tlsContext.getSocketFactory());
|
httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.sslContextFactory.createSslContext().getSocketFactory());
|
||||||
}
|
}
|
||||||
return urlConnection;
|
return urlConnection;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.jivesoftware.smack.debugger.ConsoleDebugger;
|
||||||
import org.jivesoftware.smack.util.Function;
|
import org.jivesoftware.smack.util.Function;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
|
import org.jivesoftware.smack.util.SslContextFactory;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.debugger.EnhancedDebugger;
|
import org.jivesoftware.smackx.debugger.EnhancedDebugger;
|
||||||
|
@ -72,7 +73,7 @@ public final class Configuration {
|
||||||
|
|
||||||
public final String serviceTlsPin;
|
public final String serviceTlsPin;
|
||||||
|
|
||||||
public final SSLContext tlsContext;
|
public final SslContextFactory sslContextFactory;
|
||||||
|
|
||||||
public final SecurityMode securityMode;
|
public final SecurityMode securityMode;
|
||||||
|
|
||||||
|
@ -121,9 +122,10 @@ public final class Configuration {
|
||||||
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
|
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
|
||||||
serviceTlsPin = builder.serviceTlsPin;
|
serviceTlsPin = builder.serviceTlsPin;
|
||||||
if (serviceTlsPin != null) {
|
if (serviceTlsPin != null) {
|
||||||
tlsContext = Java7Pinning.forPin(serviceTlsPin);
|
SSLContext sslContext = Java7Pinning.forPin(serviceTlsPin);
|
||||||
|
sslContextFactory = () -> sslContext;
|
||||||
} else {
|
} else {
|
||||||
tlsContext = null;
|
sslContextFactory = null;
|
||||||
}
|
}
|
||||||
securityMode = builder.securityMode;
|
securityMode = builder.securityMode;
|
||||||
if (builder.replyTimeout > 0) {
|
if (builder.replyTimeout > 0) {
|
||||||
|
@ -168,8 +170,8 @@ public final class Configuration {
|
||||||
this.testPackages = builder.testPackages;
|
this.testPackages = builder.testPackages;
|
||||||
|
|
||||||
this.configurationApplier = b -> {
|
this.configurationApplier = b -> {
|
||||||
if (tlsContext != null) {
|
if (sslContextFactory != null) {
|
||||||
b.setCustomSSLContext(tlsContext);
|
b.setSslContextFactory(sslContextFactory);
|
||||||
}
|
}
|
||||||
b.setSecurityMode(securityMode);
|
b.setSecurityMode(securityMode);
|
||||||
b.setXmppDomain(service);
|
b.setXmppDomain(service);
|
||||||
|
|
|
@ -57,7 +57,9 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest
|
||||||
+ " does not accept files of size " + FILE_SIZE
|
+ " does not accept files of size " + FILE_SIZE
|
||||||
+ ". It only accepts files with a maximum size of " + uploadService.getMaxFileSize());
|
+ ". It only accepts files with a maximum size of " + uploadService.getMaxFileSize());
|
||||||
}
|
}
|
||||||
hfumOne.setTlsContext(environment.configuration.tlsContext);
|
if (environment.configuration.sslContextFactory != null) {
|
||||||
|
hfumOne.setTlsContext(environment.configuration.sslContextFactory.createSslContext());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class TlsTest {
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(tlsPin)) {
|
if (StringUtils.isNotEmpty(tlsPin)) {
|
||||||
SSLContext sslContext = Java7Pinning.forPin(tlsPin);
|
SSLContext sslContext = Java7Pinning.forPin(tlsPin);
|
||||||
builder.setCustomSSLContext(sslContext);
|
builder.setSslContextFactory(() -> sslContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ import org.jivesoftware.smack.compress.packet.Compressed;
|
||||||
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
|
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
|
||||||
import org.jivesoftware.smack.datatypes.UInt16;
|
import org.jivesoftware.smack.datatypes.UInt16;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
|
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||||
import org.jivesoftware.smack.packet.Element;
|
import org.jivesoftware.smack.packet.Element;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
|
@ -24,11 +24,6 @@ import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.channels.SelectableChannel;
|
import java.nio.channels.SelectableChannel;
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.UnrecoverableKeyException;
|
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -50,7 +45,6 @@ import javax.net.ssl.SSLEngineResult;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext;
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||||
|
@ -75,6 +69,7 @@ import org.jivesoftware.smack.debugger.SmackDebugger;
|
||||||
import org.jivesoftware.smack.fsm.State;
|
import org.jivesoftware.smack.fsm.State;
|
||||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||||
|
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.StartTls;
|
import org.jivesoftware.smack.packet.StartTls;
|
||||||
import org.jivesoftware.smack.packet.StreamOpen;
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
@ -867,13 +862,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
|
||||||
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException {
|
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException {
|
||||||
connectionInternal.sendAndWaitForResponse(StartTls.INSTANCE, TlsProceed.class, TlsFailure.class);
|
connectionInternal.sendAndWaitForResponse(StartTls.INSTANCE, TlsProceed.class, TlsFailure.class);
|
||||||
|
|
||||||
SmackTlsContext smackTlsContext;
|
SmackTlsContext smackTlsContext = connectionInternal.getSmackTlsContext();
|
||||||
try {
|
|
||||||
smackTlsContext = connectionInternal.getSmackTlsContext();
|
|
||||||
} catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException
|
|
||||||
| CertificateException | KeyStoreException | NoSuchProviderException e) {
|
|
||||||
throw new SmackWrappedException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsState = new TlsState(smackTlsContext);
|
tlsState = new TlsState(smackTlsContext);
|
||||||
connectionInternal.addXmppInputOutputFilter(tlsState);
|
connectionInternal.addXmppInputOutputFilter(tlsState);
|
||||||
|
|
Loading…
Reference in a new issue