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;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
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.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -56,18 +41,9 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
import java.util.logging.Level;
|
||||
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.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 org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
|
||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||
import org.jivesoftware.smack.SmackConfiguration.UnknownIqRequestReplyMode;
|
||||
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.StanzaFilter;
|
||||
import org.jivesoftware.smack.filter.StanzaIdFilter;
|
||||
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler;
|
||||
import org.jivesoftware.smack.packet.Bind;
|
||||
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.packet.SaslNonza;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
import org.jivesoftware.smack.util.CloseableUtil;
|
||||
import org.jivesoftware.smack.util.CollectionUtil;
|
||||
import org.jivesoftware.smack.util.Consumer;
|
||||
import org.jivesoftware.smack.util.DNSUtil;
|
||||
import org.jivesoftware.smack.util.MultiMap;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
import org.jivesoftware.smack.util.Predicate;
|
||||
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.XmlPullParserException;
|
||||
|
||||
|
@ -2203,148 +2175,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
outgoingStreamXmlEnvironment = xmlEnvironmentBuilder.build();
|
||||
}
|
||||
|
||||
public static final class SmackTlsContext {
|
||||
public final SSLContext sslContext;
|
||||
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);
|
||||
protected final SmackTlsContext getSmackTlsContext() {
|
||||
return config.smackTlsContext;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,24 @@
|
|||
|
||||
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.UnknownHostException;
|
||||
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.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -31,21 +46,34 @@ import java.util.logging.Logger;
|
|||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
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.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import org.jivesoftware.smack.datatypes.UInt16;
|
||||
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.StanzaIdSource;
|
||||
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
|
||||
import org.jivesoftware.smack.proxy.ProxyInfo;
|
||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||
import org.jivesoftware.smack.util.CloseableUtil;
|
||||
import org.jivesoftware.smack.util.CollectionUtil;
|
||||
import org.jivesoftware.smack.util.DNSUtil;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.SslContextFactory;
|
||||
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.EntityBareJid;
|
||||
|
@ -104,11 +132,6 @@ public abstract class ConnectionConfiguration {
|
|||
protected final DnsName host;
|
||||
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
|
||||
*/
|
||||
|
@ -138,9 +161,9 @@ public abstract class ConnectionConfiguration {
|
|||
|
||||
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;
|
||||
|
||||
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;
|
||||
username = builder.username;
|
||||
password = builder.password;
|
||||
|
@ -202,13 +236,7 @@ public abstract class ConnectionConfiguration {
|
|||
|
||||
dnssecMode = builder.dnssecMode;
|
||||
|
||||
customX509TrustManager = builder.customX509TrustManager;
|
||||
|
||||
securityMode = builder.securityMode;
|
||||
keystoreType = builder.keystoreType;
|
||||
keystorePath = builder.keystorePath;
|
||||
pkcs11Library = builder.pkcs11Library;
|
||||
customSSLContext = builder.customSSLContext;
|
||||
enabledSSLProtocols = builder.enabledSSLProtocols;
|
||||
enabledSSLCiphers = builder.enabledSSLCiphers;
|
||||
hostnameVerifier = builder.hostnameVerifier;
|
||||
|
@ -223,11 +251,115 @@ public abstract class ConnectionConfiguration {
|
|||
|
||||
// If the enabledSaslmechanisms are set, then they must not be empty
|
||||
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() {
|
||||
|
@ -287,50 +419,6 @@ public abstract class ConnectionConfiguration {
|
|||
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.
|
||||
*
|
||||
|
@ -602,10 +690,10 @@ public abstract class ConnectionConfiguration {
|
|||
public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
|
||||
private SecurityMode securityMode = SecurityMode.required;
|
||||
private DnssecMode dnssecMode = DnssecMode.disabled;
|
||||
private String keystorePath = System.getProperty("javax.net.ssl.keyStore");
|
||||
private String keystoreType = KeyStore.getDefaultType();
|
||||
private String keystorePath;
|
||||
private String keystoreType;
|
||||
private String pkcs11Library = "pkcs11.config";
|
||||
private SSLContext customSSLContext;
|
||||
private SslContextFactory sslContextFactory;
|
||||
private String[] enabledSSLProtocols;
|
||||
private String[] enabledSSLCiphers;
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
|
@ -929,9 +1017,28 @@ public abstract class ConnectionConfiguration {
|
|||
*
|
||||
* @param context the custom SSLContext for new sockets.
|
||||
* @return a reference to this builder.
|
||||
* @deprecated use {@link #setSslContextFactory(SslContextFactory)} instead}.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.5.
|
||||
@Deprecated
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1172,5 +1279,4 @@ public abstract class ConnectionConfiguration {
|
|||
|
||||
protected abstract B getThis();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
package org.jivesoftware.smack.c2s;
|
||||
|
||||
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.util.ArrayList;
|
||||
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.AttemptResult;
|
||||
import org.jivesoftware.smack.internal.AbstractStats;
|
||||
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Nonza;
|
||||
|
@ -186,9 +182,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
|
|||
}
|
||||
|
||||
@Override
|
||||
public SmackTlsContext getSmackTlsContext()
|
||||
throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException,
|
||||
UnrecoverableKeyException, KeyStoreException, NoSuchProviderException {
|
||||
public SmackTlsContext getSmackTlsContext() {
|
||||
return ModularXmppClientToServerConnection.this.getSmackTlsContext();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,20 +16,12 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.c2s.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
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.Queue;
|
||||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
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.debugger.SmackDebugger;
|
||||
import org.jivesoftware.smack.fsm.ConnectionStateEvent;
|
||||
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||
import org.jivesoftware.smack.packet.Nonza;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
|
@ -107,9 +100,7 @@ public abstract class ModularXmppClientToServerConnectionInternal {
|
|||
public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException,
|
||||
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException;
|
||||
|
||||
public abstract SmackTlsContext getSmackTlsContext()
|
||||
throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException,
|
||||
UnrecoverableKeyException, KeyStoreException, NoSuchProviderException;
|
||||
public abstract SmackTlsContext getSmackTlsContext();
|
||||
|
||||
public abstract <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(Nonza nonza,
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
@ -38,12 +34,9 @@ import java.util.Set;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
|
@ -131,14 +124,10 @@ public class TLSUtils {
|
|||
*
|
||||
* @param builder a connection configuration 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.
|
||||
*/
|
||||
public static <B extends ConnectionConfiguration.Builder<B, ?>> B acceptAllCertificates(B builder) throws NoSuchAlgorithmException, KeyManagementException {
|
||||
SSLContext context = SSLContext.getInstance(TLS);
|
||||
context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom());
|
||||
builder.setCustomSSLContext(context);
|
||||
public static <B extends ConnectionConfiguration.Builder<B, ?>> B acceptAllCertificates(B builder) {
|
||||
builder.setCustomX509TrustManager(new AcceptAllTrustManager());
|
||||
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;
|
||||
|
||||
static {
|
||||
|
|
|
@ -37,7 +37,6 @@ import javax.net.ssl.HttpsURLConnection;
|
|||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||
import org.jivesoftware.smack.ConnectionListener;
|
||||
import org.jivesoftware.smack.Manager;
|
||||
|
@ -428,11 +427,6 @@ public final class HttpFileUploadManager extends Manager {
|
|||
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 {
|
||||
final URL putUrl = slot.getPutUrl();
|
||||
|
||||
|
|
|
@ -84,9 +84,9 @@ public abstract class AbstractSmackIntTest {
|
|||
|
||||
protected HttpURLConnection getHttpUrlConnectionFor(URL url) throws IOException {
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||
if (sinttestConfiguration.tlsContext != null && urlConnection instanceof HttpsURLConnection) {
|
||||
if (sinttestConfiguration.sslContextFactory != null && urlConnection instanceof HttpsURLConnection) {
|
||||
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
|
||||
httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.tlsContext.getSocketFactory());
|
||||
httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.sslContextFactory.createSslContext().getSocketFactory());
|
||||
}
|
||||
return urlConnection;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.jivesoftware.smack.debugger.ConsoleDebugger;
|
|||
import org.jivesoftware.smack.util.Function;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
import org.jivesoftware.smack.util.SslContextFactory;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import org.jivesoftware.smackx.debugger.EnhancedDebugger;
|
||||
|
@ -72,7 +73,7 @@ public final class Configuration {
|
|||
|
||||
public final String serviceTlsPin;
|
||||
|
||||
public final SSLContext tlsContext;
|
||||
public final SslContextFactory sslContextFactory;
|
||||
|
||||
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'.");
|
||||
serviceTlsPin = builder.serviceTlsPin;
|
||||
if (serviceTlsPin != null) {
|
||||
tlsContext = Java7Pinning.forPin(serviceTlsPin);
|
||||
SSLContext sslContext = Java7Pinning.forPin(serviceTlsPin);
|
||||
sslContextFactory = () -> sslContext;
|
||||
} else {
|
||||
tlsContext = null;
|
||||
sslContextFactory = null;
|
||||
}
|
||||
securityMode = builder.securityMode;
|
||||
if (builder.replyTimeout > 0) {
|
||||
|
@ -168,8 +170,8 @@ public final class Configuration {
|
|||
this.testPackages = builder.testPackages;
|
||||
|
||||
this.configurationApplier = b -> {
|
||||
if (tlsContext != null) {
|
||||
b.setCustomSSLContext(tlsContext);
|
||||
if (sslContextFactory != null) {
|
||||
b.setSslContextFactory(sslContextFactory);
|
||||
}
|
||||
b.setSecurityMode(securityMode);
|
||||
b.setXmppDomain(service);
|
||||
|
|
|
@ -57,7 +57,9 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest
|
|||
+ " does not accept files of size " + FILE_SIZE
|
||||
+ ". 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
|
||||
|
|
|
@ -89,7 +89,7 @@ public class TlsTest {
|
|||
|
||||
if (StringUtils.isNotEmpty(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.datatypes.UInt16;
|
||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||
import org.jivesoftware.smack.packet.Element;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
|
|
@ -24,11 +24,6 @@ import java.nio.channels.ClosedChannelException;
|
|||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -50,7 +45,6 @@ import javax.net.ssl.SSLEngineResult;
|
|||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext;
|
||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
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.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||
import org.jivesoftware.smack.internal.SmackTlsContext;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.StartTls;
|
||||
import org.jivesoftware.smack.packet.StreamOpen;
|
||||
|
@ -867,13 +862,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
|
|||
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException {
|
||||
connectionInternal.sendAndWaitForResponse(StartTls.INSTANCE, TlsProceed.class, TlsFailure.class);
|
||||
|
||||
SmackTlsContext smackTlsContext;
|
||||
try {
|
||||
smackTlsContext = connectionInternal.getSmackTlsContext();
|
||||
} catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException
|
||||
| CertificateException | KeyStoreException | NoSuchProviderException e) {
|
||||
throw new SmackWrappedException(e);
|
||||
}
|
||||
SmackTlsContext smackTlsContext = connectionInternal.getSmackTlsContext();
|
||||
|
||||
tlsState = new TlsState(smackTlsContext);
|
||||
connectionInternal.addXmppInputOutputFilter(tlsState);
|
||||
|
|
Loading…
Reference in a new issue