mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 14:02:06 +01:00
Added support for key and trust stores when making SSL/TLS connections
that require the client authenticate itself. This supports PKCS#11 devices. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@9538 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
dbac260051
commit
d3ee0e2f3e
2 changed files with 109 additions and 25 deletions
|
@ -46,7 +46,7 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
private String truststorePassword;
|
private String truststorePassword;
|
||||||
private String keystorePath;
|
private String keystorePath;
|
||||||
private String keystoreType;
|
private String keystoreType;
|
||||||
private String keystorePassword;
|
private String pkcsConfig;
|
||||||
private boolean verifyChainEnabled = false;
|
private boolean verifyChainEnabled = false;
|
||||||
private boolean verifyRootCAEnabled = false;
|
private boolean verifyRootCAEnabled = false;
|
||||||
private boolean selfSignedCertificateEnabled = false;
|
private boolean selfSignedCertificateEnabled = false;
|
||||||
|
@ -133,7 +133,7 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
truststorePassword = "changeit";
|
truststorePassword = "changeit";
|
||||||
keystorePath = System.getProperty("javax.net.ssl.keyStore");
|
keystorePath = System.getProperty("javax.net.ssl.keyStore");
|
||||||
keystoreType = "jks";
|
keystoreType = "jks";
|
||||||
keystorePassword = "changeit";
|
pkcsConfig = "pkcs11.config";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,24 +286,25 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
this.keystoreType = keystoreType;
|
this.keystoreType = keystoreType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the password to use to access the keystore file. It is assumed that all
|
* Returns the PKCS11 configuration file location, needed when the
|
||||||
* certificates share the same password in the keystore.
|
* Keystore type is PKCS11.
|
||||||
*
|
*
|
||||||
* @return the password to use to access the keystore file.
|
* @return the path to the PKCS11 configuration file
|
||||||
*/
|
*/
|
||||||
public String getKeystorePassword() {
|
public String getPKCSConfig() {
|
||||||
return keystorePassword;
|
return pkcsConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the password to use to access the keystore file. It is assumed that all
|
* Sets the PKCS11 configuration file location, needed when the
|
||||||
* certificates share the same password in the trust store.
|
* Keystore type is PKCS11
|
||||||
*
|
*
|
||||||
* @param keystorePassword the password to use to access the keystore file.
|
* @param pkcsConfig the path to the PKCS11 configuration file
|
||||||
*/
|
*/
|
||||||
public void setKeystorePassword(String keystorePassword) {
|
public void setPKCSConfig(String pkcsConfig) {
|
||||||
this.keystorePassword = keystorePassword;
|
this.pkcsConfig = pkcsConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,8 @@ import org.jivesoftware.smack.util.StringUtils;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.Security;
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -43,6 +45,8 @@ import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import javax.security.auth.callback.CallbackHandler;
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
|
import javax.security.auth.callback.Callback;
|
||||||
|
import javax.security.auth.callback.PasswordCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a connection to a XMPP server. A simple use of this API might
|
* Creates a connection to a XMPP server. A simple use of this API might
|
||||||
|
@ -101,6 +105,9 @@ public class XMPPConnection {
|
||||||
// connection ID, which is a value sent by the server once a connection is made.
|
// connection ID, which is a value sent by the server once a connection is made.
|
||||||
private static AtomicInteger connectionCounter = new AtomicInteger(0);
|
private static AtomicInteger connectionCounter = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// CallbackHandler to handle prompting for theh keystore password.
|
||||||
|
private CallbackHandler callbackHandler = null;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Use try block since we may not have permission to get a system
|
// Use try block since we may not have permission to get a system
|
||||||
// property (for example, when an applet).
|
// property (for example, when an applet).
|
||||||
|
@ -172,6 +179,7 @@ public class XMPPConnection {
|
||||||
private ConnectionConfiguration configuration;
|
private ConnectionConfiguration configuration;
|
||||||
private ChatManager chatManager;
|
private ChatManager chatManager;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new connection to the specified XMPP server. A DNS SRV lookup will be
|
* Creates a new connection to the specified XMPP server. A DNS SRV lookup will be
|
||||||
* performed to determine the IP address and port corresponding to the
|
* performed to determine the IP address and port corresponding to the
|
||||||
|
@ -185,7 +193,30 @@ public class XMPPConnection {
|
||||||
* {@link #XMPPConnection(ConnectionConfiguration)} constructor.<p>
|
* {@link #XMPPConnection(ConnectionConfiguration)} constructor.<p>
|
||||||
* <p/>
|
* <p/>
|
||||||
* Note that XMPPConnection constructors do not establish a connection to the server
|
* Note that XMPPConnection constructors do not establish a connection to the server
|
||||||
* and you must call {@link #connect()}.
|
* and you must call {@link #connect()}.<p>
|
||||||
|
* <p/>
|
||||||
|
* The CallbackHandler will only be used if the connection requires the client provide
|
||||||
|
* an SSL certificate to the server. The CallbackHandler must handle the PasswordCallback
|
||||||
|
* to prompt for a password to unlock the keystore containing the SSL certificate.
|
||||||
|
*
|
||||||
|
* @param serviceName the name of the XMPP server to connect to; e.g. <tt>example.com</tt>.
|
||||||
|
* @param callbackHandler the CallbackHandler used to prompt for the password to the keystore.
|
||||||
|
*/
|
||||||
|
public XMPPConnection(String serviceName, CallbackHandler callbackHandler) {
|
||||||
|
// Create the configuration for this new connection
|
||||||
|
ConnectionConfiguration config = new ConnectionConfiguration(serviceName);
|
||||||
|
config.setCompressionEnabled(false);
|
||||||
|
config.setSASLAuthenticationEnabled(true);
|
||||||
|
config.setDebuggerEnabled(DEBUG_ENABLED);
|
||||||
|
this.configuration = config;
|
||||||
|
this.callbackHandler = callbackHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new XMPP conection in the same way {@link #XMPPConnection(String,CallbackHandler)} does, but
|
||||||
|
* with no callback handler for password prompting of the keystore. This will work
|
||||||
|
* in most cases, provided the client is not required to provide a certificate to
|
||||||
|
* the server.
|
||||||
*
|
*
|
||||||
* @param serviceName the name of the XMPP server to connect to; e.g. <tt>example.com</tt>.
|
* @param serviceName the name of the XMPP server to connect to; e.g. <tt>example.com</tt>.
|
||||||
*/
|
*/
|
||||||
|
@ -196,6 +227,21 @@ public class XMPPConnection {
|
||||||
config.setSASLAuthenticationEnabled(true);
|
config.setSASLAuthenticationEnabled(true);
|
||||||
config.setDebuggerEnabled(DEBUG_ENABLED);
|
config.setDebuggerEnabled(DEBUG_ENABLED);
|
||||||
this.configuration = config;
|
this.configuration = config;
|
||||||
|
this.callbackHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new XMPP conection in the same way {@link #XMPPConnection(ConnectionConfiguration,CallbackHandler)} does, but
|
||||||
|
* with no callback handler for password prompting of the keystore. This will work
|
||||||
|
* in most cases, provided the client is not required to provide a certificate to
|
||||||
|
* the server.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param config the connection configuration.
|
||||||
|
*/
|
||||||
|
public XMPPConnection(ConnectionConfiguration config) {
|
||||||
|
this.configuration = config;
|
||||||
|
this.callbackHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,14 +252,22 @@ public class XMPPConnection {
|
||||||
* {@link #XMPPConnection(String)} constructor is a better approach.<p>
|
* {@link #XMPPConnection(String)} constructor is a better approach.<p>
|
||||||
* <p/>
|
* <p/>
|
||||||
* Note that XMPPConnection constructors do not establish a connection to the server
|
* Note that XMPPConnection constructors do not establish a connection to the server
|
||||||
* and you must call {@link #connect()}.
|
* and you must call {@link #connect()}.<p>
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* The CallbackHandler will only be used if the connection requires the client provide
|
||||||
|
* an SSL certificate to the server. The CallbackHandler must handle the PasswordCallback
|
||||||
|
* to prompt for a password to unlock the keystore containing the SSL certificate.
|
||||||
*
|
*
|
||||||
* @param config the connection configuration.
|
* @param config the connection configuration.
|
||||||
|
* @param callbackHandler the CallbackHandler used to prompt for the password to the keystore.
|
||||||
*/
|
*/
|
||||||
public XMPPConnection(ConnectionConfiguration config) {
|
public XMPPConnection(ConnectionConfiguration config, CallbackHandler callbackHandler) {
|
||||||
this.configuration = config;
|
this.configuration = config;
|
||||||
|
this.callbackHandler = callbackHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the connection ID for this connection, which is the value set by the server
|
* Returns the connection ID for this connection, which is the value set by the server
|
||||||
* when opening a XMPP stream. If the server does not set a connection ID, this value
|
* when opening a XMPP stream. If the server does not set a connection ID, this value
|
||||||
|
@ -1249,17 +1303,46 @@ public class XMPPConnection {
|
||||||
*/
|
*/
|
||||||
void proceedTLSReceived() throws Exception {
|
void proceedTLSReceived() throws Exception {
|
||||||
SSLContext context = SSLContext.getInstance("TLS");
|
SSLContext context = SSLContext.getInstance("TLS");
|
||||||
KeyStore ks = KeyStore.getInstance(configuration.getKeystoreType());
|
KeyStore ks;
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
|
||||||
KeyManager[] kms = null;
|
KeyManager[] kms = null;
|
||||||
try {
|
|
||||||
ks.load(new FileInputStream(configuration.getKeystorePath()), configuration.getKeystorePassword().toCharArray());
|
if(callbackHandler == null) {
|
||||||
kmf.init(ks,configuration.getKeystorePassword().toCharArray());
|
ks = null;
|
||||||
kms = kmf.getKeyManagers();
|
} else {
|
||||||
} catch (FileNotFoundException fourohfour) {
|
PasswordCallback pcb;
|
||||||
kms = null;
|
System.out.println("Keystore type: "+configuration.getKeystoreType());
|
||||||
} catch (NullPointerException npe) {
|
if(configuration.getKeystoreType().equals("PKCS11")) {
|
||||||
kms = null;
|
Provider p = new sun.security.pkcs11.SunPKCS11(configuration.getPKCSConfig());
|
||||||
|
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(configuration.getKeystoreType().equals("Apple")) {
|
||||||
|
ks = KeyStore.getInstance("KeychainStore","Apple");
|
||||||
|
ks.load(null,null);
|
||||||
|
//pcb = new PasswordCallback("Apple Keychain",false);
|
||||||
|
//pcb.setPassword(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ks = KeyStore.getInstance(configuration.getKeystoreType());
|
||||||
|
pcb = new PasswordCallback("Keystore Password: ",false);
|
||||||
|
callbackHandler.handle(new Callback[]{pcb});
|
||||||
|
ks.load(new FileInputStream(configuration.getKeystorePath()), pcb.getPassword());
|
||||||
|
}
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||||
|
try {
|
||||||
|
if(pcb == null) {
|
||||||
|
kmf.init(ks,null);
|
||||||
|
} else {
|
||||||
|
kmf.init(ks,pcb.getPassword());
|
||||||
|
}
|
||||||
|
kms = kmf.getKeyManagers();
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
kms = null;
|
||||||
|
}
|
||||||
|
pcb.clearPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify certificate presented by the server
|
// Verify certificate presented by the server
|
||||||
|
|
Loading…
Reference in a new issue