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:
Jay Kline 2007-11-30 19:40:31 +00:00 committed by jay
parent dbac260051
commit d3ee0e2f3e
2 changed files with 109 additions and 25 deletions

View File

@ -46,7 +46,7 @@ public class ConnectionConfiguration implements Cloneable {
private String truststorePassword;
private String keystorePath;
private String keystoreType;
private String keystorePassword;
private String pkcsConfig;
private boolean verifyChainEnabled = false;
private boolean verifyRootCAEnabled = false;
private boolean selfSignedCertificateEnabled = false;
@ -133,7 +133,7 @@ public class ConnectionConfiguration implements Cloneable {
truststorePassword = "changeit";
keystorePath = System.getProperty("javax.net.ssl.keyStore");
keystoreType = "jks";
keystorePassword = "changeit";
pkcsConfig = "pkcs11.config";
}
/**
@ -286,24 +286,25 @@ public class ConnectionConfiguration implements Cloneable {
this.keystoreType = keystoreType;
}
/**
* Returns the password to use to access the keystore file. It is assumed that all
* certificates share the same password in the keystore.
* Returns the PKCS11 configuration file location, needed when the
* 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() {
return keystorePassword;
public String getPKCSConfig() {
return pkcsConfig;
}
/**
* Sets the password to use to access the keystore file. It is assumed that all
* certificates share the same password in the trust store.
* Sets the PKCS11 configuration file location, needed when the
* 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) {
this.keystorePassword = keystorePassword;
public void setPKCSConfig(String pkcsConfig) {
this.pkcsConfig = pkcsConfig;
}
/**

View File

@ -30,6 +30,8 @@ import org.jivesoftware.smack.util.StringUtils;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.KeyManager;
import java.io.*;
@ -43,6 +45,8 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
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
@ -101,6 +105,9 @@ public class XMPPConnection {
// connection ID, which is a value sent by the server once a connection is made.
private static AtomicInteger connectionCounter = new AtomicInteger(0);
// CallbackHandler to handle prompting for theh keystore password.
private CallbackHandler callbackHandler = null;
static {
// Use try block since we may not have permission to get a system
// property (for example, when an applet).
@ -172,6 +179,7 @@ public class XMPPConnection {
private ConnectionConfiguration configuration;
private ChatManager chatManager;
/**
* 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
@ -185,7 +193,30 @@ public class XMPPConnection {
* {@link #XMPPConnection(ConnectionConfiguration)} constructor.<p>
* <p/>
* 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>.
*/
@ -196,6 +227,21 @@ public class XMPPConnection {
config.setSASLAuthenticationEnabled(true);
config.setDebuggerEnabled(DEBUG_ENABLED);
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>
* <p/>
* 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 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.callbackHandler = callbackHandler;
}
/**
* 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
@ -1249,17 +1303,46 @@ public class XMPPConnection {
*/
void proceedTLSReceived() throws Exception {
SSLContext context = SSLContext.getInstance("TLS");
KeyStore ks = KeyStore.getInstance(configuration.getKeystoreType());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
KeyStore ks;
KeyManager[] kms = null;
try {
ks.load(new FileInputStream(configuration.getKeystorePath()), configuration.getKeystorePassword().toCharArray());
kmf.init(ks,configuration.getKeystorePassword().toCharArray());
kms = kmf.getKeyManagers();
} catch (FileNotFoundException fourohfour) {
kms = null;
} catch (NullPointerException npe) {
kms = null;
if(callbackHandler == null) {
ks = null;
} else {
PasswordCallback pcb;
System.out.println("Keystore type: "+configuration.getKeystoreType());
if(configuration.getKeystoreType().equals("PKCS11")) {
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