Implement use of Certificate Verification service in jabber.

cusax-fix
Damian Minkov 16 years ago
parent f8a495de12
commit 2a49a5de69

Binary file not shown.

@ -9,6 +9,7 @@
import java.util.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.service.gui.*;
//import net.java.sip.communicator.service.media.*;
import net.java.sip.communicator.service.protocol.*;
@ -42,6 +43,11 @@ public class JabberActivator
*/
private static ConfigurationService configurationService = null;
/**
* File access service.
*/
private static FileAccessService fileService = null;
/**
* Media service.
*/
@ -122,6 +128,26 @@ public static ConfigurationService getConfigurationService()
return configurationService;
}
/**
* Returns a reference to a FileAccessService implementation currently
* registered in the bundle context or null if no such implementation was
* found.
*
* @return a currently valid implementation of the FileAccessService.
*/
public static FileAccessService getFileAccessService()
{
if(fileService == null)
{
ServiceReference fileReference
= bundleContext.getServiceReference(
FileAccessService.class.getName());
fileService
= (FileAccessService) bundleContext.getService(fileReference);
}
return fileService;
}
/**
* Returns a reference to the bundle context that we were started with.
* @return a reference to the BundleContext instance that we were started

@ -6,15 +6,20 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.*;
import java.text.*;
import java.util.*;
import javax.net.ssl.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.jabberconstants.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.impl.protocol.jabber.sasl.*;
import net.java.sip.communicator.service.gui.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.packet.*;
@ -22,6 +27,8 @@
import org.jivesoftware.smackx.*;
import org.jivesoftware.smackx.packet.*;
import org.osgi.framework.*;
/**
* An implementation of the protocol provider service over the Jabber protocol
*
@ -113,6 +120,54 @@ public class ProtocolProviderServiceJabberImpl
*/
private JabberStatusEnum jabberStatusEnum;
/**
* The service we use to interact with user.
*/
private CertificateVerificationService guiVerification;
/**
*
*/
private boolean additionalCertificateChecks = true;
/**
* The key store holding stored certificate during previous sessions.
*/
private KeyStore keyStore;
/**
* The property for the configuration value to store the
* KeyStore file location.
*/
private static final String KEYSTORE_FILE_PROP =
"net.java.sip.communicator.impl.protocol.sip.net.KEYSTORE";
/**
* The default password used for the keystore.
*/
private char[] defaultPassword = new char[0];
/**
* Used with tls connecting when certificates are not trusted
* and we ask the user to confirm connection. When some timeout expires
* connect method returns, and we use abortConnecting to abort further
* execution cause after user chooses we make further processing from there.
*/
private boolean abortConnecting = false;
/**
* Used with tls. When certificate was confirmed we abort current
* connecting and try connecting again.
*/
private boolean abortConnectingAndReconnect = false;
/**
* This are the certificates which are temporally allowed
* only for this session.
*/
private ArrayList<X509Certificate> temporalyAllowed =
new ArrayList<X509Certificate>();
/**
* Returns the state of the registration of this protocol provider
* @return the <tt>RegistrationState</tt> that this provider is
@ -128,6 +183,27 @@ else if(connection.isConnected() && connection.isAuthenticated())
return RegistrationState.UNREGISTERED;
}
/**
* Return the certificate verification service impl.
* @return the CertificateVerification service.
*/
private CertificateVerificationService getCertificateVerificationService()
{
if(guiVerification == null)
{
ServiceReference guiVerifyReference
= JabberActivator.getBundleContext().getServiceReference(
CertificateVerificationService.class.getName());
if(guiVerifyReference != null)
guiVerification = (CertificateVerificationService)
JabberActivator.getBundleContext().getService(
guiVerifyReference);
}
return guiVerification;
}
/**
* Starts the registration process. Connection details such as
* registration server, user name/number are provided through the
@ -135,7 +211,7 @@ else if(connection.isConnected() && connection.isAuthenticated())
*
* @param authority the security authority that will be used for resolving
* any security challenges that may be returned during the
* registration or at any moment while wer're registered.
* registration or at any moment while we're registered.
* @throws OperationFailedException with the corresponding code it the
* registration fails for some reason (e.g. a networking error or an
* implementation problem).
@ -423,9 +499,70 @@ else if(globalProxyType.equals(
proxy
);
confConn.setReconnectionAllowed(false);
confConn.setExpiredCertificatesCheckEnabled(
additionalCertificateChecks);
confConn.setNotMatchingDomainCheckEnabled(
additionalCertificateChecks);
TrustManagerFactory tmFactory = null;
try
{
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, defaultPassword);
String keyStoreFile =
JabberActivator.getConfigurationService()
.getString(KEYSTORE_FILE_PROP);
if(keyStoreFile == null || keyStoreFile.length() == 0)
{
File f = JabberActivator.getFileAccessService()
.getPrivatePersistentFile("jssecacerts");
keyStoreFile = f.getCanonicalPath();
JabberActivator.getConfigurationService()
.setProperty(KEYSTORE_FILE_PROP, keyStoreFile);
keyStore.store(
new FileOutputStream(f), defaultPassword);
}
else
{
File f = new File(keyStoreFile);
if(!f.exists())
{
// if for some reason file is missing, create it
// by saving the empty store
keyStore.store(
new FileOutputStream(f), defaultPassword);
}
keyStore.load(new FileInputStream(keyStoreFile), null);
}
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
tmFactory = TrustManagerFactory.getInstance(algorithm);
tmFactory.init(keyStore);
confConn.setKeystorePath(keyStoreFile);
} catch (Exception e)
{
logger.error("Cannot create key store", e);
}
connection = new XMPPConnection(confConn);
if(tmFactory != null)
{
connection.setCustomTrustManager(
new HostTrustManager(
(X509TrustManager)tmFactory.getTrustManagers()[0],
serverAddress,
Integer.parseInt(serverPort)));
}
connection.connect();
connection.addConnectionListener(
new JabberConnectionListener());
}
catch (XMPPException exc)
{
@ -442,8 +579,20 @@ else if(globalProxyType.equals(
, exc);
}
connection.addConnectionListener(
new JabberConnectionListener());
if(abortConnecting)
{
abortConnecting = false;
if(abortConnectingAndReconnect)
{
abortConnectingAndReconnect = false;
reregister(SecurityAuthority.CONNECTION_FAILED);
}
else
{
connection.disconnect();
}
return;
}
fireRegistrationStateChanged(
getRegistrationState()
@ -487,6 +636,21 @@ else if(globalProxyType.equals(
connection.addConnectionListener(
new JabberConnectionListener());
if(abortConnecting)
{
abortConnecting = false;
if(abortConnectingAndReconnect)
{
abortConnectingAndReconnect = false;
reregister(SecurityAuthority.CONNECTION_FAILED);
}
else
{
connection.disconnect();
}
return;
}
fireRegistrationStateChanged(
getRegistrationState()
, RegistrationState.REGISTERING
@ -898,6 +1062,11 @@ public void connectionClosedOnError(Exception exception)
"Connecting multiple times with the same resource");
return;
}
} // Ignore certificate exceptions as we handle them elsewhere
else if(exception instanceof SSLHandshakeException &&
exception.getCause() instanceof CertificateException)
{
return;
}
if(!reconnecting)
@ -1022,4 +1191,165 @@ String getFullJid(Contact contact)
return presence.getFrom();
}
/**
* The trust manager which asks the client whether to trust particular
* certificate which is not globally trusted.
*/
private class HostTrustManager
implements X509TrustManager
{
/**
* The address we connect to.
*/
String address;
/**
* The port we connect to.
*/
int port;
/**
* The default trust manager.
*/
private final X509TrustManager tm;
/**
* Creates the custom trust manager.
* @param tm the default trust manager.
* @param address the address we are connecting to.
* @param port the port.
*/
HostTrustManager(X509TrustManager tm, String address, int port)
{
this.tm = tm;
this.port = port;
this.address = address;
}
/**
* Not used.
* @return
*/
public X509Certificate[] getAcceptedIssuers() {
throw new UnsupportedOperationException();
}
/**
* Not used.
* @param chain the cert chain.
* @param authType authentication type like: RSA.
* @throws CertificateException
*/
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
throw new UnsupportedOperationException();
}
/**
* Check whether a certificate is trusted, if not as user whether he
* trust it.
* @param chain the certificate chain.
* @param authType authentication type like: RSA.
* @throws CertificateException not trusted.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
try
{
tm.checkServerTrusted(chain, authType);
} catch (Throwable certificateException)
{
try
{
for (int i = 0; i < chain.length; i++)
{
X509Certificate c = chain[i];
// check for temporaly allowed certs
if(temporalyAllowed.contains(c))
{
return;
}
// now check for permanent allow of certs
String alias = keyStore.getCertificateAlias(c);
if(alias != null)
return;
}
CertificateVerificationService guiVerification =
getCertificateVerificationService();
if(guiVerification == null)
throw new CertificateException(certificateException.getMessage());
abortConnecting = true;
int result = guiVerification
.verificationNeeded(chain, address, port);
if(result == CertificateVerificationService.DO_NOT_TRUST)
{
fireRegistrationStateChanged(getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
RegistrationStateChangeEvent.REASON_USER_REQUEST,
"Not trusted certificate");
return;
}
else if(result
== CertificateVerificationService.TRUST_THIS_SESSION_ONLY)
{
for (X509Certificate c : chain)
temporalyAllowed.add(c);
}
else if(result == CertificateVerificationService.TRUST_ALWAYS)
{
for (X509Certificate c : chain)
keyStore.setCertificateEntry(
String.valueOf(System.currentTimeMillis()), c);
}
try
{
String keyStoreFile = JabberActivator.getConfigurationService()
.getString(KEYSTORE_FILE_PROP);
keyStore.store(
new FileOutputStream(keyStoreFile), defaultPassword);
} catch (Exception e)
{
logger.error("Error saving keystore.", e);
}
if(abortConnecting)
{
abortConnectingAndReconnect = true;
return;
}
else
{
// register.connect in new thread so we can release the
// current connecting thread, otherwise this blocks
// jabber
new Thread(new Runnable()
{
public void run()
{
reregister(SecurityAuthority.CONNECTION_FAILED);
}
}).start();
return;
}
} catch (KeyStoreException e)
{
// something happend
logger.error("Error trying to " +
"show certificate to user", e);
throw new CertificateException(certificateException.getMessage());
}
}
}
}
}

@ -24,6 +24,7 @@ Import-Package: org.osgi.framework,
org.jivesoftware.smackx.filetransfer,
org.jivesoftware.smackx.provider,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
net.java.sip.communicator.service.configuration.event,
@ -36,5 +37,6 @@ Import-Package: org.osgi.framework,
org.xmlpull.v1,
org.xmlpull.mxp1,
javax.xml.parsers,
javax.net.ssl,
javax.security.sasl,
javax.security.auth.callback

Loading…
Cancel
Save