diff options
author | Ingo Bauersachs <ingo@jitsi.org> | 2011-08-25 18:30:44 +0000 |
---|---|---|
committer | Ingo Bauersachs <ingo@jitsi.org> | 2011-08-25 18:30:44 +0000 |
commit | 661810caa7cfebe1c72b8dac3ad3d86adf021f04 (patch) | |
tree | ec50a9f7d04633298b36eb9a0886ccc338126f37 /src/net/java | |
parent | e08fb14afaffa218289086f1d9fe3f58ed2e0bcb (diff) | |
download | jitsi-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')
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, |