aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java
diff options
context:
space:
mode:
authorIngo Bauersachs <ingo@jitsi.org>2011-08-25 18:30:44 +0000
committerIngo Bauersachs <ingo@jitsi.org>2011-08-25 18:30:44 +0000
commit661810caa7cfebe1c72b8dac3ad3d86adf021f04 (patch)
treeec50a9f7d04633298b36eb9a0886ccc338126f37 /src/net/java
parente08fb14afaffa218289086f1d9fe3f58ed2e0bcb (diff)
downloadjitsi-661810caa7cfebe1c72b8dac3ad3d86adf021f04.zip
jitsi-661810caa7cfebe1c72b8dac3ad3d86adf021f04.tar.gz
jitsi-661810caa7cfebe1c72b8dac3ad3d86adf021f04.tar.bz2
Enable client TLS authentication for SIP and add a corresponding
configuration form
Diffstat (limited to 'src/net/java')
-rw-r--r--src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java302
-rw-r--r--src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java25
-rw-r--r--src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java377
-rw-r--r--src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf11
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java21
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java2
-rw-r--r--src/net/java/sip/communicator/plugin/certconfig/CertConfigActivator.java111
-rw-r--r--src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java548
-rw-r--r--src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java215
-rw-r--r--src/net/java/sip/communicator/plugin/certconfig/CertConfigTableModel.java91
-rw-r--r--src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf21
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java52
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccRegWizzActivator.java23
-rw-r--r--[-rwxr-xr-x]src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java20
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java11
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java3
-rw-r--r--[-rwxr-xr-x]src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/service/certificate/CertificateConfigEntry.java175
-rw-r--r--src/net/java/sip/communicator/service/certificate/CertificateService.java93
-rw-r--r--src/net/java/sip/communicator/service/certificate/KeyStoreType.java68
-rw-r--r--src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java7
-rw-r--r--src/net/java/sip/communicator/util/swing/AuthenticationWindow.java121
-rw-r--r--src/net/java/sip/communicator/util/swing/X509CertificatePanel.java417
-rw-r--r--src/net/java/sip/communicator/util/util.manifest.mf4
24 files changed, 2322 insertions, 397 deletions
diff --git a/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java b/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java
index 921d9c6..d6915c9 100644
--- a/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java
@@ -6,14 +6,17 @@
*/
package net.java.sip.communicator.impl.certificate;
+import java.beans.*;
import java.io.*;
import java.net.*;
import java.security.*;
+import java.security.KeyStore.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.util.*;
import javax.net.ssl.*;
+import javax.security.auth.callback.*;
import javax.swing.*;
import org.bouncycastle.asn1.*;
@@ -22,9 +25,11 @@ import org.bouncycastle.asn1.x509.X509Extension;
import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.credentialsstorage.*;
import net.java.sip.communicator.service.httputil.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
/**
* Implementation of the CertificateService. It asks the user to trust a
@@ -33,9 +38,27 @@ import net.java.sip.communicator.util.*;
* @author Ingo Bauersachs
*/
public class CertificateServiceImpl
- implements CertificateService
+ implements CertificateService, PropertyChangeListener
{
+ // ------------------------------------------------------------------------
+ // static data
+ // ------------------------------------------------------------------------
+ private final List<KeyStoreType> supportedTypes =
+ new LinkedList<KeyStoreType>()
+ {
+ {
+ add(new KeyStoreType("PKCS11", new String[]
+ { ".dll", ".so" }, false));
+ add(new KeyStoreType("PKCS12", new String[]
+ { ".p12", ".pfx" }, true));
+ add(new KeyStoreType(KeyStore.getDefaultType(), new String[]
+ { ".ks", ".jks" }, true));
+ }
+ };
+
+ // ------------------------------------------------------------------------
// services
+ // ------------------------------------------------------------------------
private static final Logger logger =
Logger.getLogger(CertificateServiceImpl.class);
@@ -45,7 +68,12 @@ public class CertificateServiceImpl
private final ConfigurationService config =
CertificateVerificationActivator.getConfigurationService();
+ private final CredentialsStorageService credService =
+ CertificateVerificationActivator.getCredService();
+
+ // ------------------------------------------------------------------------
// properties
+ // ------------------------------------------------------------------------
/**
* Base property name for the storage of certificate user preferences.
*/
@@ -55,7 +83,9 @@ public class CertificateServiceImpl
/** Hash algorithm for the cert thumbprint*/
private final static String THUMBPRINT_HASH_ALGORITHM = "SHA1";
- // variables
+ // ------------------------------------------------------------------------
+ // fields
+ // ------------------------------------------------------------------------
/**
* Stores the certificates that are trusted as long as this service lives.
*/
@@ -81,6 +111,142 @@ public class CertificateServiceImpl
return entry;
}
+ // ------------------------------------------------------------------------
+ // Truststore configuration
+ // ------------------------------------------------------------------------
+ public CertificateServiceImpl()
+ {
+ setTrustStore();
+ config.addPropertyChangeListener(PNAME_TRUSTSTORE, this);
+ }
+
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ setTrustStore();
+ }
+
+ private void setTrustStore()
+ {
+ String trustStore = (String)config.getProperty(PNAME_TRUSTSTORE);
+ if(trustStore != null)
+ {
+ System.setProperty("javax.net.ssl.trustStoreType",
+ trustStore);
+ String password =
+ (String) credService.loadPassword(PNAME_TRUSTSTORE_PASSWORD);
+ if(password != null)
+ {
+ System.setProperty("javax.net.ssl.trustStorePassword",
+ password);
+ }
+ }
+ else
+ {
+ System.getProperties().remove("javax.net.ssl.trustStoreType");
+ System.getProperties().remove("javax.net.ssl.trustStorePassword");
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Client authentication configuration
+ // ------------------------------------------------------------------------
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.service.certificate.CertificateService#
+ * getSupportedKeyStoreTypes()
+ */
+ public List<KeyStoreType> getSupportedKeyStoreTypes()
+ {
+ return supportedTypes;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.service.certificate.CertificateService#
+ * getClientAuthCertificateConfigs()
+ */
+ public List<CertificateConfigEntry> getClientAuthCertificateConfigs()
+ {
+ List<CertificateConfigEntry> map =
+ new LinkedList<CertificateConfigEntry>();
+ for (String propName : config.getPropertyNamesByPrefix(
+ PNAME_CLIENTAUTH_CERTCONFIG_BASE, false))
+ {
+ String propValue = config.getString(propName);
+ if(propValue == null || !propName.endsWith(propValue))
+ continue;
+
+ String pnBase = PNAME_CLIENTAUTH_CERTCONFIG_BASE
+ + "." + propValue;
+ CertificateConfigEntry e = new CertificateConfigEntry();
+ e.setId(propValue);
+ e.setAlias(config.getString(pnBase + ".alias"));
+ e.setDisplayName(config.getString(pnBase + ".displayName"));
+ e.setKeyStore(config.getString(pnBase + ".keyStore"));
+ e.setSavePassword(config.getBoolean(pnBase + ".savePassword", false));
+ if(e.isSavePassword())
+ {
+ e.setKeyStorePassword(credService.loadPassword(pnBase));
+ }
+ String type = config.getString(pnBase + ".keyStoreType");
+ for(KeyStoreType kt : getSupportedKeyStoreTypes())
+ {
+ if(kt.getName().equals(type))
+ {
+ e.setKeyStoreType(kt);
+ break;
+ }
+ }
+ map.add(e);
+ }
+ return map;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.service.certificate.CertificateService#
+ * setClientAuthCertificateConfig
+ * (net.java.sip.communicator.service.certificate.CertificateConfigEntry)
+ */
+ public void setClientAuthCertificateConfig(CertificateConfigEntry e)
+ {
+ if (e.getId() == null)
+ e.setId("conf" + Math.abs(new Random().nextInt()));
+ String pn = PNAME_CLIENTAUTH_CERTCONFIG_BASE + "." + e.getId();
+ config.setProperty(pn, e.getId());
+ config.setProperty(pn + ".alias", e.getAlias());
+ config.setProperty(pn + ".displayName", e.getDisplayName());
+ config.setProperty(pn + ".keyStore", e.getKeyStore());
+ config.setProperty(pn + ".savePassword", e.isSavePassword());
+ if (e.isSavePassword())
+ credService.storePassword(pn, e.getKeyStorePassword());
+ else
+ credService.removePassword(pn);
+ config.setProperty(pn + ".keyStoreType", e.getKeyStoreType());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.service.certificate.CertificateService#
+ * removeClientAuthCertificateConfig(java.lang.String)
+ */
+ public void removeClientAuthCertificateConfig(String id)
+ {
+ for (String p : config.getPropertyNamesByPrefix(
+ PNAME_CLIENTAUTH_CERTCONFIG_BASE + "." + id, true))
+ {
+ config.removeProperty(p);
+ }
+ config.removeProperty(PNAME_CLIENTAUTH_CERTCONFIG_BASE + "." + id);
+ }
+
+ // ------------------------------------------------------------------------
+ // Certificate trust handling
+ // ------------------------------------------------------------------------
/*
* (non-Javadoc)
*
@@ -157,18 +323,140 @@ public class CertificateServiceImpl
kmFactory.init(ks, keyStorePassword == null ? null
: keyStorePassword.toCharArray());
+ return getSSLContext(kmFactory.getKeyManagers(), trustManager);
+ }
+ catch (Exception e)
+ {
+ throw new GeneralSecurityException("Cannot init SSLContext", e);
+ }
+ }
+
+ private Builder loadKeyStore(final CertificateConfigEntry entry)
+ throws KeyStoreException
+ {
+ final File f = new File(entry.getKeyStore());
+ final KeyStoreType kt = entry.getKeyStoreType();
+ if ("PKCS11".equals(kt.getName()))
+ {
+ String config =
+ "name=" + f.getName() + "\nlibrary=" + f.getAbsoluteFile();
+ Provider p =
+ new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(
+ config.getBytes()));
+ Security.insertProviderAt(p, 0);
+ }
+ KeyStore.Builder ksBuilder =
+ KeyStore.Builder.newInstance(kt.getName(), null, f,
+ new KeyStore.CallbackHandlerProtection(new CallbackHandler()
+ {
+ public void handle(Callback[] callbacks)
+ throws IOException,
+ UnsupportedCallbackException
+ {
+ for (Callback cb : callbacks)
+ {
+ if (!(cb instanceof PasswordCallback))
+ throw new UnsupportedCallbackException(cb);
+
+ PasswordCallback pwcb = (PasswordCallback) cb;
+ if (entry.isSavePassword())
+ {
+ pwcb.setPassword(entry.getKeyStorePassword()
+ .toCharArray());
+ return;
+ }
+ else
+ {
+ AuthenticationWindow aw =
+ new AuthenticationWindow(
+ null,
+ f.getName(),
+ null,
+ kt.getName(),
+ false,
+ null
+ );
+ aw.setAllowSavePassword(false);
+ aw.setVisible(true);
+ if (!aw.isCanceled())
+ pwcb.setPassword(aw.getPassword());
+ else
+ throw new IOException("User cancel");
+ }
+ }
+ }
+ }));
+ return ksBuilder;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.service.certificate.CertificateService#
+ * getSSLContext(java.lang.String, javax.net.ssl.X509TrustManager)
+ */
+ public SSLContext getSSLContext(String clientCertConfig,
+ X509TrustManager trustManager)
+ throws GeneralSecurityException
+ {
+ try
+ {
+ if(clientCertConfig == null)
+ return getSSLContext(trustManager);
+ CertificateConfigEntry entry = null;
+ for (CertificateConfigEntry e : getClientAuthCertificateConfigs())
+ {
+ if (e.getId().equals(clientCertConfig))
+ {
+ entry = e;
+ break;
+ }
+ }
+ if (entry == null)
+ throw new GeneralSecurityException(
+ "Client certificate config with id <"
+ + clientCertConfig
+ + "> not found."
+ );
+
+ final KeyManagerFactory kmf =
+ KeyManagerFactory.getInstance("NewSunX509");
+ kmf.init(new KeyStoreBuilderParameters(loadKeyStore(entry)));
+
+ return getSSLContext(kmf.getKeyManagers(), trustManager);
+ }
+ catch (Exception e)
+ {
+ throw new GeneralSecurityException("Cannot init SSLContext", e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.service.certificate.CertificateService#
+ * getSSLContext(javax.net.ssl.KeyManager[], javax.net.ssl.X509TrustManager)
+ */
+ public SSLContext getSSLContext(KeyManager[] keyManagers,
+ X509TrustManager trustManager)
+ throws GeneralSecurityException
+ {
+ try
+ {
//TODO: inject our own socket factory to use our own DNS stuff
SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(kmFactory.getKeyManagers(), new TrustManager[]
- { trustManager }, null);
+ sslContext.init(
+ keyManagers,
+ new TrustManager[] { trustManager },
+ null
+ );
return sslContext;
}
catch (Exception e)
{
- throw new GeneralSecurityException("Cannot init SSLContext: "
- + e.getMessage());
+ throw new GeneralSecurityException("Cannot init SSLContext", e);
}
}
@@ -663,7 +951,7 @@ public class CertificateServiceImpl
* @return The SHA-1 hash of the certificate.
* @throws CertificateException
*/
- static String getThumbprint(Certificate cert, String algorithm)
+ private static String getThumbprint(Certificate cert, String algorithm)
throws CertificateException
{
MessageDigest digest;
diff --git a/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java b/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java
index 827cf67..c358ec9 100644
--- a/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java
+++ b/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java
@@ -8,6 +8,7 @@ package net.java.sip.communicator.impl.certificate;
import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.credentialsstorage.*;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
@@ -43,6 +44,11 @@ public class CertificateVerificationActivator
private static ResourceManagementService resourcesService;
/**
+ * The service to store and access passwords.
+ */
+ private static CredentialsStorageService credService;
+
+ /**
* Called when this bundle is started.
*
* @param bc The execution context of the bundle being started.
@@ -125,4 +131,23 @@ public class CertificateVerificationActivator
}
return resourcesService;
}
+
+ /**
+ * Returns the <tt>CredentialsStorageService</tt>, through which we will
+ * access all passwords.
+ *
+ * @return the <tt>CredentialsStorageService</tt>, through which we will
+ * access all passwords.
+ */
+ public static CredentialsStorageService getCredService()
+ {
+ if (credService == null)
+ {
+ credService
+ = ServiceUtils.getService(
+ bundleContext,
+ CredentialsStorageService.class);
+ }
+ return credService;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java b/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java
index c5cdbe1..8c7a0f9 100644
--- a/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java
+++ b/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java
@@ -9,13 +9,7 @@ package net.java.sip.communicator.impl.certificate;
import java.awt.*;
import java.awt.event.*;
import java.security.cert.*;
-import java.security.interfaces.*;
-import java.text.*;
-import java.util.Formatter;
-import javax.naming.*;
-import javax.naming.ldap.*;
-import javax.security.auth.x500.*;
import javax.swing.*;
import net.java.sip.communicator.service.resources.*;
@@ -36,12 +30,6 @@ class VerifyCertificateDialog
.getResources();
/**
- * Date formatter.
- */
- private DateFormat dateFormatter = DateFormat
- .getDateInstance(DateFormat.MEDIUM);
-
- /**
* The maximum width that we allow message dialogs to have.
*/
private static final int MAX_MSG_PANE_WIDTH = 600;
@@ -231,7 +219,7 @@ class VerifyCertificateDialog
Component certInfoPane = null;
if(cert instanceof X509Certificate)
{
- certInfoPane = getX509DisplayComponent((X509Certificate)cert);
+ certInfoPane = new X509CertificatePanel((X509Certificate)cert);
}
else
{
@@ -293,367 +281,4 @@ class VerifyCertificateDialog
{
actionCancel();
}
-
- /**
- * Returns the display component for X509 certificate.
- *
- * @param certificate the certificate to show
- * @return the created component
- */
- private Component getX509DisplayComponent(
- X509Certificate certificate)
- {
- Insets valueInsets = new Insets(2,10,0,0);
- Insets titleInsets = new Insets(10,5,0,0);
-
- TransparentPanel certDisplayPanel
- = new TransparentPanel(new GridBagLayout());
-
- int currentRow = 0;
-
- GridBagConstraints constraints = new GridBagConstraints();
- constraints.anchor = GridBagConstraints.WEST;
- constraints.fill = GridBagConstraints.HORIZONTAL;
- constraints.insets = new Insets(2,5,0,0);
- constraints.gridx = 0;
- constraints.weightx = 0;
- constraints.weighty = 0;
- constraints.gridy = currentRow++;
-
- X500Principal issuer = certificate.getIssuerX500Principal();
- X500Principal subject = certificate.getSubjectX500Principal();
-
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_ISSUED_TO")),
- constraints);
-
- // subject
- constraints.insets = valueInsets;
- try
- {
- for(Rdn name : new LdapName(subject.getName()).getRdns())
- {
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- String lbl =
- R.getI18NString("service.gui.CERT_INFO_" + name.getType());
- if (lbl
- .equals("!service.gui.CERT_INFO_" + name.getType() + "!"))
- lbl = name.getType();
- certDisplayPanel.add(new JLabel(lbl), constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(
- name.getValue() instanceof byte[] ?
- getHex((byte[])name.getValue()) + " ("
- + new String((byte[]) name.getValue()) + ")"
- : name.getValue().toString()),
- constraints);
- }
- }
- catch (InvalidNameException e1)
- {
- constraints.gridy = currentRow++;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_CN")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(subject.getName()),
- constraints);
- }
-
- // issuer
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- constraints.insets = titleInsets;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_ISSUED_BY")),
- constraints);
- constraints.insets = valueInsets;
- try
- {
- for(Rdn name : new LdapName(issuer.getName()).getRdns())
- {
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- constraints.gridx = 0;
- String lbl =
- R.getI18NString("service.gui.CERT_INFO_" + name.getType());
- if (lbl
- .equals("!service.gui.CERT_INFO_" + name.getType() + "!"))
- lbl = name.getType();
- certDisplayPanel.add(new JLabel(lbl), constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(
- name.getValue() instanceof byte[] ?
- getHex((byte[])name.getValue()) + " ("
- + new String((byte[]) name.getValue()) + ")"
- : name.getValue().toString()),
- constraints);
- }
- }
- catch (InvalidNameException e1)
- {
- constraints.gridy = currentRow++;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_CN")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(issuer.getName()),
- constraints);
- }
-
- // validity
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- constraints.insets = titleInsets;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_VALIDITY")),
- constraints);
- constraints.insets = valueInsets;
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_ISSUED_ON")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(dateFormatter.format(certificate.getNotBefore())),
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_EXPIRES_ON")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(dateFormatter.format(certificate.getNotAfter())),
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- constraints.insets = titleInsets;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_FINGERPRINTS")),
- constraints);
- constraints.insets = valueInsets;
-
- try
- {
- String sha1String = CertificateServiceImpl.getThumbprint(certificate, "SHA1");
- String md5String = CertificateServiceImpl.getThumbprint(certificate, "MD5");
-
- JTextArea sha1Area = new JTextArea(sha1String);
- sha1Area.setLineWrap(false);
- sha1Area.setOpaque(false);
- sha1Area.setWrapStyleWord(true);
- sha1Area.setEditable(false);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel("SHA1:"),
- constraints);
-
- constraints.gridx = 1;
- certDisplayPanel.add(
- sha1Area,
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel("MD5:"),
- constraints);
-
- JTextArea md5Area = new JTextArea(md5String);
- md5Area.setLineWrap(false);
- md5Area.setOpaque(false);
- md5Area.setWrapStyleWord(true);
- md5Area.setEditable(false);
-
- constraints.gridx = 1;
- certDisplayPanel.add(
- md5Area,
- constraints);
- }
- catch (Exception e)
- {
- // do nothing as we cannot show this value
- }
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- constraints.insets = titleInsets;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_CERT_DETAILS")),
- constraints);
- constraints.insets = valueInsets;
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_SER_NUM")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(certificate.getSerialNumber().toString()),
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_VER")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(String.valueOf(certificate.getVersion())),
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_SIGN_ALG")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(String.valueOf(certificate.getSigAlgName())),
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- constraints.insets = titleInsets;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_PUB_KEY_INFO")),
- constraints);
- constraints.insets = valueInsets;
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_ALG")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(certificate.getPublicKey().getAlgorithm()),
- constraints);
-
- if(certificate.getPublicKey().getAlgorithm().equals("RSA"))
- {
- RSAPublicKey key = (RSAPublicKey)certificate.getPublicKey();
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_PUB_KEY")),
- constraints);
-
- JTextArea pubkeyArea = new JTextArea(
- R.getI18NString(
- "service.gui.CERT_INFO_KEY_BYTES_PRINT",
- new String[]{
- String.valueOf(key.getModulus().toByteArray().length - 1),
- key.getModulus().toString(16)
- }));
- pubkeyArea.setLineWrap(false);
- pubkeyArea.setOpaque(false);
- pubkeyArea.setWrapStyleWord(true);
- pubkeyArea.setEditable(false);
-
- constraints.gridx = 1;
- certDisplayPanel.add(
- pubkeyArea,
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_EXP")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(key.getPublicExponent().toString()),
- constraints);
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_KEY_SIZE")),
- constraints);
- constraints.gridx = 1;
- certDisplayPanel.add(
- new JLabel(R.getI18NString(
- "service.gui.CERT_INFO_KEY_BITS_PRINT",
- new String[]{
- String.valueOf(key.getModulus().bitLength())})),
- constraints);
- }
- else if(certificate.getPublicKey().getAlgorithm().equals("DSA"))
- {
- DSAPublicKey key =
- (DSAPublicKey)certificate.getPublicKey();
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel("Y:"), constraints);
-
- JTextArea yArea = new JTextArea(key.getY().toString(16));
- yArea.setLineWrap(false);
- yArea.setOpaque(false);
- yArea.setWrapStyleWord(true);
- yArea.setEditable(false);
-
- constraints.gridx = 1;
- certDisplayPanel.add(
- yArea,
- constraints);
- }
-
- constraints.gridy = currentRow++;
- constraints.gridx = 0;
- certDisplayPanel.add(new JLabel(
- R.getI18NString("service.gui.CERT_INFO_SIGN")),
- constraints);
-
- JTextArea signArea = new JTextArea(
- R.getI18NString(
- "service.gui.CERT_INFO_KEY_BYTES_PRINT",
- new String[]{
- String.valueOf(certificate.getSignature().length),
- getHex(certificate.getSignature())
- }));
- signArea.setLineWrap(false);
- signArea.setOpaque(false);
- signArea.setWrapStyleWord(true);
- signArea.setEditable(false);
-
- constraints.gridx = 1;
- certDisplayPanel.add(
- signArea,
- constraints);
-
- return certDisplayPanel;
- }
-
- /**
- * Converts the byte array to hex string.
- * @param raw the data.
- * @return the hex string.
- */
- public String getHex( byte [] raw )
- {
- if (raw == null)
- return null;
-
- StringBuilder hex = new StringBuilder(2 * raw.length);
- Formatter f = new Formatter(hex);
- for (byte b : raw)
- {
- f.format("%02x", b);
- }
- return hex.toString();
- }
}
diff --git a/src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf b/src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf
index c9c5ed3..84d9a25 100644
--- a/src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf
+++ b/src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf
@@ -7,16 +7,19 @@ System-Bundle: yes
Export-Package: net.java.sip.communicator.service.certificate
Import-Package: org.osgi.framework,
net.java.sip.communicator.util,
- net.java.sip.communicator.service.resources,
- net.java.sip.communicator.service.fileaccess,
- net.java.sip.communicator.service.configuration,
net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.credentialsstorage,
+ net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.service.httputil,
+ net.java.sip.communicator.service.resources,
javax.net.ssl,
+ javax.security.auth.callback,
javax.security.auth.x500,
javax.naming,
javax.naming.ldap,
javax.swing,
org.apache.http.conn.ssl,
org.bouncycastle.asn1,
- org.bouncycastle.asn1.x509 \ No newline at end of file
+ org.bouncycastle.asn1.x509,
+ sun.security.pkcs11
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
index 348c17a..873ba1e 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
@@ -255,7 +255,9 @@ public class SipRegistrarConnection
|| exc.getCause() instanceof IOException
|| exc.getCause() instanceof SSLHandshakeException)
{
- if(exc.getCause().getCause() instanceof CertificateException)
+ if(exc.getCause().getCause() instanceof CertificateException
+ || exc.getCause().getMessage()
+ .startsWith("Received fatal alert"))
{
setRegistrationState(RegistrationState.UNREGISTERED
, RegistrationStateChangeEvent.REASON_USER_REQUEST
@@ -926,6 +928,23 @@ public class SipRegistrarConnection
}
else
{
+ //if we're in certificate authentication mode, a 403 probably
+ //means that the certificate didn't match. stop connecting.
+ String certId = sipProvider.getAccountID()
+ .getAccountPropertyString(
+ ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE);
+ if(certId != null)
+ {
+ //tell the others we couldn't register
+ this.setRegistrationState(
+ RegistrationState.AUTHENTICATION_FAILED,
+ RegistrationStateChangeEvent
+ .REASON_NON_EXISTING_USER_ID,
+ "We failed to authenticate with the server."
+ );
+ return;
+ }
+
//we got a BAD PASSWD reply. send a new credential-less request
//in order to trigger a new challenge and rerequest a password.
retryTran = sipProvider.getSipSecurityManager()
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
index c955f06..18aab95 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
@@ -210,6 +210,8 @@ public class SslNetworkLayer
}
return certificateVerification.getSSLContext(
+ (String)id.getAccountProperty(
+ ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE),
certificateVerification.getTrustManager(
identities,
null,
diff --git a/src/net/java/sip/communicator/plugin/certconfig/CertConfigActivator.java b/src/net/java/sip/communicator/plugin/certconfig/CertConfigActivator.java
new file mode 100644
index 0000000..4dbf8b4
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/certconfig/CertConfigActivator.java
@@ -0,0 +1,111 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.certconfig;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.credentialsstorage.CredentialsStorageService;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * OSGi Activator for the Certificate Configuration Advanced Form.
+ *
+ * @author Ingo Bauersachs
+ */
+public class CertConfigActivator
+ implements BundleActivator
+{
+ private static BundleContext bundleContext;
+ static ResourceManagementService R;
+
+ public void start(BundleContext bc) throws Exception
+ {
+ bundleContext = bc;
+ Dictionary<String, String> properties = new Hashtable<String, String>();
+ properties.put(ConfigurationForm.FORM_TYPE,
+ ConfigurationForm.ADVANCED_TYPE);
+
+ R = ServiceUtils.getService(bc, ResourceManagementService.class);
+
+ bc.registerService(ConfigurationForm.class.getName(),
+ new LazyConfigurationForm(
+ CertConfigPanel.class.getName(),
+ getClass().getClassLoader(),
+ null,
+ "plugin.certconfig.TITLE",
+ 2000,
+ true),
+ properties
+ );
+ }
+
+ public void stop(BundleContext arg0) throws Exception
+ {
+ }
+
+ static BundleContext getBundleContext()
+ {
+ return bundleContext;
+ }
+
+ /**
+ * Returns a reference to a ConfigurationService implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found.
+ *
+ * @return a currently valid implementation of the ConfigurationService.
+ */
+ public static ConfigurationService getConfigService()
+ {
+ return ServiceUtils.getService(bundleContext,
+ ConfigurationService.class);
+ }
+
+ /**
+ * Returns a reference to a CertificateService implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found.
+ *
+ * @return a currently valid implementation of the CertificateService.
+ */
+ public static CertificateService getCertService()
+ {
+ return ServiceUtils.getService(bundleContext, CertificateService.class);
+ }
+
+ /**
+ * Returns a reference to a UIService implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found.
+ *
+ * @return a currently valid implementation of the UIService.
+ */
+ public static UIService getUIService()
+ {
+ return ServiceUtils.getService(bundleContext, UIService.class);
+ }
+
+ /**
+ * Returns a reference to a CredentialsStorageService implementation
+ * currently registered in the bundle context or null if no such
+ * implementation was found.
+ *
+ * @return a currently valid implementation of the
+ * CredentialsStorageService.
+ */
+ public static CredentialsStorageService getCredService()
+ {
+ return ServiceUtils.getService(bundleContext,
+ CredentialsStorageService.class);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java b/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java
new file mode 100644
index 0000000..4d56958
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java
@@ -0,0 +1,548 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.certconfig;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.security.*;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+import javax.security.auth.callback.*;
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * Dialog window to add/edit client certificate configuration entries.
+ *
+ * @author Ingo Bauersachs
+ */
+public class CertConfigEntryDialog
+ extends SIPCommDialog
+ implements ActionListener, ItemListener, ChangeListener
+{
+ // ------------------------------------------------------------------------
+ // Fields and services
+ // ------------------------------------------------------------------------
+ private static final long serialVersionUID = 8361336563239745007L;
+ private static final Logger logger = Logger
+ .getLogger(CertConfigEntryDialog.class);
+ private ResourceManagementService R = CertConfigActivator.R;
+ private CertificateService cs = CertConfigActivator.getCertService();
+ private CertificateConfigEntry entry;
+ private boolean success = false;
+
+ // ------------------------------------------------------------------------
+ // GUI members
+ // ------------------------------------------------------------------------
+ private JButton cmdOk;
+ private JButton cmdCancel;
+ private JButton cmdBrowse;
+ private JTextField txtDisplayName;
+ private JTextField txtKeyStore;
+ private JComboBox cboKeyStoreTypes;
+ private JCheckBox chkSavePassword;
+ private JPasswordField txtKeyStorePassword;
+ private JComboBox cboAlias;
+ private JButton cmdShowCert;
+ private KeyStore keyStore;
+
+ // ------------------------------------------------------------------------
+ // Initialization
+ // ------------------------------------------------------------------------
+ public CertConfigEntryDialog(CertificateConfigEntry e)
+ {
+ super(false);
+ entry = e;
+ initComponents();
+ setPreferredSize(new Dimension(650, 270));
+
+ try
+ {
+ if(entry.getKeyStore() != null)
+ {
+ txtKeyStorePassword.setText(entry.getKeyStorePassword());
+ chkSavePassword.setSelected(entry.isSavePassword());
+ cboKeyStoreTypes.setEnabled(true);
+ cboKeyStoreTypes.setSelectedItem(entry.getKeyStoreType());
+ keyStore = loadKeyStore();
+ cboAlias.setEnabled(true);
+ loadAliases();
+ cboAlias.setSelectedItem(entry.getAlias());
+ }
+ }
+ catch (KeyStoreException ex)
+ {
+ logger.error("Unable to load all data", ex);
+ showGenericError("plugin.certconfig.KEYSTORE_EXCEPTION", ex);
+ }
+ catch (ProviderException ex)
+ {
+ logger.error("Unable to load all data", ex);
+ showGenericError("plugin.certconfig.KEYSTORE_EXCEPTION", ex);
+ }
+ }
+
+ private void initComponents()
+ {
+ setTitle(R.getI18NString("plugin.certconfig.EDIT_ENTRY"));
+ setLayout(new BorderLayout());
+ JPanel fields = new TransparentPanel();
+ fields.setLayout(new GridBagLayout());
+
+ JLabel lblDisplayName = new JLabel();
+ lblDisplayName.setText(R.getI18NString("service.gui.DISPLAY_NAME"));
+ txtDisplayName = new JTextField();
+ txtDisplayName.setText(entry.getDisplayName());
+
+ JLabel lblKeyStore = new JLabel();
+ lblKeyStore.setText(R.getI18NString("plugin.certconfig.KEYSTORE"));
+ txtKeyStore = new JTextField();
+ txtKeyStore.setText(entry.getKeyStore());
+ txtKeyStore.setEditable(false);
+
+ cmdBrowse = new JButton();
+ cmdBrowse.setText(R.getI18NString("service.gui.BROWSE"));
+ cmdBrowse.addActionListener(this);
+
+ JLabel lblKeyStorePassword = new JLabel();
+ lblKeyStorePassword.setText(
+ R.getI18NString("plugin.certconfig.KEYSTORE_PASSWORD"));
+ txtKeyStorePassword = new JPasswordField();
+ txtKeyStorePassword.setEditable(false);
+
+ chkSavePassword = new SIPCommCheckBox();
+ chkSavePassword.setText(
+ R.getI18NString("service.gui.REMEMBER_PASSWORD"));
+ chkSavePassword.addChangeListener(this);
+ chkSavePassword.setEnabled(false);
+
+ JLabel lblKeyStoreType = new JLabel();
+ lblKeyStoreType.setText(
+ R.getI18NString("plugin.certconfig.KEYSTORE_TYPE"));
+ cboKeyStoreTypes =
+ new JComboBox(cs.getSupportedKeyStoreTypes().toArray());
+ cboKeyStoreTypes.addItemListener(this);
+ cboKeyStoreTypes.setEnabled(false);
+
+ JLabel lblAlias = new JLabel();
+ lblAlias.setText(R.getI18NString("plugin.certconfig.ALIAS"));
+ cboAlias = new JComboBox();
+ cboAlias.addItemListener(this);
+ cboAlias.setEnabled(false);
+
+ cmdShowCert = new JButton();
+ cmdShowCert.setText(R.getI18NString("service.gui.SHOW_CERT") + "...");
+ cmdShowCert.addActionListener(this);
+ cmdShowCert.setEnabled(false);
+
+ cmdCancel = new JButton();
+ cmdCancel.setText(R.getI18NString("service.gui.CANCEL"));
+ cmdCancel.addActionListener(this);
+
+ cmdOk = new JButton();
+ cmdOk.setText(R.getI18NString("service.gui.OK"));
+ cmdOk.addActionListener(this);
+ cmdOk.setPreferredSize(cmdCancel.getPreferredSize());
+
+ TransparentPanel buttons = new TransparentPanel();
+ buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ buttons.add(cmdOk);
+ buttons.add(cmdCancel);
+
+ GridBagConstraints first = new GridBagConstraints();
+ first.gridx = 0;
+ first.gridy = 0;
+ first.weightx = 0;
+ first.anchor = GridBagConstraints.LINE_START;
+ first.gridwidth = 1;
+ first.insets = new Insets(2,4,2,4);
+ first.fill = GridBagConstraints.HORIZONTAL;
+
+ GridBagConstraints second = new GridBagConstraints();
+ second.gridx = 1;
+ second.gridy = 0;
+ second.weightx = 2;
+ second.anchor = GridBagConstraints.LINE_START;
+ second.gridwidth = 1; //GridBagConstraints.REMAINDER;
+ second.insets = first.insets;
+ second.fill = GridBagConstraints.HORIZONTAL;
+
+ GridBagConstraints third = new GridBagConstraints();
+ third.gridx = 2;
+ third.gridy = 0;
+ third.weightx = 1;
+ third.anchor = GridBagConstraints.LINE_END;
+ third.gridwidth = 1;
+ third.insets = first.insets;
+ third.fill = GridBagConstraints.HORIZONTAL;
+
+ fields.add(lblDisplayName, first);
+ fields.add(txtDisplayName, second);
+
+ first.gridy = second.gridy = ++third.gridy;
+ fields.add(lblKeyStore, first);
+ fields.add(txtKeyStore, second);
+ fields.add(cmdBrowse, third);
+
+ first.gridy = second.gridy = ++third.gridy;
+ fields.add(lblKeyStoreType, first);
+ fields.add(cboKeyStoreTypes, second);
+
+ first.gridy = second.gridy = ++third.gridy;
+ fields.add(lblKeyStorePassword, first);
+ fields.add(txtKeyStorePassword, second);
+
+ first.gridy = second.gridy = ++third.gridy;
+ fields.add(chkSavePassword, second);
+
+ first.gridy = second.gridy = ++third.gridy;
+ fields.add(lblAlias, first);
+ fields.add(cboAlias, second);
+ fields.add(cmdShowCert, third);
+
+ add(fields, BorderLayout.CENTER);
+ add(buttons, BorderLayout.SOUTH);
+ }
+
+ // ------------------------------------------------------------------------
+ // Event handling
+ // ------------------------------------------------------------------------
+ protected void close(boolean escaped)
+ {
+ cmdCancel.doClick();
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ if(e.getSource() == cmdOk)
+ {
+ if(cboAlias.getSelectedItem() == null
+ || StringUtils.isNullOrEmpty(txtDisplayName.getText())
+ || StringUtils.isNullOrEmpty(txtKeyStore.getText()))
+ {
+ JOptionPane.showMessageDialog(this,
+ R.getI18NString("plugin.certconfig.INCOMPLETE"),
+ R.getI18NString("service.gui.ERROR"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ entry.setAlias(cboAlias.getSelectedItem().toString());
+ entry.setDisplayName(txtDisplayName.getText());
+ entry.setSavePassword(chkSavePassword.isSelected());
+ entry.setKeyStorePassword(
+ new String(txtKeyStorePassword.getPassword()));
+ entry.setKeyStoreType(
+ (KeyStoreType) cboKeyStoreTypes.getSelectedItem());
+ entry.setKeyStore(txtKeyStore.getText());
+ success = true;
+ dispose();
+ }
+ if(e.getSource() == cmdCancel)
+ {
+ dispose();
+ }
+ if(e.getSource() == cmdBrowse)
+ {
+ browseKeyStore();
+ }
+ if(e.getSource() == cmdShowCert)
+ {
+ showSelectedCertificate();
+ }
+ }
+
+ private void showSelectedCertificate()
+ {
+ try
+ {
+ @SuppressWarnings("serial")
+ SIPCommDialog dlg = new SIPCommDialog(this, false)
+ {
+ private JButton cmdClose;
+ {
+ setTitle(cboAlias.getSelectedItem().toString());
+ setLayout(new BorderLayout());
+ final JScrollPane certScroll =
+ new JScrollPane(new X509CertificatePanel(
+ (X509Certificate) keyStore.getCertificate(cboAlias
+ .getSelectedItem().toString())));
+ certScroll.setPreferredSize(new Dimension(600, 300));
+ certScroll.getVerticalScrollBar().setValue(0);
+ add(certScroll, BorderLayout.CENTER);
+
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ certScroll.getVerticalScrollBar().setValue(0);
+ }
+ });
+
+ cmdClose = new JButton();
+ cmdClose.setText(R.getI18NString("service.gui.CLOSE"));
+ cmdClose.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ dispose();
+ }
+ });
+
+ TransparentPanel buttons =
+ new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+ buttons.add(cmdClose);
+ add(buttons, BorderLayout.SOUTH);
+
+ setLocationRelativeTo(cmdShowCert);
+ }
+
+ protected void close(boolean escaped)
+ {
+ cmdClose.doClick();
+ }
+ };
+ dlg.setModal(true);
+ dlg.setVisible(true);
+ }
+ catch (KeyStoreException e1)
+ {
+ logger.error("Unable to show the selected certificate", e1);
+ showGenericError("plugin.certconfig.SHOW_CERT_EXCEPTION", e1);
+ }
+ }
+
+ /**
+ * Opens a FileChoserDialog to let the user pick a keystore and tries to
+ * auto-detect the keystore type using the file extension
+ */
+ private void browseKeyStore()
+ {
+ SipCommFileChooser dlg =
+ GenericFileDialog.create(null,
+ R.getI18NString("plugin.certconfig.BROWSE_KEYSTORE"),
+ SipCommFileChooser.LOAD_FILE_OPERATION);
+ dlg.setSelectionMode(SipCommFileChooser.FILES_ONLY);
+ dlg.addFilter(new SipCommFileFilter()
+ {
+ public String getDescription()
+ {
+ return R
+ .getI18NString("plugin.certconfig.FILE_TYPE_DESCRIPTION");
+ }
+
+ public boolean accept(File f)
+ {
+ for(KeyStoreType kt : cs.getSupportedKeyStoreTypes())
+ for(String ext : kt.getFileExtensions())
+ if(f.getName().endsWith(ext))
+ return true;
+
+ return false;
+ }
+ });
+ File f = dlg.getFileFromDialog();
+ if(f != null)
+ {
+ cboKeyStoreTypes.setEnabled(true);
+ cboKeyStoreTypes.setSelectedItem(null);
+ cboAlias.setEnabled(true);
+
+ txtKeyStore.setText(f.getAbsolutePath());
+ for(KeyStoreType kt: cs.getSupportedKeyStoreTypes())
+ for(String ext : kt.getFileExtensions())
+ if(f.getName().endsWith(ext))
+ cboKeyStoreTypes.setSelectedItem(kt);
+ }
+ }
+
+ /**
+ * Open the keystore selected by the user. If the type is set as PKCS#11,
+ * the file is loaded as a provider. If the store is protected by a
+ * password, the user is being asked by an authentication dialog.
+ *
+ * @return The loaded keystore
+ * @throws KeyStoreException when something goes wrong
+ */
+ private KeyStore loadKeyStore() throws KeyStoreException
+ {
+ final File f = new File(txtKeyStore.getText());
+ final KeyStoreType kt =
+ (KeyStoreType) cboKeyStoreTypes.getSelectedItem();
+ if("PKCS11".equals(kt.getName()))
+ {
+ String config =
+ "name=" + f.getName() + "\nlibrary=" + f.getAbsoluteFile();
+ Provider p =
+ new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(
+ config.getBytes()));
+ Security.insertProviderAt(p, 0);
+ }
+ KeyStore.Builder ksBuilder = KeyStore.Builder.newInstance(
+ kt.getName(),
+ null,
+ f,
+ new KeyStore.CallbackHandlerProtection(new CallbackHandler()
+ {
+ public void handle(Callback[] callbacks)
+ throws IOException,
+ UnsupportedCallbackException
+ {
+ for(Callback cb : callbacks)
+ {
+ if(!(cb instanceof PasswordCallback))
+ throw new UnsupportedCallbackException(cb);
+ PasswordCallback pwcb = (PasswordCallback)cb;
+ if(
+ (
+ txtKeyStorePassword.getPassword() != null
+ && txtKeyStorePassword.getPassword().length>0
+ )
+ || chkSavePassword.isSelected())
+ {
+ pwcb.setPassword(txtKeyStorePassword.getPassword());
+ return;
+ }
+ AuthenticationWindow aw = new AuthenticationWindow(
+ CertConfigEntryDialog.this,
+ f.getName(),
+ null,
+ kt.getName(),
+ false,
+ null
+ );
+ aw.setAllowSavePassword(!"PKCS11".equals(kt.getName()));
+ aw.setVisible(true);
+ if(!aw.isCanceled())
+ {
+ pwcb.setPassword(aw.getPassword());
+ if (!"PKCS11".equals(kt.getName())
+ && aw.isRememberPassword())
+ {
+ txtKeyStorePassword.setText(new String(aw
+ .getPassword()));
+ }
+ chkSavePassword.setSelected(aw
+ .isRememberPassword());
+ }
+ else
+ throw new IOException("User cancel");
+ }
+ }
+ }));
+ return ksBuilder.getKeyStore();
+ }
+
+ /**
+ * Load the certificate entry aliases from the chosen keystore.
+ */
+ private void loadAliases()
+ {
+ String currentDisplayName = txtDisplayName.getText();
+ String currentAlias =
+ cboAlias.getSelectedItem() == null ? null : cboAlias
+ .getSelectedItem().toString();
+ try
+ {
+ cboAlias.removeAllItems();
+ Enumeration<String> e = keyStore.aliases();
+ while(e.hasMoreElements())
+ {
+ cboAlias.addItem(e.nextElement());
+ }
+ // if the display name is empty or identical to the alias, set it
+ // to the alias of the newly selected cert
+ if(
+ (
+ StringUtils.isNullOrEmpty(currentDisplayName)
+ || (
+ currentDisplayName != null
+ && currentDisplayName.equals(currentAlias)
+ )
+ )
+ && cboAlias.getSelectedItem() != null)
+ {
+ txtDisplayName.setText(cboAlias.getSelectedItem().toString());
+ }
+
+ }
+ catch (KeyStoreException e)
+ {
+ cboAlias.removeAllItems();
+ logger.error("Unable to obtain aliases from keystore", e);
+ showGenericError("plugin.certconfig.ALIAS_LOAD_EXCEPTION", e);
+ }
+ }
+
+ private void showGenericError(String msg, Throwable e)
+ {
+ JOptionPane.showMessageDialog(
+ this,
+ R.getI18NString(msg, new String[]{e.getMessage()}),
+ R.getI18NString("service.gui.ERROR"),
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
+
+ public boolean showDialog()
+ {
+ setModal(true);
+ setVisible(true);
+ setVisible(false);
+ return success;
+ }
+
+ public void itemStateChanged(ItemEvent e)
+ {
+ if(e.getStateChange() != ItemEvent.SELECTED)
+ return;
+ if(e.getSource() == cboKeyStoreTypes)
+ {
+ KeyStoreType kt = (KeyStoreType)cboKeyStoreTypes.getSelectedItem();
+ if(kt == null)
+ return;
+ try
+ {
+ if(!"PKCS11".equals(kt.getName()))
+ chkSavePassword.setEnabled(true);
+ txtKeyStorePassword.setEditable(kt.hasKeyStorePassword()
+ && chkSavePassword.isSelected());
+
+ keyStore = loadKeyStore();
+ loadAliases();
+ }
+ catch (KeyStoreException ex)
+ {
+ cboAlias.removeAllItems();
+ showGenericError("plugin.certconfig.INVALID_KEYSTORE_TYPE", ex);
+ }
+ }
+ if(e.getSource() == cboAlias)
+ {
+ cmdShowCert.setEnabled(cboAlias.getSelectedItem() != null);
+ }
+ }
+
+ public void stateChanged(ChangeEvent e)
+ {
+ if(e.getSource() == chkSavePassword)
+ {
+ txtKeyStorePassword.setEditable(
+ chkSavePassword.isSelected()
+ && ((KeyStoreType) cboKeyStoreTypes.getSelectedItem())
+ .hasKeyStorePassword()
+ );
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java b/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java
new file mode 100644
index 0000000..0c4dd41
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java
@@ -0,0 +1,215 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.certconfig;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.*;
+
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * Advanced configuration form to define client TLS certificate templates.
+ *
+ * @author Ingo Bauersachs
+ */
+public class CertConfigPanel
+ extends TransparentPanel
+ implements ConfigurationForm, ActionListener, ListSelectionListener
+{
+ // ------------------------------------------------------------------------
+ // Fields
+ // ------------------------------------------------------------------------
+ private static final long serialVersionUID = 2324122652952574574L;
+ private ResourceManagementService R;
+ private CertConfigTableModel model;
+
+ // ------------------------------------------------------------------------
+ // GUI members
+ // ------------------------------------------------------------------------
+ private JButton cmdAdd;
+ private JButton cmdRemove;
+ private JButton cmdEdit;
+ private JTable tblCertList;
+ private JRadioButton rdoUseWindows;
+ private JRadioButton rdoUseJava;
+
+
+ // ------------------------------------------------------------------------
+ // initialization
+ // ------------------------------------------------------------------------
+ /**
+ * Creates a new instance of this class.
+ */
+ public CertConfigPanel()
+ {
+ R = CertConfigActivator.R;
+ model = new CertConfigTableModel();
+ initComponents();
+ valueChanged(null);
+ }
+
+ private void initComponents()
+ {
+ this.setLayout(new BorderLayout());
+
+ if (OSUtils.IS_WINDOWS)
+ {
+ JPanel pnlCertConfig = new TransparentPanel(new GridLayout(2, 1));
+ pnlCertConfig.setBorder(BorderFactory.createTitledBorder(
+ R.getI18NString("plugin.certconfig.TRUSTSTORE_CONFIG")));
+ add(pnlCertConfig, BorderLayout.NORTH);
+
+ ButtonGroup grpTrustStore = new ButtonGroup();
+
+ rdoUseJava = new SIPCommRadioButton();
+ rdoUseJava.setText(
+ R.getI18NString("plugin.certconfig.JAVA_TRUSTSTORE"));
+ rdoUseJava.addActionListener(this);
+ grpTrustStore.add(rdoUseJava);
+ pnlCertConfig.add(rdoUseJava);
+
+ rdoUseWindows = new SIPCommRadioButton();
+ rdoUseWindows.setText(
+ R.getI18NString("plugin.certconfig.WINDOWS_TRUSTSTORE"));
+ rdoUseWindows.addActionListener(this);
+ grpTrustStore.add(rdoUseWindows);
+ pnlCertConfig.add(rdoUseWindows);
+
+ if ("Windows-ROOT".equals(CertConfigActivator.getConfigService()
+ .getProperty(CertificateService.PNAME_TRUSTSTORE)))
+ {
+ rdoUseWindows.setSelected(true);
+ }
+ else
+ {
+ rdoUseJava.setSelected(true);
+ }
+ }
+
+ JPanel pnlCertList = new TransparentPanel(new BorderLayout());
+ pnlCertList.setBorder(BorderFactory.createTitledBorder(
+ R.getI18NString("plugin.certconfig.CERT_LIST_TITLE")));
+ add(pnlCertList, BorderLayout.CENTER);
+
+ JLabel lblNote = new JLabel();
+ lblNote.setText(
+ R.getI18NString("plugin.certconfig.CERT_LIST_DESCRIPTION"));
+ lblNote.setBorder(new EmptyBorder(7, 7, 7, 7));
+ pnlCertList.add(lblNote, BorderLayout.NORTH);
+
+ tblCertList = new JTable();
+ tblCertList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ tblCertList.getSelectionModel().addListSelectionListener(this);
+ tblCertList.setModel(model);
+ pnlCertList.add(new JScrollPane(tblCertList), BorderLayout.CENTER);
+
+ TransparentPanel buttons = new TransparentPanel();
+ buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ pnlCertList.add(buttons, BorderLayout.SOUTH);
+
+ cmdAdd = new JButton();
+ cmdAdd.setText(R.getI18NString("service.gui.ADD"));
+ cmdAdd.addActionListener(this);
+ buttons.add(cmdAdd);
+
+ cmdRemove = new JButton();
+ cmdRemove.setText(R.getI18NString("service.gui.REMOVE"));
+ cmdRemove.addActionListener(this);
+ buttons.add(cmdRemove);
+
+ cmdEdit = new JButton();
+ cmdEdit.setText(R.getI18NString("service.gui.EDIT"));
+ cmdEdit.addActionListener(this);
+ buttons.add(cmdEdit);
+ }
+
+ // ------------------------------------------------------------------------
+ // event handling
+ // ------------------------------------------------------------------------
+ public void valueChanged(ListSelectionEvent e)
+ {
+ int row = tblCertList.getSelectedRow();
+ cmdRemove.setEnabled(row > -1);
+ cmdEdit.setEnabled(row > -1);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ if (e.getSource() == cmdAdd)
+ {
+ CertificateConfigEntry newEntry = new CertificateConfigEntry();
+ CertConfigEntryDialog dlg = new CertConfigEntryDialog(newEntry);
+ if (dlg.showDialog())
+ CertConfigActivator.getCertService()
+ .setClientAuthCertificateConfig(newEntry);
+ }
+ if (e.getSource() == cmdRemove)
+ {
+ CertConfigActivator.getCertService()
+ .removeClientAuthCertificateConfig(
+ model.getItem(tblCertList.getSelectedRow()).getId());
+ }
+ if (e.getSource() == cmdEdit)
+ {
+ CertificateConfigEntry entry =
+ model.getItem(tblCertList.getSelectedRow());
+ CertConfigEntryDialog dlg = new CertConfigEntryDialog(entry);
+ if (dlg.showDialog())
+ CertConfigActivator.getCertService()
+ .setClientAuthCertificateConfig(entry);
+ }
+ if (e.getSource() == rdoUseJava)
+ {
+ CertConfigActivator.getConfigService().removeProperty(
+ CertificateService.PNAME_TRUSTSTORE);
+ CertConfigActivator.getCredService().removePassword(
+ CertificateService.PNAME_TRUSTSTORE_PASSWORD);
+ }
+ if (e.getSource() == rdoUseWindows)
+ {
+ CertConfigActivator.getConfigService().setProperty(
+ CertificateService.PNAME_TRUSTSTORE, "Windows-ROOT");
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Configuration form members
+ // ------------------------------------------------------------------------
+ public String getTitle()
+ {
+ return CertConfigActivator.R.getI18NString("plugin.certconfig.TITLE");
+ }
+
+ public byte[] getIcon()
+ {
+ return null;
+ }
+
+ public Object getForm()
+ {
+ return this;
+ }
+
+ public int getIndex()
+ {
+ return -1;
+ }
+
+ public boolean isAdvanced()
+ {
+ return true;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/plugin/certconfig/CertConfigTableModel.java b/src/net/java/sip/communicator/plugin/certconfig/CertConfigTableModel.java
new file mode 100644
index 0000000..67aacbf
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/certconfig/CertConfigTableModel.java
@@ -0,0 +1,91 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.certconfig;
+
+import java.beans.*;
+import java.util.*;
+
+import javax.swing.table.*;
+
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.service.resources.*;
+
+/**
+ * Backing data model for a JTable that displays the client certificate
+ * configuration entries.
+ *
+ * @author Ingo Bauersachs
+ */
+public class CertConfigTableModel
+ extends AbstractTableModel
+ implements PropertyChangeListener
+{
+ private static final long serialVersionUID = -6369348252411082340L;
+ private CertificateService cvs;
+ private List<CertificateConfigEntry> model;
+ private ResourceManagementService R = CertConfigActivator.R;
+
+ public CertConfigTableModel()
+ {
+ CertConfigActivator.getConfigService().addPropertyChangeListener(this);
+ cvs = CertConfigActivator.getCertService();
+ model = cvs.getClientAuthCertificateConfigs();
+ }
+
+ public int getRowCount()
+ {
+ return model.size();
+ }
+
+ public int getColumnCount()
+ {
+ return 3;
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex)
+ {
+ switch(columnIndex)
+ {
+ case 0:
+ return model.get(rowIndex).getDisplayName();
+ case 1:
+ return model.get(rowIndex).getAlias();
+ case 2:
+ return model.get(rowIndex).getKeyStoreType();
+ }
+ return null;
+ }
+
+ public CertificateConfigEntry getItem(int rowIndex)
+ {
+ return model.get(rowIndex);
+ }
+
+ public String getColumnName(int column)
+ {
+ switch(column)
+ {
+ case 0:
+ return R.getI18NString("service.gui.DISPLAY_NAME");
+ case 1:
+ return R.getI18NString("plugin.certconfig.ALIAS");
+ case 2:
+ return R.getI18NString("plugin.certconfig.KEYSTORE_TYPE");
+ }
+ return super.getColumnName(column);
+ }
+
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ if (evt.getPropertyName().startsWith(
+ CertificateService.PNAME_CLIENTAUTH_CERTCONFIG_BASE))
+ {
+ model = cvs.getClientAuthCertificateConfigs();
+ super.fireTableDataChanged();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf b/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf
new file mode 100644
index 0000000..679e141
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf
@@ -0,0 +1,21 @@
+Bundle-Activator: net.java.sip.communicator.plugin.certconfig.CertConfigActivator
+Bundle-Name: Certificate Configuration Plugin
+Bundle-Description: Allows the configuration of general X.509 certificate settings.
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+System-Bundle: no
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.service.certificate,
+ net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.credentialsstorage,
+ net.java.sip.communicator.service.gui,
+ net.java.sip.communicator.service.resources,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.util.swing,
+ javax.swing,
+ javax.swing.border,
+ javax.swing.event,
+ javax.swing.table,
+ javax.security.auth,
+ javax.security.auth.callback,
+ sun.security.pkcs11 \ No newline at end of file
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java
index 398781a..6e3c464 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java
@@ -11,6 +11,7 @@ import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
+import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.util.swing.*;
/**
@@ -42,6 +43,8 @@ public class ConnectionPanel
private final JCheckBox proxyAutoCheckBox;
+ private final JComboBox certificate = new JComboBox();
+
private JComboBox transportCombo = new JComboBox(new Object[]
{ "UDP", "TCP", "TLS" });
@@ -101,7 +104,8 @@ public class ConnectionPanel
JPanel mainPanel = new TransparentPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
- JPanel registrarMainPanel = new TransparentPanel(new BorderLayout(10, 10));
+ JPanel registrarMainPanel =
+ new TransparentPanel(new BorderLayout(10, 10));
JPanel labelsPanel
= new TransparentPanel(new GridLayout(0, 1, 10, 10));
@@ -118,8 +122,12 @@ public class ConnectionPanel
JLabel serverPortLabel
= new JLabel(Resources.getString("service.gui.PORT"));
+ JLabel certLabel = new JLabel(
+ Resources.getString("plugin.sipaccregwizz.CLIENT_CERTIFICATE"));
+
labelsPanel.add(serverLabel);
labelsPanel.add(authNameLabel);
+ labelsPanel.add(certLabel);
JPanel serverPanel = new TransparentPanel(new BorderLayout(5, 5));
serverPanel.add(serverField, BorderLayout.CENTER);
@@ -131,6 +139,8 @@ public class ConnectionPanel
valuesPanel.add(serverPanel);
valuesPanel.add(authNameField);
+ valuesPanel.add(certificate);
+ initCertificateAliases(null);
registrarMainPanel.add(labelsPanel, BorderLayout.WEST);
registrarMainPanel.add(valuesPanel, BorderLayout.CENTER);
@@ -215,6 +225,21 @@ public class ConnectionPanel
this.add(mainPanel, BorderLayout.NORTH);
}
+ private void initCertificateAliases(String id)
+ {
+ certificate.removeAllItems();
+ certificate.insertItemAt(
+ Resources.getString("plugin.sipaccregwizz.NO_CERTIFICATE"), 0);
+ certificate.setSelectedIndex(0);
+ for(CertificateConfigEntry e : SIPAccRegWizzActivator
+ .getCertificateService().getClientAuthCertificateConfigs())
+ {
+ certificate.addItem(e);
+ if(e.getId().equals(id))
+ certificate.setSelectedItem(e);
+ }
+ }
+
/**
* Parse the server part from the sip id and set it to server as default
* value. If Advanced option is enabled Do nothing.
@@ -477,6 +502,31 @@ public class ConnectionPanel
}
/**
+ * Gets the ID of the selected client TLS certificate or <tt>null</tt> if no
+ * certificate is selected.
+ *
+ * @return the ID of the selected client TLS certificate or <tt>null</tt> if
+ * no certificate is selected.
+ */
+ String getCertificateId()
+ {
+ if(certificate.getSelectedItem() != null
+ && certificate.getSelectedItem() instanceof CertificateConfigEntry)
+ return ((CertificateConfigEntry)certificate.getSelectedItem())
+ .getId();
+ return null;
+ }
+
+ /**
+ * Sets the selected client TLS certificate entry.
+ * @param id The ID of the entry to select.
+ */
+ void setCertificateId(String id)
+ {
+ initCertificateAliases(id);
+ }
+
+ /**
* Returns the keep alive method.
* @return the keep alive method
*/
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccRegWizzActivator.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccRegWizzActivator.java
index 48b4bf1..be335bf 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccRegWizzActivator.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccRegWizzActivator.java
@@ -10,6 +10,7 @@ import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.browserlauncher.*;
+import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
@@ -42,6 +43,8 @@ public class SIPAccRegWizzActivator
private static UIService uiService;
+ private static CertificateService certService;
+
/**
* Starts this bundle.
*
@@ -157,6 +160,26 @@ public class SIPAccRegWizzActivator
}
/**
+ * Returns the <tt>CertificateService</tt> obtained from the bundle
+ * context.
+ * @return the <tt>CertificateService</tt> obtained from the bundle
+ * context
+ */
+ public static CertificateService getCertificateService()
+ {
+ if (certService == null)
+ {
+ ServiceReference serviceReference = bundleContext
+ .getServiceReference(CertificateService.class.getName());
+
+ certService = (CertificateService)bundleContext
+ .getService(serviceReference);
+ }
+
+ return certService;
+ }
+
+ /**
* Indicates if the advanced account configuration is currently disabled.
*
* @return <tt>true</tt> if the advanced account configuration is disabled,
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java
index 589318d..f07e83d 100755..100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java
@@ -32,6 +32,8 @@ public class SIPAccountRegistration
private boolean rememberPassword = true;
+ private String tlsClientCertificate;
+
private String serverAddress;
private String displayName;
@@ -148,6 +150,24 @@ public class SIPAccountRegistration
}
/**
+ * Gets the ID of the client certificate configuration.
+ * @return the ID of the client certificate configuration.
+ */
+ public String getTlsClientCertificate()
+ {
+ return tlsClientCertificate;
+ }
+
+ /**
+ * Sets the ID of the client certificate configuration.
+ * @param id the client certificate configuration template ID.
+ */
+ public void setTlsClientCertificate(String id)
+ {
+ tlsClientCertificate = id;
+ }
+
+ /**
* Returns the UIN of the sip registration account.
*
* @return the UIN of the sip registration account.
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java
index 53d64ce..7172cc5 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java
@@ -265,6 +265,8 @@ public class SIPAccountRegistrationForm
connectionPanel.isDefaultEncryptionEnabled());
registration.setSipZrtpAttribute(
connectionPanel.isSipZrtpEnabled());
+ registration.setTlsClientCertificate(
+ connectionPanel.getCertificateId());
registration.setPollingPeriod(
presencePanel.getPollPeriod());
registration.setSubscriptionExpiration(
@@ -343,10 +345,13 @@ public class SIPAccountRegistrationForm
ProtocolProviderFactory.DEFAULT_ENCRYPTION, true);
boolean enabledSipZrtpAttribute = accountID.getAccountPropertyBoolean(
- ProtocolProviderFactory.DEFAULT_SIPZRTP_ATTRIBUTE, true);
+ ProtocolProviderFactory.DEFAULT_SIPZRTP_ATTRIBUTE, true);
+
+ String clientTlsCertificateId = accountID.getAccountPropertyString(
+ ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE);
boolean proxyAutoConfigureEnabled = accountID.getAccountPropertyBoolean(
- ProtocolProviderFactory.PROXY_AUTO_CONFIG, false);
+ ProtocolProviderFactory.PROXY_AUTO_CONFIG, false);
String pollingPeriod = accountID.getAccountPropertyString(
ProtocolProviderFactory.POLLING_PERIOD);
@@ -420,6 +425,8 @@ public class SIPAccountRegistrationForm
connectionPanel.setSipZrtpEnabled( enabledSipZrtpAttribute,
enabledDefaultEncryption);
+ connectionPanel.setCertificateId(clientTlsCertificateId);
+
presencePanel.setPollPeriod(pollingPeriod);
presencePanel.setSubscriptionExpiration(subscriptionPeriod);
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java
index c55b103..45fa442 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java
@@ -473,6 +473,9 @@ public class SIPAccountRegistrationWizard
accountProperties.put(ProtocolProviderFactory.SUBSCRIPTION_EXPIRATION,
registration.getSubscriptionExpiration());
+ accountProperties.put(ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE,
+ registration.getTlsClientCertificate());
+
if(registration.getKeepAliveMethod() != null)
accountProperties.put("KEEP_ALIVE_METHOD",
registration.getKeepAliveMethod());
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf
index e637dc4..fc73be1 100755..100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf
@@ -7,6 +7,7 @@ System-Bundle: yes
Export-package: net.java.sip.communicator.plugin.sipaccregwizz
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.browserlauncher,
+ net.java.sip.communicator.service.certificate,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.contactlist.event,
diff --git a/src/net/java/sip/communicator/service/certificate/CertificateConfigEntry.java b/src/net/java/sip/communicator/service/certificate/CertificateConfigEntry.java
new file mode 100644
index 0000000..c049502
--- /dev/null
+++ b/src/net/java/sip/communicator/service/certificate/CertificateConfigEntry.java
@@ -0,0 +1,175 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.certificate;
+
+/**
+ * Data object for client certificate configuration entries.
+ *
+ * @author Ingo Bauersachs
+ */
+public class CertificateConfigEntry
+{
+ // ------------------------------------------------------------------------
+ // Fields
+ // ------------------------------------------------------------------------
+ private KeyStoreType keyStoreType;
+ private String keyStorePassword;
+ private String displayName;
+ private String alias;
+ private String id;
+ private String keyStore;
+ private boolean savePassword;
+
+ // ------------------------------------------------------------------------
+ // Properties
+ // ------------------------------------------------------------------------
+ /**
+ * Sets the key store type.
+ *
+ * @param keyStoreType the new key store type
+ */
+ public void setKeyStoreType(KeyStoreType keyStoreType)
+ {
+ this.keyStoreType = keyStoreType;
+ }
+
+ /**
+ * Gets the key store type.
+ *
+ * @return the key store type
+ */
+ public KeyStoreType getKeyStoreType()
+ {
+ return keyStoreType;
+ }
+
+ /**
+ * Sets the key store password.
+ *
+ * @param keyStorePassword the new key store password
+ */
+ public void setKeyStorePassword(String keyStorePassword)
+ {
+ this.keyStorePassword = keyStorePassword;
+ }
+
+ /**
+ * Gets the key store password.
+ *
+ * @return the key store password
+ */
+ public String getKeyStorePassword()
+ {
+ return keyStorePassword;
+ }
+
+ /**
+ * Sets the display name.
+ *
+ * @param displayName the new display name
+ */
+ public void setDisplayName(String displayName)
+ {
+ this.displayName = displayName;
+ }
+
+ /**
+ * Gets the display name.
+ *
+ * @return the display name
+ */
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ /**
+ * Sets the alias.
+ *
+ * @param alias the new alias
+ */
+ public void setAlias(String alias)
+ {
+ this.alias = alias;
+ }
+
+ /**
+ * Gets the alias.
+ *
+ * @return the alias
+ */
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ /**
+ * Sets the id.
+ *
+ * @param id the new id
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Gets the id.
+ *
+ * @return the id
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Sets the key store.
+ *
+ * @param keyStore the new key store
+ */
+ public void setKeyStore(String keyStore)
+ {
+ this.keyStore = keyStore;
+ }
+
+ /**
+ * Gets the key store.
+ *
+ * @return the key store
+ */
+ public String getKeyStore()
+ {
+ return keyStore;
+ }
+
+ /**
+ * Sets the save password.
+ *
+ * @param savePassword the new save password
+ */
+ public void setSavePassword(boolean savePassword)
+ {
+ this.savePassword = savePassword;
+ }
+
+ /**
+ * Checks if is save password.
+ *
+ * @return true, if is save password
+ */
+ public boolean isSavePassword()
+ {
+ return savePassword;
+ }
+
+ @Override
+ public String toString()
+ {
+ return displayName;
+ }
+}
diff --git a/src/net/java/sip/communicator/service/certificate/CertificateService.java b/src/net/java/sip/communicator/service/certificate/CertificateService.java
index 3c4e4c6..8197932 100644
--- a/src/net/java/sip/communicator/service/certificate/CertificateService.java
+++ b/src/net/java/sip/communicator/service/certificate/CertificateService.java
@@ -8,6 +8,7 @@ package net.java.sip.communicator.service.certificate;
import java.security.GeneralSecurityException;
import java.security.cert.*;
+import java.util.*;
import javax.net.ssl.*;
@@ -20,6 +21,9 @@ import javax.net.ssl.*;
*/
public interface CertificateService
{
+ // ------------------------------------------------------------------------
+ // Configuration property names
+ // ------------------------------------------------------------------------
/**
* Property for always trust mode. When enabled certificate check is
* skipped.
@@ -36,6 +40,29 @@ public interface CertificateService
"net.java.sip.communicator.service.tls.NO_USER_INTERACTION";
/**
+ * The property name prefix of all client authentication configurations.
+ */
+ public static final String PNAME_CLIENTAUTH_CERTCONFIG_BASE =
+ "net.java.sip.communicator.service.cert.clientauth";
+
+ /**
+ * Property that is being applied to the system property
+ * <tt>javax.net.ssl.trustStoreType</tt>
+ */
+ public static final String PNAME_TRUSTSTORE =
+ "net.java.sip.communicator.service.cert.truststore.file";
+
+ /**
+ * Property that is being applied to the system property
+ * <tt>javax.net.ssl.trustStorePassword</tt>
+ */
+ public static final String PNAME_TRUSTSTORE_PASSWORD =
+ "net.java.sip.communicator.service.cert.truststore";
+
+ // ------------------------------------------------------------------------
+ // constants
+ // ------------------------------------------------------------------------
+ /**
* Result of user interaction. User does not trust this certificate.
*/
public final static int DO_NOT_TRUST = 0;
@@ -51,6 +78,43 @@ public interface CertificateService
*/
public final static int TRUST_THIS_SESSION_ONLY = 2;
+ // ------------------------------------------------------------------------
+ // Client authentication configuration
+ // ------------------------------------------------------------------------
+ /**
+ * Returns all saved {@see CertificateConfigEntry}s.
+ *
+ * @return List of the saved authentication configurations.
+ */
+ public List<CertificateConfigEntry> getClientAuthCertificateConfigs();
+
+ /**
+ * Deletes a saved {@see CertificateConfigEntry}.
+ *
+ * @param id The ID ({@see CertificateConfigEntry#getId()}) of the entry to
+ * delete.
+ */
+ public void removeClientAuthCertificateConfig(String id);
+
+ /**
+ * Saves or updates the passed @see CertificateConfigEntry to the config.
+ * If {@see CertificateConfigEntry#getId()} returns null, a new entry is
+ * created.
+ *
+ * @param entry The @see CertificateConfigEntry to save or update.
+ */
+ public void setClientAuthCertificateConfig(CertificateConfigEntry entry);
+
+ /**
+ * Gets a list of all supported KeyStore types.
+ *
+ * @return a list of all supported KeyStore types.
+ */
+ public List<KeyStoreType> getSupportedKeyStoreTypes();
+
+ // ------------------------------------------------------------------------
+ // Certificate trust handling
+ // ------------------------------------------------------------------------
/**
* Get an SSL Context that validates certificates based on the JRE default
* check and asks the user when the JRE check fails.
@@ -75,6 +139,35 @@ public interface CertificateService
throws GeneralSecurityException;
/**
+ * Get an SSL Context with the specified trustmanager.
+ *
+ * @param clientCertConfig The ID of a client certificate configuration
+ * entry that is to be used when the server asks for a client TLS
+ * certificate
+ * @param trustManager The trustmanager that will be used by the created
+ * SSLContext
+ * @return An SSL context based on the supplied trust manager.
+ * @throws GeneralSecurityException
+ */
+ public SSLContext getSSLContext(String clientCertConfig,
+ X509TrustManager trustManager)
+ throws GeneralSecurityException;
+
+ /**
+ * Get an SSL Context with the specified trustmanager.
+ *
+ * @param keyManagers The key manager(s) to be used for client
+ * authentication
+ * @param trustManager The trustmanager that will be used by the created
+ * SSLContext
+ * @return An SSL context based on the supplied trust manager.
+ * @throws GeneralSecurityException
+ */
+ public SSLContext getSSLContext(KeyManager[] keyManagers,
+ X509TrustManager trustManager)
+ throws GeneralSecurityException;
+
+ /**
* Creates a trustmanager that validates the certificate based on the JRE
* default check and asks the user when the JRE check fails. When
* <tt>null</tt> is passed as the <tt>identityToTest</tt> then no check is
diff --git a/src/net/java/sip/communicator/service/certificate/KeyStoreType.java b/src/net/java/sip/communicator/service/certificate/KeyStoreType.java
new file mode 100644
index 0000000..7e90cdb
--- /dev/null
+++ b/src/net/java/sip/communicator/service/certificate/KeyStoreType.java
@@ -0,0 +1,68 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.certificate;
+
+/**
+ * Data object for KeyStore configurations. Primarily used during adding/
+ * editing client certificate configurations.
+ *
+ * @author Ingo Bauersachs
+ */
+public class KeyStoreType
+{
+ private String name;
+ private String[] fileExtensions;
+ private boolean hasKeyStorePassword;
+
+ /**
+ * Creates a new instance of this class.
+ * @param name the display name of the keystore type.
+ * @param fileExtensions known file name extensions (including the dot)
+ * @param hasKeyStorePassword
+ */
+ public KeyStoreType(String name, String[] fileExtensions,
+ boolean hasKeyStorePassword)
+ {
+ this.name = name;
+ this.fileExtensions = fileExtensions;
+ this.hasKeyStorePassword = hasKeyStorePassword;
+ }
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Gets the display name.
+ * @return the display name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Gets the known file name extensions.
+ * @return Known file name extensions (including the dot).
+ */
+ public String[] getFileExtensions()
+ {
+ return fileExtensions;
+ }
+
+ /**
+ * Flag that indicates if the keystore supports passwords.
+ * @return <tt>true</tt> if the keystore supports passwords, <tt>false</tt>
+ * otherwise.
+ */
+ public boolean hasKeyStorePassword()
+ {
+ return hasKeyStorePassword;
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
index c998ed2..60c1376 100644
--- a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
+++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
@@ -197,6 +197,13 @@ public abstract class ProtocolProviderFactory
"DEFAULT_SIPZRTP_ATTRIBUTE";
/**
+ * The name of the property which defines the ID of the client TLS
+ * certificate configuration entry.
+ */
+ public static final String CLIENT_TLS_CERTIFICATE =
+ "CLIENT_TLS_CERTIFICATE";
+
+ /**
* The name of the property under which we store the boolean value
* indicating if the user name should be automatically changed if the
* specified name already exists. This property is meant to be used by IRC
diff --git a/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java b/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java
index 037b90b..d32760f 100644
--- a/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java
+++ b/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java
@@ -20,7 +20,7 @@ import net.java.sip.communicator.util.*;
* @author Yana Stamcheva
*/
public class AuthenticationWindow
- extends SIPCommFrame
+ extends SIPCommDialog
implements ActionListener
{
private static final long serialVersionUID = 1L;
@@ -105,7 +105,23 @@ public class AuthenticationWindow
boolean isUserNameEditable,
ImageIcon icon)
{
- super(false);
+ this(null, server, isUserNameEditable, icon);
+ }
+
+ /**
+ * Creates an instance of the <tt>LoginWindow</tt>.
+ *
+ * @param owner the owner of this dialog
+ * @param server the server name
+ * @param isUserNameEditable indicates if the user name is editable
+ * @param icon the icon to display on the left of the authentication window
+ */
+ public AuthenticationWindow(Dialog owner,
+ String server,
+ boolean isUserNameEditable,
+ ImageIcon icon)
+ {
+ super(owner, false);
this.server = server;
@@ -164,7 +180,32 @@ public class AuthenticationWindow
ImageIcon icon,
String errorMessage)
{
- this(userName, password, server, isUserNameEditable, icon);
+ this(null, userName, password, server, isUserNameEditable,
+ icon, errorMessage);
+ }
+
+ /**
+ * Creates an instance of the <tt>LoginWindow</tt>.
+ *
+ * @param owner the owner of this dialog
+ * @param userName the user name to set by default
+ * @param password the password to set by default
+ * @param server the server name this authentication window is about
+ * @param isUserNameEditable indicates if the user name should be editable
+ * by the user or not
+ * @param icon the icon displayed on the left of the authentication window
+ * @param errorMessage an error message explaining a reason for opening
+ * the authentication dialog (when a wrong password was provided, etc.)
+ */
+ public AuthenticationWindow(Dialog owner,
+ String userName,
+ char[] password,
+ String server,
+ boolean isUserNameEditable,
+ ImageIcon icon,
+ String errorMessage)
+ {
+ this(owner, userName, password, server, isUserNameEditable, icon);
this.infoTextArea.setForeground(Color.RED);
this.infoTextArea.setText(errorMessage);
@@ -191,7 +232,36 @@ public class AuthenticationWindow
ImageIcon icon,
String errorMessage)
{
- this(userName, password, server, isUserNameEditable, icon, errorMessage);
+ this(null, userName, password, server, isUserNameEditable,
+ isRememberPassword, icon, errorMessage);
+ }
+
+
+ /**
+ * Creates an instance of the <tt>LoginWindow</tt>.
+ *
+ * @param owner the owner of this dialog
+ * @param userName the user name to set by default
+ * @param password the password to set by default
+ * @param server the server name this authentication window is about
+ * @param isUserNameEditable indicates if the user name should be editable
+ * by the user or not
+ * @param isRememberPassword indicates if the password should be rememberd
+ * @param icon the icon displayed on the left of the authentication window
+ * @param errorMessage an error message explaining a reason for opening
+ * the authentication dialog (when a wrong password was provided, etc.)
+ */
+ public AuthenticationWindow(Dialog owner,
+ String userName,
+ char[] password,
+ String server,
+ boolean isUserNameEditable,
+ boolean isRememberPassword,
+ ImageIcon icon,
+ String errorMessage)
+ {
+ this(owner, userName, password, server, isUserNameEditable,
+ icon, errorMessage);
this.isRememberPassword = isRememberPassword;
}
@@ -213,7 +283,29 @@ public class AuthenticationWindow
boolean isUserNameEditable,
ImageIcon icon)
{
- this(server, isUserNameEditable, icon);
+ this(null, userName, password, server, isUserNameEditable, icon);
+ }
+
+ /**
+ * Creates an instance of the <tt>LoginWindow</tt>.
+ *
+ * @param owner the owner of this dialog
+ * @param userName the user name to set by default
+ * @param password the password to set by default
+ * @param server the server name this authentication window is about
+ * @param isUserNameEditable indicates if the user name should be editable
+ * by the user or not
+ * @param icon the icon displayed on the left of the authentication window
+ */
+ public AuthenticationWindow(
+ Dialog owner,
+ String userName,
+ char[] password,
+ String server,
+ boolean isUserNameEditable,
+ ImageIcon icon)
+ {
+ this(owner, server, isUserNameEditable, icon);
if (userName != null)
{
@@ -228,6 +320,15 @@ public class AuthenticationWindow
}
/**
+ * Shows or hides the "save password" checkbox.
+ * @param allow the checkbox is shown when allow is <tt>true</tt>
+ */
+ public void setAllowSavePassword(boolean allow)
+ {
+ rememberPassCheckBox.setVisible(allow);
+ }
+
+ /**
* Initializes the icon image.
*
* @param icon the icon to show on the left of the window
@@ -322,6 +423,11 @@ public class AuthenticationWindow
this.loginButton.setName("ok");
this.cancelButton.setName("cancel");
+ if(loginButton.getPreferredSize().width
+ > cancelButton.getPreferredSize().width)
+ cancelButton.setPreferredSize(loginButton.getPreferredSize());
+ else
+ loginButton.setPreferredSize(cancelButton.getPreferredSize());
this.loginButton.setMnemonic(
UtilActivator.getResources().getI18nMnemonic("service.gui.OK"));
@@ -416,6 +522,8 @@ public class AuthenticationWindow
{
this.setName("AUTHENTICATION");
+ if(getOwner() != null)
+ setModal(true);
super.setVisible(isVisible);
if(isVisible)
@@ -426,6 +534,9 @@ public class AuthenticationWindow
else
passwdField.requestFocus();
+ if(getOwner() != null)
+ return;
+
synchronized (lock)
{
while(!buttonClicked)
diff --git a/src/net/java/sip/communicator/util/swing/X509CertificatePanel.java b/src/net/java/sip/communicator/util/swing/X509CertificatePanel.java
new file mode 100644
index 0000000..fa00554
--- /dev/null
+++ b/src/net/java/sip/communicator/util/swing/X509CertificatePanel.java
@@ -0,0 +1,417 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.util.swing;
+
+import java.awt.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.interfaces.*;
+import java.text.*;
+import java.util.*;
+
+import javax.naming.*;
+import javax.naming.ldap.*;
+import javax.security.auth.x500.*;
+import javax.swing.*;
+
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Panel that shows the content of an X509Certificate.
+ */
+public class X509CertificatePanel
+ extends TransparentPanel
+{
+ private static final long serialVersionUID = -8368302061995971947L;
+
+ public X509CertificatePanel(X509Certificate certificate)
+ {
+ ResourceManagementService R = UtilActivator.getResources();
+ DateFormat dateFormatter = DateFormat
+ .getDateInstance(DateFormat.MEDIUM);
+
+ Insets valueInsets = new Insets(2,10,0,0);
+ Insets titleInsets = new Insets(10,5,0,0);
+
+ setLayout(new GridBagLayout());
+
+ int currentRow = 0;
+
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.anchor = GridBagConstraints.WEST;
+ constraints.fill = GridBagConstraints.HORIZONTAL;
+ constraints.insets = new Insets(2,5,0,0);
+ constraints.gridx = 0;
+ constraints.weightx = 0;
+ constraints.weighty = 0;
+ constraints.gridy = currentRow++;
+
+ X500Principal issuer = certificate.getIssuerX500Principal();
+ X500Principal subject = certificate.getSubjectX500Principal();
+
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ISSUED_TO")),
+ constraints);
+
+ // subject
+ constraints.insets = valueInsets;
+ try
+ {
+ for(Rdn name : new LdapName(subject.getName()).getRdns())
+ {
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ String lbl =
+ R.getI18NString("service.gui.CERT_INFO_" + name.getType());
+ if (lbl
+ .equals("!service.gui.CERT_INFO_" + name.getType() + "!"))
+ lbl = name.getType();
+ add(new JLabel(lbl), constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(
+ name.getValue() instanceof byte[] ?
+ getHex((byte[])name.getValue()) + " ("
+ + new String((byte[]) name.getValue()) + ")"
+ : name.getValue().toString()),
+ constraints);
+ }
+ }
+ catch (InvalidNameException e1)
+ {
+ constraints.gridy = currentRow++;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_CN")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(subject.getName()),
+ constraints);
+ }
+
+ // issuer
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ISSUED_BY")),
+ constraints);
+ constraints.insets = valueInsets;
+ try
+ {
+ for(Rdn name : new LdapName(issuer.getName()).getRdns())
+ {
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.gridx = 0;
+ String lbl =
+ R.getI18NString("service.gui.CERT_INFO_" + name.getType());
+ if (lbl
+ .equals("!service.gui.CERT_INFO_" + name.getType() + "!"))
+ lbl = name.getType();
+ add(new JLabel(lbl), constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(
+ name.getValue() instanceof byte[] ?
+ getHex((byte[])name.getValue()) + " ("
+ + new String((byte[]) name.getValue()) + ")"
+ : name.getValue().toString()),
+ constraints);
+ }
+ }
+ catch (InvalidNameException e1)
+ {
+ constraints.gridy = currentRow++;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_CN")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(issuer.getName()),
+ constraints);
+ }
+
+ // validity
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_VALIDITY")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ISSUED_ON")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(dateFormatter.format(certificate.getNotBefore())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_EXPIRES_ON")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(dateFormatter.format(certificate.getNotAfter())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_FINGERPRINTS")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ try
+ {
+ String sha1String = getThumbprint(certificate, "SHA1");
+ String md5String = getThumbprint(certificate, "MD5");
+
+ JTextArea sha1Area = new JTextArea(sha1String);
+ sha1Area.setLineWrap(false);
+ sha1Area.setOpaque(false);
+ sha1Area.setWrapStyleWord(true);
+ sha1Area.setEditable(false);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel("SHA1:"),
+ constraints);
+
+ constraints.gridx = 1;
+ add(
+ sha1Area,
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel("MD5:"),
+ constraints);
+
+ JTextArea md5Area = new JTextArea(md5String);
+ md5Area.setLineWrap(false);
+ md5Area.setOpaque(false);
+ md5Area.setWrapStyleWord(true);
+ md5Area.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ md5Area,
+ constraints);
+ }
+ catch (Exception e)
+ {
+ // do nothing as we cannot show this value
+ }
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_CERT_DETAILS")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_SER_NUM")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(certificate.getSerialNumber().toString()),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_VER")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(String.valueOf(certificate.getVersion())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_SIGN_ALG")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(String.valueOf(certificate.getSigAlgName())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_PUB_KEY_INFO")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ALG")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(certificate.getPublicKey().getAlgorithm()),
+ constraints);
+
+ if(certificate.getPublicKey().getAlgorithm().equals("RSA"))
+ {
+ RSAPublicKey key = (RSAPublicKey)certificate.getPublicKey();
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_PUB_KEY")),
+ constraints);
+
+ JTextArea pubkeyArea = new JTextArea(
+ R.getI18NString(
+ "service.gui.CERT_INFO_KEY_BYTES_PRINT",
+ new String[]{
+ String.valueOf(key.getModulus().toByteArray().length-1),
+ key.getModulus().toString(16)
+ }));
+ pubkeyArea.setLineWrap(false);
+ pubkeyArea.setOpaque(false);
+ pubkeyArea.setWrapStyleWord(true);
+ pubkeyArea.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ pubkeyArea,
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_EXP")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(key.getPublicExponent().toString()),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_KEY_SIZE")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(R.getI18NString(
+ "service.gui.CERT_INFO_KEY_BITS_PRINT",
+ new String[]{
+ String.valueOf(key.getModulus().bitLength())})),
+ constraints);
+ }
+ else if(certificate.getPublicKey().getAlgorithm().equals("DSA"))
+ {
+ DSAPublicKey key =
+ (DSAPublicKey)certificate.getPublicKey();
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel("Y:"), constraints);
+
+ JTextArea yArea = new JTextArea(key.getY().toString(16));
+ yArea.setLineWrap(false);
+ yArea.setOpaque(false);
+ yArea.setWrapStyleWord(true);
+ yArea.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ yArea,
+ constraints);
+ }
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_SIGN")),
+ constraints);
+
+ JTextArea signArea = new JTextArea(
+ R.getI18NString(
+ "service.gui.CERT_INFO_KEY_BYTES_PRINT",
+ new String[]{
+ String.valueOf(certificate.getSignature().length),
+ getHex(certificate.getSignature())
+ }));
+ signArea.setLineWrap(false);
+ signArea.setOpaque(false);
+ signArea.setWrapStyleWord(true);
+ signArea.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ signArea,
+ constraints);
+ }
+
+ /**
+ * Converts the byte array to hex string.
+ * @param raw the data.
+ * @return the hex string.
+ */
+ private String getHex( byte [] raw )
+ {
+ if (raw == null)
+ return null;
+
+ StringBuilder hex = new StringBuilder(2 * raw.length);
+ Formatter f = new Formatter(hex);
+ for (byte b : raw)
+ {
+ f.format("%02x", b);
+ }
+ return hex.toString();
+ } /**
+ * Calculates the hash of the certificate known as the "thumbprint"
+ * and returns it as a string representation.
+ *
+ * @param cert The certificate to hash.
+ * @param algorithm The hash algorithm to use.
+ * @return The SHA-1 hash of the certificate.
+ * @throws CertificateException
+ */
+ private static String getThumbprint(X509Certificate cert, String algorithm)
+ throws CertificateException
+ {
+ MessageDigest digest;
+ try
+ {
+ digest = MessageDigest.getInstance(algorithm);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new CertificateException(e);
+ }
+ byte[] encodedCert = cert.getEncoded();
+ StringBuilder sb = new StringBuilder(encodedCert.length * 2);
+ Formatter f = new Formatter(sb);
+ for (byte b : digest.digest(encodedCert))
+ {
+ f.format("%02x", b);
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/net/java/sip/communicator/util/util.manifest.mf b/src/net/java/sip/communicator/util/util.manifest.mf
index ef3ffa3..e61bdfa 100644
--- a/src/net/java/sip/communicator/util/util.manifest.mf
+++ b/src/net/java/sip/communicator/util/util.manifest.mf
@@ -22,9 +22,11 @@ Import-Package: org.xml.sax,
javax.swing.border,
javax.swing.filechooser,
javax.swing,
- javax.naming.directory,
javax.naming,
+ javax.naming.directory,
+ javax.naming.ldap,
javax.imageio,
+ javax.security.auth.x500,
net.java.sip.communicator.util.xml,
net.java.sip.communicator.util,
net.java.sip.communicator.service.resources,