mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-22 10:37:59 +01:00
Merge pull request #518 from Flowdalic/ssl-context-config
SSLContext config
This commit is contained in:
commit
3885153ea1
2 changed files with 150 additions and 70 deletions
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software, 2017-2020 Florian Schmaus.
|
||||
* Copyright 2003-2007 Jive Software, 2017-2022 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -32,6 +32,7 @@ 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;
|
||||
|
@ -189,7 +190,7 @@ public abstract class ConnectionConfiguration {
|
|||
protected ConnectionConfiguration(Builder<?, ?> builder) {
|
||||
try {
|
||||
smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory,
|
||||
builder.customX509TrustManager, builder.keystoreType, builder.keystorePath,
|
||||
builder.customX509TrustManager, builder.keyManagers, builder.sslContextSecureRandom, builder.keystoreType, builder.keystorePath,
|
||||
builder.callbackHandler, builder.pkcs11Library);
|
||||
} catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException
|
||||
| KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException
|
||||
|
@ -252,7 +253,7 @@ public abstract class ConnectionConfiguration {
|
|||
}
|
||||
|
||||
private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
|
||||
X509TrustManager trustManager, String keystoreType, String keystorePath,
|
||||
X509TrustManager trustManager, KeyManager[] keyManagers, SecureRandom secureRandom, String keystoreType, String keystorePath,
|
||||
CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException,
|
||||
CertificateException, IOException, KeyStoreException, NoSuchProviderException,
|
||||
UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException,
|
||||
|
@ -266,69 +267,10 @@ public abstract class ConnectionConfiguration {
|
|||
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();
|
||||
}
|
||||
// TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHanlder() and
|
||||
// setPKCS11Library() in the builder, and all related fields and the parameters of this function.
|
||||
if (keyManagers == null) {
|
||||
keyManagers = Builder.getKeyManagersFrom(keystoreType, keystorePath, callbackHandler, pkcs11Library);
|
||||
}
|
||||
|
||||
SmackDaneVerifier daneVerifier = null;
|
||||
|
@ -343,7 +285,7 @@ public abstract class ConnectionConfiguration {
|
|||
}
|
||||
|
||||
// User requested DANE verification.
|
||||
daneVerifier.init(context, kms, trustManager, null);
|
||||
daneVerifier.init(context, keyManagers, trustManager, secureRandom);
|
||||
} else {
|
||||
final TrustManager[] trustManagers;
|
||||
if (trustManager != null) {
|
||||
|
@ -354,7 +296,7 @@ public abstract class ConnectionConfiguration {
|
|||
trustManagers = null;
|
||||
}
|
||||
|
||||
context.init(kms, trustManagers, null);
|
||||
context.init(keyManagers, trustManagers, secureRandom);
|
||||
}
|
||||
|
||||
return new SmackTlsContext(context, daneVerifier);
|
||||
|
@ -688,6 +630,8 @@ 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 KeyManager[] keyManagers;
|
||||
private SecureRandom sslContextSecureRandom;
|
||||
private String keystorePath;
|
||||
private String keystoreType;
|
||||
private String pkcs11Library = "pkcs11.config";
|
||||
|
@ -942,7 +886,12 @@ public abstract class ConnectionConfiguration {
|
|||
* @param callbackHandler to obtain information, such as the password or
|
||||
* principal information during the SASL authentication.
|
||||
* @return a reference to this builder.
|
||||
* @deprecated set a callback-handler aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.6.
|
||||
@Deprecated
|
||||
public B setCallbackHandler(CallbackHandler callbackHandler) {
|
||||
this.callbackHandler = callbackHandler;
|
||||
return getThis();
|
||||
|
@ -970,6 +919,47 @@ public abstract class ConnectionConfiguration {
|
|||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection.
|
||||
*
|
||||
* @param keyManagers an array of {@link KeyManager}s to initialize the {@link SSLContext} with.
|
||||
* @return a reference to this builder.
|
||||
* @since 4.4.5
|
||||
*/
|
||||
public B setKeyManagers(KeyManager[] keyManagers) {
|
||||
this.keyManagers = keyManagers;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection.
|
||||
*
|
||||
* @param keyManager the {@link KeyManager}s to initialize the {@link SSLContext} with.
|
||||
* @return a reference to this builder.
|
||||
* @see #setKeyManagers(KeyManager[])
|
||||
* @since 4.4.5
|
||||
*/
|
||||
public B setKeyManager(KeyManager keyManager) {
|
||||
KeyManager[] keyManagers = new KeyManager[] { keyManager };
|
||||
return setKeyManagers(keyManagers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link SecureRandom} used to initialize the {@link SSLContext} used by Smack to establish the XMPP
|
||||
* connection. Note that you usually do not need (nor want) to set this. Because if the {@link SecureRandom} is
|
||||
* not explicitly set, Smack will initialize the {@link SSLContext} with <code>null</code> as
|
||||
* {@link SecureRandom} argument. And all sane {@link SSLContext} implementations will then select a safe secure
|
||||
* random source by default.
|
||||
*
|
||||
* @param secureRandom the {@link SecureRandom} to initialize the {@link SSLContext} with.
|
||||
* @return a reference to this builder.
|
||||
* @since 4.4.5
|
||||
*/
|
||||
public B setSslContextSecureRandom(SecureRandom secureRandom) {
|
||||
this.sslContextSecureRandom = secureRandom;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the keystore file. The key store file contains the
|
||||
* certificates that may be used to authenticate the client to the server,
|
||||
|
@ -977,7 +967,12 @@ public abstract class ConnectionConfiguration {
|
|||
*
|
||||
* @param keystorePath the path to the keystore file.
|
||||
* @return a reference to this builder.
|
||||
* @deprecated set a keystore-path aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.6.
|
||||
@Deprecated
|
||||
public B setKeystorePath(String keystorePath) {
|
||||
this.keystorePath = keystorePath;
|
||||
return getThis();
|
||||
|
@ -988,7 +983,12 @@ public abstract class ConnectionConfiguration {
|
|||
*
|
||||
* @param keystoreType the keystore type.
|
||||
* @return a reference to this builder.
|
||||
* @deprecated set a key-type aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.6.
|
||||
@Deprecated
|
||||
public B setKeystoreType(String keystoreType) {
|
||||
this.keystoreType = keystoreType;
|
||||
return getThis();
|
||||
|
@ -1000,7 +1000,12 @@ public abstract class ConnectionConfiguration {
|
|||
*
|
||||
* @param pkcs11Library the path to the PKCS11 library file.
|
||||
* @return a reference to this builder.
|
||||
* @deprecated set a PKCS11-library aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.6.
|
||||
@Deprecated
|
||||
public B setPKCS11Library(String pkcs11Library) {
|
||||
this.pkcs11Library = pkcs11Library;
|
||||
return getThis();
|
||||
|
@ -1276,5 +1281,77 @@ public abstract class ConnectionConfiguration {
|
|||
public abstract C build();
|
||||
|
||||
protected abstract B getThis();
|
||||
|
||||
public static KeyManager[] getKeyManagersFrom(String keystoreType, String keystorePath,
|
||||
CallbackHandler callbackHandler, String pkcs11Library)
|
||||
throws NoSuchMethodException, SecurityException, ClassNotFoundException, KeyStoreException,
|
||||
NoSuchProviderException, NoSuchAlgorithmException, CertificateException, IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, UnsupportedCallbackException, UnrecoverableKeyException {
|
||||
KeyManager[] keyManagers = null;
|
||||
KeyStore ks = null;
|
||||
PasswordCallback pcb = 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();
|
||||
}
|
||||
keyManagers = kmf.getKeyManagers();
|
||||
}
|
||||
}
|
||||
|
||||
return keyManagers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import javax.net.SocketFactory;
|
|||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
|
@ -713,9 +714,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
SmackTlsContext smackTlsContext = getSmackTlsContext();
|
||||
|
||||
Socket plain = socket;
|
||||
int port = plain.getPort();
|
||||
String xmppServiceDomainString = config.getXMPPServiceDomain().toString();
|
||||
SSLSocketFactory sslSocketFactory = smackTlsContext.sslContext.getSocketFactory();
|
||||
// Secure the plain connection
|
||||
socket = smackTlsContext.sslContext.getSocketFactory().createSocket(plain,
|
||||
config.getXMPPServiceDomain().toString(), plain.getPort(), true);
|
||||
socket = sslSocketFactory.createSocket(plain, xmppServiceDomainString, port, true);
|
||||
|
||||
final SSLSocket sslSocket = (SSLSocket) socket;
|
||||
// Immediately set the enabled SSL protocols and ciphers. See SMACK-712 why this is
|
||||
|
|
Loading…
Reference in a new issue