[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:
Florian Schmaus 2020-05-25 14:47:36 +02:00
parent 7156849c77
commit f5448c5faa
14 changed files with 252 additions and 314 deletions

View File

@ -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);
} }
} }

View File

@ -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) { private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
throw new IllegalStateException("You can not use a custom SSL context with DNSSEC enabled"); 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();
} }
} }

View File

@ -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();
} }

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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 {

View File

@ -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();

View File

@ -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;
} }

View File

@ -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);

View File

@ -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

View File

@ -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);
} }

View File

@ -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;

View File

@ -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);