mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-23 02:58:00 +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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue