Update CertificateVerificationService validates client options, obtaining sslcontext by specifying only error message, option to insert certificate and to look for system properties and system wide keystore. Patch provided by Ingo Bauersachs.

cusax-fix
Damian Minkov 15 years ago
parent eb3d66d730
commit 52d8ab0c93

@ -502,6 +502,18 @@ The certificate is not trusted, which means that the server''s \
identity cannot<br> be automatically verified. \
Do you want to continue connecting?<br><br> \
For more information, click "Show Certificate".</html>
service.gui.CERT_DIALOG_CLIENT_DESCRIPTION_TXT=<html>{0} can''t verify the identity \
of the connecting client.<br><br> \
The certificate is not trusted, which means that the client''s \
identity cannot<br> be automatically verified. \
Do you want to accept the connection?<br><br> \
For more information, click "Show Certificate".</html>
service.gui.CERT_DIALOG_PEER_DESCRIPTION_TXT=<html>The identity \
of the peer {0} could not be verified.<br><br> \
The certificate is not trusted, which means that the peer''s \
identity cannot<br> be automatically verified. \
Do you want to continue connecting?<br><br> \
For more information, click "Show Certificate".</html>
service.gui.CERT_INFO_ISSUED_TO=<html><b>Issued To</b></html>
service.gui.CERT_INFO_CN=Common Name:
service.gui.CERT_INFO_O=Organization:

@ -25,7 +25,7 @@ public class CertificateVerificationActivator
/**
* The bundle context for this bundle.
*/
static BundleContext bundleContext;
protected static BundleContext bundleContext;
/**
* The configuration service.

@ -12,7 +12,6 @@
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.cert.*;
import java.security.interfaces.*;
import java.text.*;
@ -112,21 +111,61 @@ private File getKeyStoreLocation()
}
}
/**
* Add a certificate to the local trust store.
*
* @param cert The certificate to add to the trust store.
* @param trustMode Whether to trust the certificate permanently
* or only for the current session.
* @throws GeneralSecurityException
*/
public void addCertificateToTrust(X509Certificate cert, int trustMode)
throws GeneralSecurityException
{
switch (trustMode)
{
case TRUST_ALWAYS:
try
{
KeyStore kStore = getKeyStore();
synchronized (kStore)
{
kStore.setCertificateEntry(
String.valueOf(System.currentTimeMillis()), cert);
kStore.store(new FileOutputStream(getKeyStoreLocation()),
defaultPassword);
}
}
catch (Exception e)
{
logger.error("cannot add certificate to truststore", e);
}
break;
case TRUST_THIS_SESSION_ONLY:
temporalyAllowed.add(cert);
break;
default:
throw new GeneralSecurityException(
"Cannot add a certificate to truststore "
+ "when no trust is requested.");
}
}
/**
* Checks does the user trust the supplied chain of certificates, when
* connecting to the server and port.
*
* @param chain the chain of the certificates to check with user.
* @param toHost the host we are connecting.
* @param toPort the port used when connecting.
* @param message A text that describes why the verification failed.
* @return the result of user interaction on of DO_NOT_TRUST, TRUST_ALWAYS,
* TRUST_THIS_SESSION_ONLY.
*/
public int verify(
final X509Certificate[] chain, final String toHost, final int toPort)
protected int verify(
final X509Certificate[] chain, final String message)
{
final VerifyCertificateDialog dialog = new VerifyCertificateDialog(
chain, toHost, toPort);
chain, message);
try
{
// show the dialog in the swing thread and wait for the user
@ -154,6 +193,9 @@ else if(dialog.alwaysTrustCheckBox.isSelected())
synchronized(kStore)
{
// TODO this is potentially dangerous and not what the user
// wanted (e.g. trusting a complete fraud CA instead of only
// trusting one single certificate
for (X509Certificate c : chain)
kStore.setCertificateEntry(
String.valueOf(System.currentTimeMillis()), c);
@ -182,13 +224,12 @@ else if(dialog.alwaysTrustCheckBox.isSelected())
* Obtain custom trust manager, which will try verify the certificate and
* if verification fails will query the user for acceptance.
*
* @param toHost the host we are connecting.
* @param toPort the port used when connecting.
* @param message A text that describes why the verification failed.
* @return the custom trust manager.
* @throws GeneralSecurityException when there is problem creating
* the trust manager
*/
public X509TrustManager getTrustManager(String toHost, int toPort)
public X509TrustManager getTrustManager(String message)
throws GeneralSecurityException
{
TrustManagerFactory tmFactory =
@ -197,8 +238,7 @@ public X509TrustManager getTrustManager(String toHost, int toPort)
tmFactory.init((KeyStore)null);
return new HostTrustManager(
(X509TrustManager)tmFactory.getTrustManagers()[0],
toHost, toPort);
(X509TrustManager)tmFactory.getTrustManagers()[0], message);
}
/**
@ -208,34 +248,76 @@ public X509TrustManager getTrustManager(String toHost, int toPort)
*
* @param toHost the host we are connecting.
* @param toPort the port used when connecting.
* @return SSL context object
*
* @throws IOException if the SSLContext could not be initialized
* @return the SSLContext
* @throws IOException throws exception when unable to initialize the
* ssl context.
*/
public SSLContext getSSLContext(String toHost, int toPort)
throws IOException
{
return getSSLContext(
CertificateVerificationActivator.getResources().
getI18NString(
"service.gui.CERT_DIALOG_DESCRIPTION_TXT",
new String[]{
CertificateVerificationActivator.getResources()
.getSettingsString("service.gui.APPLICATION_NAME"),
toHost,
Integer.toString(toPort)
}
)
);
}
/**
* Returns SSLContext instance initialized with the custom trust manager,
* which will try verify the certificate and if verification fails
* will query the user for acceptance.
*
* @param message The message to show on the verification GUI if necessary
* @return the SSLContext
* @throws IOException throws exception when unable to initialize the
* ssl context.
*/
public SSLContext getSSLContext(String message)
throws IOException
{
try
{
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLS");
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmFactory =
TrustManagerFactory.getInstance(algorithm);
KeyManagerFactory kmFactory =
KeyManagerFactory.getInstance(algorithm);
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextInt();
// init the default truststore
tmFactory.init((KeyStore)null);
kmFactory.init(null, null);
sslContext.init(kmFactory.getKeyManagers(),
new TrustManager[]{new HostTrustManager(
(X509TrustManager)tmFactory.getTrustManagers()[0],
toHost, toPort)}
, secureRandom);
KeyStore ks = KeyStore.getInstance(
System.getProperty(
"javax.net.ssl.keyStoreType",
KeyStore.getDefaultType()
)
);
KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm()
);
String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
if(System.getProperty("javax.net.ssl.keyStore") != null)
{
ks.load(
new FileInputStream(System.getProperty("javax.net.ssl.keyStore")),
null
);
}
else
{
ks.load(null, null);
}
kmFactory.init(
ks,
keyStorePassword == null ? null : keyStorePassword.toCharArray()
);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(
kmFactory.getKeyManagers(),
new TrustManager[]{getTrustManager(message)},
null
);
return sslContext;
} catch (Exception e)
@ -322,14 +404,9 @@ private static class VerifyCertificateDialog
Certificate cert;
/**
* The host we are connecting to.
* A text that describes why the verification failed.
*/
String host;
/**
* The port we use.
*/
int port;
String message;
/**
* The certificate panel.
@ -369,12 +446,10 @@ private static class VerifyCertificateDialog
* Creates the dialog.
*
* @param certs the certificates list
* @param host the host
* @param port the port
* @param message A text that describes why the verification failed.
*/
public VerifyCertificateDialog( Certificate[] certs,
String host,
int port)
String message)
{
super(false);
@ -384,8 +459,7 @@ public VerifyCertificateDialog( Certificate[] certs,
// for now shows only the first certificate from the chain
this.cert = certs[0];
this.host = host;
this.port = port;
this.message = message;
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
@ -414,20 +488,11 @@ private void init()
imgLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
northPanel.add(imgLabel, BorderLayout.WEST);
String descriptionTxt = CertificateVerificationActivator
.getResources().getI18NString(
"service.gui.CERT_DIALOG_DESCRIPTION_TXT",
new String[]{
CertificateVerificationActivator.getResources()
.getSettingsString("service.gui.APPLICATION_NAME"),
host,
String.valueOf(port)});
StyledHTMLEditorPane descriptionPane = new StyledHTMLEditorPane();
descriptionPane.setOpaque(false);
descriptionPane.setEditable(false);
descriptionPane.setContentType("text/html");
descriptionPane.setText(descriptionTxt);
descriptionPane.setText(message);
descriptionPane.setSize(
new Dimension(MAX_MSG_PANE_WIDTH, MAX_MSG_PANE_HEIGHT));
int height = descriptionPane.getPreferredSize().height;
@ -1003,14 +1068,10 @@ private class HostTrustManager
implements X509TrustManager
{
/**
* The address we connect to.
* A text displayed on the gui that describes why the
* verification failed.
*/
String address;
/**
* The port we connect to.
*/
int port;
String message;
/**
* The default trust manager.
@ -1020,23 +1081,20 @@ private class HostTrustManager
/**
* 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)
private HostTrustManager(X509TrustManager tm, String message)
{
this.tm = tm;
this.port = port;
this.address = address;
this.message = message;
}
/**
* Not used.
* Not used, returns an empty array.
* @return UnsupportedOperationException
*/
public X509Certificate[] getAcceptedIssuers()
{
throw new UnsupportedOperationException();
return new X509Certificate[]{};
}
/**
@ -1049,7 +1107,22 @@ public X509Certificate[] getAcceptedIssuers()
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
throw new UnsupportedOperationException();
if(CertificateVerificationActivator.getConfigurationService()
.getBoolean(CertificateVerificationService
.ALWAYS_TRUST_MODE_ENABLED_PROP_NAME,
false))
{
return;
}
try
{
tm.checkClientTrusted(chain, authType);
// everything is fine certificate is globally trusted
} catch (CertificateException certificateException)
{
checkLocalTrust(chain, authType, certificateException);
}
}
/**
@ -1084,44 +1157,52 @@ public void checkServerTrusted(X509Certificate[] chain, String authType)
}
catch (CertificateException certificateException)
{
KeyStore kStore = getKeyStore();
checkLocalTrust(chain, authType, certificateException);
}
}
private void checkLocalTrust(X509Certificate[] chain,
String authType,
CertificateException certificateException)
throws CertificateException
{
KeyStore kStore = getKeyStore();
if(kStore == null)
throw certificateException;
if(kStore == null)
throw certificateException;
try
try
{
for (int i = 0; i < chain.length; i++)
{
for (int i = 0; i < chain.length; i++)
X509Certificate c = chain[i];
// check for temporaly allowed certs
if(temporalyAllowed.contains(c))
{
X509Certificate c = chain[i];
// check for temporaly allowed certs
if(temporalyAllowed.contains(c))
{
return;
}
// now check for permanent allow of certs
String alias = kStore.getCertificateAlias(c);
if(alias != null)
{
return;
}
return;
}
if(verify(chain, address, port)
== CertificateVerificationService.DO_NOT_TRUST)
// now check for permanent allow of certs
String alias = kStore.getCertificateAlias(c);
if(alias != null)
{
throw certificateException;
return;
}
} catch (Throwable e)
{
// something happend
logger.error("Error trying to " +
"show certificate to user", e);
}
if(verify(chain, message)
== CertificateVerificationService.DO_NOT_TRUST)
{
throw certificateException;
}
} catch (Throwable e)
{
// something happend
logger.error("Error trying to " +
"show certificate to user", e);
throw certificateException;
}
}
}

@ -770,8 +770,16 @@ private ConnectState connectAndLogin(
{
connection.setCustomTrustManager(
new HostTrustManager(gvs.getTrustManager(
address,
serverPort)));
JabberActivator.getResources().getI18NString(
"service.gui.CERT_DIALOG_DESCRIPTION_TXT",
new String[]{ JabberActivator.getResources().
getSettingsString(
"service.gui.APPLICATION_NAME"),
address, Integer.toString(serverPort)
}
)
)
));
}
}
catch(GeneralSecurityException e)

@ -156,7 +156,15 @@ private SSLServerSocketFactory getSSLServerSocketFactory(
String address, int port)
throws IOException
{
return getSSLContext(address, port).getServerSocketFactory();
return getSSLContext(SipActivator.getResources().
getI18NString(
"service.gui.CERT_DIALOG_CLIENT_DESCRIPTION_TXT",
new String[]
{
SipActivator.getResources()
.getSettingsString("service.gui.APPLICATION_NAME")
})
).getServerSocketFactory();
}
/**
@ -192,13 +200,34 @@ private SSLContext getSSLContext(String address, int port)
tmFactory.getTrustManagers(), secureRandom);
return sslContext;
} catch (Throwable e)
}
catch (Throwable e)
{
throw new IOException("Cannot init SSLContext: " +
e.getMessage());
}
}
}
/**
* Creates the ssl context used to create ssl socket factories. Used
* to install our custom trust manager which knows the address
* we are connecting to.
* @param message the message to show on the verification GUI
* @return the ssl context.
* @throws IOException problem creating ssl context.
*/
private SSLContext getSSLContext(String message)
throws IOException
{
if(certificateVerification != null)
return certificateVerification.getSSLContext(message);
else
{
//goes to the non-service case which doesn't use the a message/port
return getSSLContext(null, 0);
}
}
/**
* Creates ssl socket factory.

@ -42,28 +42,26 @@ public interface CertificateVerificationService
public static int TRUST_THIS_SESSION_ONLY = 2;
/**
* Checks does the user trust the supplied chain of certificates, when
* connecting to the server and port. If needed shows dialog to confirm.
*
* @param chain the chain of the certificates to check with user.
* @param toHost the host we are connecting.
* @param toPort the port used when connecting.
* @return the result of user interaction on of DO_NOT_TRUST, TRUST_ALWAYS,
* TRUST_THIS_SESSION_ONLY.
* Add a certificate to the local trust store.
*
* @param cert The certificate to add to the trust store.
* @param trustMode Whether to trust the certificate permanently
* or only for the current session.
* @throws GeneralSecurityException
*/
public int verify(X509Certificate[] chain, String toHost, int toPort);
public void addCertificateToTrust(X509Certificate cert, int trustMode)
throws GeneralSecurityException;
/**
* Obtain custom trust manager, which will try verify the certificate and
* if verification fails will query the user for acceptance.
*
* @param toHost the host we are connecting.
* @param toPort the port used when connecting.
* Obtain custom trust manager, which tries to verify the certificate and
* queries the user for acceptance when verification fails.
*
* @param message A text that describes why the verification failed.
* @return the custom trust manager.
* @throws GeneralSecurityException when there is problem creating
* @throws GeneralSecurityException when there was a problem creating
* the trust manager
*/
public X509TrustManager getTrustManager(String toHost, int toPort)
public X509TrustManager getTrustManager(String message)
throws GeneralSecurityException;
/**
@ -79,4 +77,17 @@ public X509TrustManager getTrustManager(String toHost, int toPort)
*/
public SSLContext getSSLContext(String toHost, int toPort)
throws IOException;
/**
* Returns SSLContext instance initialized with the custom trust manager,
* which will try verify the certificate and if verification fails
* will query the user for acceptance.
*
* @param message The message to show on the verification GUI if necessary
* @return the SSLContext
* @throws IOException throws exception when unable to initialize the
* ssl context.
*/
public SSLContext getSSLContext(String message)
throws IOException;
}

Loading…
Cancel
Save