From 60385be26e44377815e0cecee93a23e9b067bc80 Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Tue, 9 Aug 2011 11:47:41 +0000 Subject: [PATCH] Ignore manual entered SIP proxy data when autodetect is selected Replace the CertificateVerificationService with CertificateService to correctly validate the hostname of servers --- build.xml | 2 + lib/installer-exclude/lcrypto-jdk16-143.jar | Bin 161353 -> 246392 bytes resources/languages/resources.properties | 15 +- .../certificate/CertificateServiceImpl.java | 692 ++++++++++++++++++ .../CertificateVerificationActivator.java | 4 +- .../CertificateVerificationServiceImpl.java | 2 + .../certificate/VerifyCertificateDialog.java | 659 +++++++++++++++++ .../impl/certificate/certificate.manifest.mf | 6 +- .../ProtocolProviderServiceJabberImpl.java | 40 +- .../sip/ProtocolProviderServiceSipImpl.java | 29 +- .../impl/protocol/sip/SipActivator.java | 51 +- .../protocol/sip/SipRegistrarConnection.java | 12 +- .../impl/protocol/sip/SipStackProperties.java | 7 +- .../impl/protocol/sip/net/RFC5922Matcher.java | 214 ++++++ .../protocol/sip/net/SslNetworkLayer.java | 277 +++---- .../protocol/sip/xcap/BaseHttpXCapClient.java | 67 +- .../certificate/CertificateMatcher.java | 24 + .../certificate/CertificateService.java | 152 ++++ .../CertificateVerificationService.java | 3 + .../service/httputil/HttpUtilActivator.java | 8 +- .../service/httputil/HttpUtils.java | 35 +- .../sip/communicator/util/NetworkUtils.java | 24 + .../jabber/TestAccountInstallation.java | 2 +- 23 files changed, 2049 insertions(+), 276 deletions(-) create mode 100644 src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java create mode 100644 src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java create mode 100644 src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java create mode 100644 src/net/java/sip/communicator/service/certificate/CertificateMatcher.java create mode 100644 src/net/java/sip/communicator/service/certificate/CertificateService.java diff --git a/build.xml b/build.xml index 008b17931..f126f4934 100644 --- a/build.xml +++ b/build.xml @@ -2366,7 +2366,9 @@ javax.swing.event, javax.swing.border"/> +service.gui.CERT_DIALOG_DESCRIPTION_TXT_NOHOST={0} can''t verify the \ +identity of the server''s certificate.

\ The certificate is not trusted, which means that the server''s \ identity cannot
be automatically verified. \ Do you want to continue connecting?

\ @@ -515,12 +522,13 @@ The certificate is not trusted, which means that the client''s \ identity cannot
be automatically verified. \ Do you want to accept the connection?

\ For more information, click "Show Certificate". -service.gui.CERT_DIALOG_PEER_DESCRIPTION_TXT=The identity \ -of the peer {0} could not be verified.

\ +service.gui.CERT_DIALOG_PEER_DESCRIPTION_TXT={0} can''t verify the identity \ +of the peer {0}.

\ The certificate is not trusted, which means that the peer''s \ identity cannot
be automatically verified. \ Do you want to continue connecting?

\ For more information, click "Show Certificate". +service.gui.CONTINUE_ANYWAY=Continue anyway service.gui.CERT_INFO_ISSUED_TO=Issued To service.gui.CERT_INFO_CN=Common Name: service.gui.CERT_INFO_O=Organization: @@ -1266,7 +1274,6 @@ plugin.loggingutils.ARCHIVE_MESSAGE_NOTOK=Error archiving logs \n{0} # dns config plugin plugin.dnsconfig.TITLE=Parallel DNS plugin.dnsconfig.border.TITLE=Backup resolver -plugin.dnsconfig.ICON= plugin.dnsconfig.chkBackupDnsEnabled.text=Enable parallel DNS resolving plugin.dnsconfig.lblBackupResolver.text=Hostname plugin.dnsconfig.lblBackupResolverFallbackIP.text=Fallback IP diff --git a/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java b/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java new file mode 100644 index 000000000..480d812ef --- /dev/null +++ b/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java @@ -0,0 +1,692 @@ +/* + * 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.impl.certificate; + +import java.io.*; +import java.net.*; +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.util.*; + +import javax.net.ssl.*; +import javax.swing.*; + +import org.bouncycastle.asn1.*; +import org.bouncycastle.asn1.x509.*; +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.httputil.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; + +/** + * Implementation of the CertificateService. It asks the user to trust a + * certificate when the automatic verification fails. + * + * @author Ingo Bauersachs + */ +public class CertificateServiceImpl + implements CertificateService +{ + // services + private static final Logger logger = + Logger.getLogger(CertificateServiceImpl.class); + + private final ResourceManagementService R = + CertificateVerificationActivator.getResources(); + + private final ConfigurationService config = + CertificateVerificationActivator.getConfigurationService(); + + // properties + /** + * Base property name for the storage of certificate user preferences. + */ + private final static String PNAME_CERT_TRUST_PREFIX = + "net.java.sip.communicator.impl.certservice"; + + /** Hash algorithm for the cert thumbprint*/ + private final static String THUMBPRINT_HASH_ALGORITHM = "SHA1"; + + // variables + /** + * Stores the certificates that are trusted as long as this service lives. + */ + private Map sessionAllowedCertificates = + new HashMap(); + + /* + * (non-Javadoc) + * + * @see net.java.sip.communicator.service.certificate.CertificateService# + * addCertificateToTrust(java.security.cert.Certificate, java.lang.String, + * int) + */ + public void addCertificateToTrust(Certificate cert, String trustFor, + int trustMode) + throws CertificateException + { + switch (trustMode) + { + case DO_NOT_TRUST: + throw new IllegalArgumentException( + "Cannot add a certificate to trust when " + + "no trust is requested."); + case TRUST_ALWAYS: + config.setProperty(PNAME_CERT_TRUST_PREFIX + ".param." + trustFor, + getThumbprint(cert, THUMBPRINT_HASH_ALGORITHM)); + break; + case TRUST_THIS_SESSION_ONLY: + sessionAllowedCertificates.put(PNAME_CERT_TRUST_PREFIX + ".param." + + trustFor, getThumbprint(cert, THUMBPRINT_HASH_ALGORITHM)); + break; + } + } + + /* + * (non-Javadoc) + * + * @see net.java.sip.communicator.service.certificate.CertificateService# + * getSSLContext() + */ + public SSLContext getSSLContext() throws GeneralSecurityException + { + return getSSLContext(getTrustManager((Iterable)null)); + } + + /* + * (non-Javadoc) + * + * @see net.java.sip.communicator.service.certificate.CertificateService# + * getSSLContext(javax.net.ssl.X509TrustManager) + */ + public SSLContext getSSLContext(X509TrustManager trustManager) + throws GeneralSecurityException + { + try + { + KeyStore ks = + KeyStore.getInstance(System.getProperty( + "javax.net.ssl.keyStoreType", KeyStore.getDefaultType())); + KeyManagerFactory kmFactory = + KeyManagerFactory.getInstance(KeyManagerFactory + .getDefaultAlgorithm()); + + String keyStorePassword = + System.getProperty("javax.net.ssl.keyStorePassword"); + if (System.getProperty("javax.net.ssl.keyStore") != null) + { + ks.load( + new FileInputStream(System + .getProperty("javax.net.ssl.keyStore")), null); + } + else + { + ks.load(null, null); + } + + kmFactory.init(ks, keyStorePassword == null ? null + : keyStorePassword.toCharArray()); + + //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); + + return sslContext; + } + catch (Exception e) + { + throw new GeneralSecurityException("Cannot init SSLContext: " + + e.getMessage()); + } + } + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.service.certificate + * .CertificateService#getTrustManager(java.lang.Iterable) + */ + public X509TrustManager getTrustManager(Iterable identitiesToTest) + throws GeneralSecurityException + { + return getTrustManager( + identitiesToTest, + new EMailAddressMatcher(), + new BrowserLikeHostnameMatcher() + ); + } + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.service.certificate.CertificateService + * #getTrustManager(java.lang.String) + */ + public X509TrustManager getTrustManager(String identityToTest) + throws GeneralSecurityException + { + return getTrustManager( + Arrays.asList(new String[]{identityToTest}), + new EMailAddressMatcher(), + new BrowserLikeHostnameMatcher() + ); + } + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.service.certificate.CertificateService + * #getTrustManager(java.lang.String, + * net.java.sip.communicator.service.certificate.CertificateMatcher, + * net.java.sip.communicator.service.certificate.CertificateMatcher) + */ + public X509TrustManager getTrustManager( + String identityToTest, + CertificateMatcher clientVerifier, + CertificateMatcher serverVerifier) + throws GeneralSecurityException + { + return getTrustManager( + Arrays.asList(new String[]{identityToTest}), + clientVerifier, + serverVerifier + ); + } + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.service.certificate.CertificateService + * #getTrustManager(java.lang.Iterable, + * net.java.sip.communicator.service.certificate.CertificateMatcher, + * net.java.sip.communicator.service.certificate.CertificateMatcher) + */ + public X509TrustManager getTrustManager( + final Iterable identitiesToTest, + final CertificateMatcher clientVerifier, + final CertificateMatcher serverVerifier) + throws GeneralSecurityException + { + // obtain the default X509 trust manager + X509TrustManager defaultTm = null; + TrustManagerFactory tmFactory = + TrustManagerFactory.getInstance(TrustManagerFactory + .getDefaultAlgorithm()); + tmFactory.init((KeyStore) null); + for (TrustManager m : tmFactory.getTrustManagers()) + { + if (m instanceof X509TrustManager) + { + defaultTm = (X509TrustManager) m; + break; + } + } + if (defaultTm == null) + throw new GeneralSecurityException( + "No default X509 trust manager found"); + + final X509TrustManager tm = defaultTm; + + return new X509TrustManager() + { + private boolean serverCheck; + + public X509Certificate[] getAcceptedIssuers() + { + return tm.getAcceptedIssuers(); + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException + { + serverCheck = true; + checkCertTrusted(chain, authType); + } + + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException + { + serverCheck = false; + checkCertTrusted(chain, authType); + } + + private void checkCertTrusted(X509Certificate[] chain, + String authType) throws CertificateException + { + if(config.getBoolean(PNAME_ALWAYS_TRUST, false)) + return; + + try + { + // check the certificate itself (issuer, validity) + try + { + chain = tryBuildChain(chain); + } + catch (Exception e) + {} // don't care and take the chain as is + + if(serverCheck) + tm.checkServerTrusted(chain, authType); + else + tm.checkClientTrusted(chain, authType); + + if(identitiesToTest == null + || !identitiesToTest.iterator().hasNext()) + return; + else if(serverCheck) + serverVerifier.verify(identitiesToTest, chain[0]); + else + clientVerifier.verify(identitiesToTest, chain[0]); + + // ok, globally valid cert + } + catch (CertificateException e) + { + String thumbprint = getThumbprint( + chain[0], THUMBPRINT_HASH_ALGORITHM); + String propName = null; + String message = null; + String storedCert = null; + String appName = + R.getSettingsString("service.gui.APPLICATION_NAME"); + + if (identitiesToTest == null + || !identitiesToTest.iterator().hasNext()) + { + propName = + PNAME_CERT_TRUST_PREFIX + ".server." + thumbprint; + message = + R.getI18NString("service.gui." + + "CERT_DIALOG_DESCRIPTION_TXT_NOHOST", + new String[] { + appName + } + ); + + // get the thumbprint from the permanent allowances + storedCert = config.getString(propName); + // not found? check the session allowances + if (storedCert == null) + storedCert = + sessionAllowedCertificates.get(propName); + } + else + { + for (String identity : identitiesToTest) + { + if (serverCheck) + { + message = + R.getI18NString( + "service.gui." + + "CERT_DIALOG_DESCRIPTION_TXT", + new String[] { + appName, + identitiesToTest.toString() + } + ); + propName = + PNAME_CERT_TRUST_PREFIX + ".param." + + identity; + } + else + { + message = + R.getI18NString( + "service.gui." + + "CERT_DIALOG_PEER_DESCRIPTION_TXT", + new String[] { + appName, + identitiesToTest.toString() + } + ); + propName = + PNAME_CERT_TRUST_PREFIX + ".param." + + identity; + } + + // get the thumbprint from the permanent allowances + storedCert = config.getString(propName); + // not found? check the session allowances + if (storedCert == null) + storedCert = + sessionAllowedCertificates.get(propName); + + // stop search for further saved allowances if we + // found a match + if (storedCert != null) + break; + } + } + + if (!thumbprint.equals(storedCert)) + { + switch (verify(chain, message)) + { + case DO_NOT_TRUST: + throw new CertificateException( + "The peer provided certificate with Subject <" + + chain[0].getSubjectDN() + + "> is not trusted"); + case TRUST_ALWAYS: + config.setProperty(propName, thumbprint); + break; + case TRUST_THIS_SESSION_ONLY: + sessionAllowedCertificates + .put(propName, thumbprint); + break; + } + } + // ok, we've seen this certificate before + } + } + + private X509Certificate[] tryBuildChain(X509Certificate[] chain) + throws IOException, + URISyntaxException, + CertificateException + { + // Only try to build chains for servers that send only their + // own cert, but no issuer. This also matches self signed (will + // be ignored later) and Root-CA signed certs. In this case we + // throw the Root-CA away after the lookup + if (chain.length != 1) + return chain; + + // ignore self signed certs + if (chain[0].getIssuerDN().equals(chain[0].getSubjectDN())) + return chain; + + // prepare for the newly created chain + List newChain = + new ArrayList(chain.length + 4); + for (X509Certificate cert : chain) + { + newChain.add(cert); + } + + // search from the topmost certificate upwards + CertificateFactory certFactory = + CertificateFactory.getInstance("X.509"); + X509Certificate current = chain[chain.length - 1]; + boolean foundParent; + int chainLookupCount = 0; + do + { + foundParent = false; + // extract the url(s) where the parent certificate can be + // found + byte[] aiaBytes = + current.getExtensionValue( + X509Extension.authorityInfoAccess.getId()); + if (aiaBytes == null) + break; + + DEROctetString octs = + (DEROctetString) ASN1Object.fromByteArray(aiaBytes); + ASN1InputStream as = new ASN1InputStream(octs.getOctets()); + AuthorityInformationAccess aia = + AuthorityInformationAccess + .getInstance(as.readObject()); + // the AIA may contain different URLs and types, try all + // of them + for (AccessDescription ad : aia.getAccessDescriptions()) + { + // we are only interested in the issuer certificate, + // not in OCSP urls the like + if (!ad.getAccessMethod().equals( + AccessDescription.id_ad_caIssuers)) + continue; + + GeneralName gn = ad.getAccessLocation(); + if (!(gn.getTagNo() == + GeneralName.uniformResourceIdentifier + && gn.getName() instanceof DERIA5String)) + continue; + + URI uri = + new URI(((DERIA5String) gn.getName()).getString()); + // only http(s) urls; LDAP is taken care of in the + // default implementation + if (!(uri.getScheme().equalsIgnoreCase("http") || uri + .getScheme().equals("https"))) + continue; + + if (logger.isDebugEnabled()) + logger + .debug("Downloading parent certificate for <" + + current.getSubjectDN() + + "> from <" + + uri + + ">"); + + try + { + InputStream is = + HttpUtils.openURLConnection(uri.toString()) + .getContent(); + X509Certificate cert = + (X509Certificate) certFactory + .generateCertificate(is); + if(!cert.getIssuerDN().equals(cert.getSubjectDN())) + { + newChain.add(cert); + foundParent = true; + current = cert; + break; // an AD was valid, ignore others + } + else + logger.debug("Parent is self-signed, ignoring"); + } + catch (Exception e) + { + logger.debug("Could not download from <" + uri + + ">"); + } + } + chainLookupCount++; + } + while (foundParent && chainLookupCount < 10); + chain = newChain.toArray(chain); + return chain; + } + }; + } + + protected class BrowserLikeHostnameMatcher + implements CertificateMatcher + { + public void verify(Iterable identitiesToTest, + X509Certificate cert) throws CertificateException + { + // check whether one of the hostname is present in the + // certificate + boolean oneMatched = false; + for(String identity : identitiesToTest) + { + try + { + org.apache.http.conn.ssl.SSLSocketFactory + .BROWSER_COMPATIBLE_HOSTNAME_VERIFIER + .verify(identity, cert); + oneMatched = true; + break; + } + catch (SSLException e) + {} + } + + if (!oneMatched) + throw new CertificateException("None of <" + + identitiesToTest + + "> matched the cert with CN=" + + cert.getSubjectDN()); + } + } + + protected class EMailAddressMatcher + implements CertificateMatcher + { + public void verify(Iterable identitiesToTest, + X509Certificate cert) throws CertificateException + { + // check if the certificate contains the E-Mail address(es) + // in the SAN(s) + //TODO: extract address from DN (E-field) too? + boolean oneMatched = false; + Iterable emails = getSubjectAltNames(cert, 6); + for(String identity : identitiesToTest) + { + for(String email : emails) + { + if(identity.equalsIgnoreCase(email)) + { + oneMatched = true; + break; + } + } + } + if(!oneMatched) + throw new CertificateException( + "The peer provided certificate with Subject <" + + cert.getSubjectDN() + + "> contains no SAN for <" + + identitiesToTest + ">"); + } + } + + /** + * Asks the user whether he trusts the supplied chain of certificates. + * + * @param chain The chain of the certificates to check with user. + * @param message A text that describes why the verification failed. + * @return The result of the user interaction. One of + * {@link CertificateService#DO_NOT_TRUST}, + * {@link CertificateService#TRUST_THIS_SESSION_ONLY}, + * {@link CertificateService#TRUST_ALWAYS} + */ + protected int verify(final X509Certificate[] chain, final String message) + { + if(config.getBoolean(PNAME_NO_USER_INTERACTION, false)) + return DO_NOT_TRUST; + + final VerifyCertificateDialog dialog = + new VerifyCertificateDialog(chain, null, message); + try + { + // show the dialog in the swing thread and wait for the user + // choice + SwingUtilities.invokeAndWait(new Runnable() + { + public void run() + { + dialog.setVisible(true); + } + }); + } + catch (Exception e) + { + logger.error("Cannot show certificate verification dialog", e); + return DO_NOT_TRUST; + } + + if(!dialog.isTrusted) + return DO_NOT_TRUST; + else if(dialog.alwaysTrustCheckBox.isSelected()) + return TRUST_ALWAYS; + else + return TRUST_THIS_SESSION_ONLY; + } + + /** + * 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 + */ + static String getThumbprint(Certificate 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(); + } + + /** + * Gets the SAN (Subject Alternative Name) of the specified type. + * + * @param cert the certificate to extract from + * @param altNameType The type to be returned + * @return SAN of the type + * + *
+     * GeneralName ::= CHOICE {
+     *                 otherName                   [0]   OtherName,
+     *                 rfc822Name                  [1]   IA5String,
+     *                 dNSName                     [2]   IA5String,
+     *                 x400Address                 [3]   ORAddress,
+     *                 directoryName               [4]   Name,
+     *                 ediPartyName                [5]   EDIPartyName,
+     *                 uniformResourceIdentifier   [6]   IA5String,
+     *                 iPAddress                   [7]   OCTET STRING,
+     *                 registeredID                [8]   OBJECT IDENTIFIER
+     *              }
+     * 
+     */
+    private static Iterable getSubjectAltNames(X509Certificate cert,
+        int altNameType)
+    {
+        Collection> altNames = null;
+        try
+        {
+            altNames = cert.getSubjectAlternativeNames();
+        }
+        catch (CertificateParsingException e)
+        {
+            return Collections.emptyList();
+        }
+
+        List matchedAltNames = new LinkedList();
+        for (List item : altNames)
+        {
+            if (item.contains(altNameType))
+            {
+                Integer type = (Integer) item.get(0);
+                if (type.intValue() == altNameType)
+                    matchedAltNames.add((String) item.get(1));
+            }
+        }
+        return matchedAltNames;
+    }
+}
diff --git a/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java b/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java
index 5818cec25..827cf677e 100644
--- a/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java
+++ b/src/net/java/sip/communicator/impl/certificate/CertificateVerificationActivator.java
@@ -53,8 +53,8 @@ public void start(BundleContext bc) throws Exception
         bundleContext = bc;
 
         bundleContext.registerService(
-            CertificateVerificationService.class.getName(),
-            new CertificateVerificationServiceImpl(),
+            CertificateService.class.getName(),
+            new CertificateServiceImpl(),
             null);
     }
 
diff --git a/src/net/java/sip/communicator/impl/certificate/CertificateVerificationServiceImpl.java b/src/net/java/sip/communicator/impl/certificate/CertificateVerificationServiceImpl.java
index 73cd39bb6..a011dc151 100644
--- a/src/net/java/sip/communicator/impl/certificate/CertificateVerificationServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/certificate/CertificateVerificationServiceImpl.java
@@ -32,9 +32,11 @@
  * Asks the user for permission for the
  * certificates which are for some reason not valid and not globally trusted.
  *
+ * @deprecated Use the new {@link CertificateService}
  * @author Damian Minkov
  * @author Yana Stamcheva
  */
+@Deprecated
 public class CertificateVerificationServiceImpl
     implements CertificateVerificationService
 {
diff --git a/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java b/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java
new file mode 100644
index 000000000..c5cdbe1f0
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/certificate/VerifyCertificateDialog.java
@@ -0,0 +1,659 @@
+/*
+ * 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.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.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * Dialog that is shown to the user when a certificate verification failed.
+ */
+class VerifyCertificateDialog
+    extends SIPCommDialog
+{
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 0L;
+
+    private ResourceManagementService R = CertificateVerificationActivator
+        .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;
+
+    /**
+     * The maximum height that we allow message dialogs to have.
+     */
+    private static final int MAX_MSG_PANE_HEIGHT = 800;
+
+    /**
+     * The certificate to show.
+     */
+    Certificate cert;
+
+    /**
+     * A text that describes why the verification failed.
+     */
+    String message;
+
+    /**
+     * The certificate panel.
+     */
+    TransparentPanel certPanel;
+
+    /**
+     * This dialog content pane.
+     */
+    TransparentPanel contentPane;
+
+    /**
+     * Whether certificate description is shown.
+     */
+    boolean certOpened = false;
+
+    /**
+     * The button to show certificate description.
+     */
+    JButton certButton;
+
+    /**
+     * The check box if checked permanently stored the certificate
+     * which will be always trusted.
+     */
+    SIPCommCheckBox alwaysTrustCheckBox = new SIPCommCheckBox(
+        R.getI18NString("service.gui.ALWAYS_TRUST"),
+        false);
+
+    /**
+     * Whether the user trusts this certificate.
+     */
+    boolean isTrusted = false;
+
+    /**
+     * Creates the dialog.
+     *
+     * @param certs the certificates list
+     * @param title The title of the dialog; when null the resource
+     * service.gui.CERT_DIALOG_TITLE is loaded.
+     * @param message A text that describes why the verification failed.
+     */
+    public VerifyCertificateDialog( Certificate[] certs,
+                                    String title, String message)
+    {
+        super(false);
+
+        setTitle(title != null ? title : 
+            R.getI18NString("service.gui.CERT_DIALOG_TITLE"));
+        setModal(true);
+
+        // for now shows only the first certificate from the chain
+        this.cert = certs[0];
+        this.message = message;
+
+        setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+
+        init();
+
+        setLocationRelativeTo(getParent());
+    }
+
+    /**
+     * Inits the dialog initial display.
+     */
+    private void init()
+    {
+        this.getContentPane().setLayout(new BorderLayout());
+
+        contentPane =
+            new TransparentPanel(new BorderLayout(5, 5));
+
+        TransparentPanel northPanel =
+            new TransparentPanel(new BorderLayout(5, 5));
+        northPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
+
+        JLabel imgLabel = new JLabel(
+            R.getImage("service.gui.icons.WARNING_ICON"));
+        imgLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+        northPanel.add(imgLabel, BorderLayout.WEST);
+
+        StyledHTMLEditorPane descriptionPane = new StyledHTMLEditorPane();
+        descriptionPane.setOpaque(false);
+        descriptionPane.setEditable(false);
+        descriptionPane.setContentType("text/html");
+        descriptionPane.setText(message);
+        descriptionPane.setSize(
+                    new Dimension(MAX_MSG_PANE_WIDTH, MAX_MSG_PANE_HEIGHT));
+        int height = descriptionPane.getPreferredSize().height;
+        descriptionPane.setPreferredSize(
+                    new Dimension(MAX_MSG_PANE_WIDTH, height));
+
+        northPanel.add(descriptionPane, BorderLayout.CENTER);
+        contentPane.add(northPanel, BorderLayout.NORTH);
+
+        certPanel = new TransparentPanel();
+        contentPane.add(certPanel, BorderLayout.CENTER);
+
+        TransparentPanel southPanel =
+            new TransparentPanel(new BorderLayout());
+        contentPane.add(southPanel, BorderLayout.SOUTH);
+
+        certButton = new JButton();
+        certButton.setText(R.getI18NString("service.gui.SHOW_CERT"));
+        certButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e)
+            {
+                actionShowCertificate();
+            }
+        });
+        TransparentPanel firstButonPanel =
+            new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
+        firstButonPanel.add(certButton);
+        southPanel.add(firstButonPanel, BorderLayout.WEST);
+
+        TransparentPanel secondButonPanel =
+            new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+        JButton cancelButton = new JButton(
+            R.getI18NString("service.gui.CANCEL"));
+
+        cancelButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e)
+            {
+                actionCancel();
+            }
+        });
+        JButton continueButton = new JButton(
+            R.getI18NString("service.gui.CONTINUE_ANYWAY"));
+
+        continueButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e)
+            {
+                actionContinue();
+            }
+        });
+        secondButonPanel.add(continueButton);
+        secondButonPanel.add(cancelButton);
+        southPanel.add(secondButonPanel, BorderLayout.EAST);
+
+        this.getContentPane().add(contentPane, BorderLayout.CENTER);
+
+        pack();
+    }
+
+    /**
+     * Action when shoe certificate button is clicked.
+     */
+    private void actionShowCertificate()
+    {
+        if(certOpened)
+        {
+            certPanel.removeAll();
+            certButton.setText(R.getI18NString("service.gui.SHOW_CERT"));
+
+            certPanel.revalidate();
+            certPanel.repaint();
+            pack();
+            certOpened = false;
+            setLocationRelativeTo(getParent());
+            return;
+        }
+
+        certPanel.setLayout(new BorderLayout());
+        certPanel.add(alwaysTrustCheckBox, BorderLayout.NORTH);
+
+        Component certInfoPane = null;
+        if(cert instanceof X509Certificate)
+        {
+            certInfoPane = getX509DisplayComponent((X509Certificate)cert);
+        }
+        else
+        {
+            JTextArea textArea = new JTextArea();
+            textArea.setOpaque(false);
+            textArea.setEditable(false);
+            textArea.setText(cert.toString());
+            certInfoPane = textArea;
+        }
+
+        final JScrollPane certScroll = new JScrollPane(certInfoPane);
+        certScroll.setPreferredSize(new Dimension(300, 300));
+        certPanel.add(certScroll, BorderLayout.CENTER);
+
+        SwingUtilities.invokeLater(new Runnable()
+        {
+            public void run()
+            {
+                certScroll.getVerticalScrollBar().setValue(0);
+            }
+        });
+
+        certButton.setText(R.getI18NString("service.gui.HIDE_CERT"));
+
+        certPanel.revalidate();
+        certPanel.repaint();
+        // restore default values for prefered size,
+        // as we have resized its components let it calculate
+        // that size
+        setPreferredSize(null);
+        pack();
+        certOpened = true;
+        setLocationRelativeTo(getParent());
+    }
+
+    /**
+     * Action when cancel button is clicked.
+     */
+    private void actionCancel()
+    {
+        isTrusted = false;
+        dispose();
+    }
+
+    /**
+     * Action when continue is clicked.
+     */
+    private void actionContinue()
+    {
+        isTrusted = true;
+        dispose();
+    }
+
+    /**
+     * Called when dialog closed or escape pressed.
+     * @param isEscaped is escape button pressed.
+     */
+    protected void close(boolean isEscaped)
+    {
+        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 03a8ee754..c9c5ed368 100644
--- a/src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf
+++ b/src/net/java/sip/communicator/impl/certificate/certificate.manifest.mf
@@ -11,8 +11,12 @@ Import-Package: org.osgi.framework,
  net.java.sip.communicator.service.fileaccess,
  net.java.sip.communicator.service.configuration,
  net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.service.httputil,
  javax.net.ssl,
  javax.security.auth.x500,
  javax.naming,
  javax.naming.ldap,
- javax.swing
+ javax.swing,
+ org.apache.http.conn.ssl,
+ org.bouncycastle.asn1,
+ org.bouncycastle.asn1.x509
\ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
index bc05e9808..cabb4ed9a 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
@@ -231,7 +231,7 @@ public class ProtocolProviderServiceJabberImpl
     /**
      * The service we use to interact with user.
      */
-    private CertificateVerificationService guiVerification;
+    private CertificateService guiVerification;
 
     /**
      * Used with tls connecting when certificates are not trusted
@@ -241,12 +241,6 @@ public class ProtocolProviderServiceJabberImpl
      */
     private boolean abortConnecting = false;
 
-    /**
-     * Shows whether we have already checked the certificate for current server.
-     * In case we use TLS.
-     */
-    private boolean certChecked = false;
-
     /**
      * Flag indicating are we currently executing connectAndLogin method.
      */
@@ -333,15 +327,15 @@ else if(connection.isConnected() && connection.isAuthenticated())
      * Return the certificate verification service impl.
      * @return the CertificateVerification service.
      */
-    private CertificateVerificationService getCertificateVerificationService()
+    private CertificateService getCertificateVerificationService()
     {
         if(guiVerification == null)
         {
             ServiceReference guiVerifyReference
                 = JabberActivator.getBundleContext().getServiceReference(
-                    CertificateVerificationService.class.getName());
+                    CertificateService.class.getName());
             if(guiVerifyReference != null)
-                guiVerification = (CertificateVerificationService)
+                guiVerification = (CertificateService)
                     JabberActivator.getBundleContext().getService(
                         guiVerifyReference);
         }
@@ -844,22 +838,20 @@ private ConnectState connectAndLogin(
 
         try
         {
-            CertificateVerificationService gvs =
+            CertificateService cvs =
                 getCertificateVerificationService();
-            if(gvs != null)
+            if(cvs != null)
             {
                 connection.setCustomTrustManager(
-                    new HostTrustManager(gvs.getTrustManager(
-                        JabberActivator.getResources().getI18NString(
-                            "service.gui.CERT_DIALOG_DESCRIPTION_TXT",
-                            new String[]{ JabberActivator.getResources().
-                                getSettingsString(
-                                        "service.gui.APPLICATION_NAME"),
-                                address, Integer.toString(serverPort)
-                            }
+                    new HostTrustManager(
+                        cvs.getTrustManager(
+                            Arrays.asList(new String[]{
+                                serviceName,
+                                "_xmpp-client._tcp." + serviceName
+                            })
                         )
                     )
-                ));
+                );
             }
         }
         catch(GeneralSecurityException e)
@@ -870,7 +862,7 @@ private ConnectState connectAndLogin(
         if(debugger == null)
             debugger = new SmackPacketDebugger();
 
-        // setts the debugger
+        // sets the debugger
         debugger.setConnection(connection);
         connection.addPacketListener(debugger, null);
         connection.addPacketInterceptor(debugger, null);
@@ -1901,13 +1893,9 @@ public void checkClientTrusted(X509Certificate[] chain, String authType)
         public void checkServerTrusted(X509Certificate[] chain, String authType)
             throws CertificateException
         {
-            if(certChecked)
-                return;
-
             abortConnecting = true;
             try
             {
-                certChecked = true;
                 tm.checkServerTrusted(chain, authType);
             }
             catch(CertificateException e)
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
index 44e2522da..6c4eee7d1 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
@@ -68,7 +68,7 @@ public class ProtocolProviderServiceSipImpl
     /**
      * The AddressFactory used to create URLs ans Address objects.
      */
-    private AddressFactory addressFactory;
+    private AddressFactoryEx addressFactory;
 
     /**
      * The HeaderFactory used to create SIP message headers.
@@ -1258,8 +1258,8 @@ public ArrayList getLocalViaHeaders(SipURI intendedDestination)
 
             int localPort = srcListeningPoint.getPort();
             String transport = srcListeningPoint.getTransport();
-            if (ListeningPoint.TCP.equalsIgnoreCase(transport))
-                //|| ListeningPoint.TLS.equalsIgnoreCase(transport)
+            if (ListeningPoint.TCP.equalsIgnoreCase(transport)
+                || ListeningPoint.TLS.equalsIgnoreCase(transport))
             {
                 InetSocketAddress localSockAddr
                     = sipStackSharing.getLocalAddressForDestination(
@@ -1488,7 +1488,7 @@ public String getContactAddressCustomParamValue()
      *
      * @return the AddressFactory used to create URLs ans Address objects.
      */
-    public AddressFactory getAddressFactory()
+    public AddressFactoryEx getAddressFactory()
     {
         return addressFactory;
     }
@@ -1886,6 +1886,22 @@ public InetSocketAddress getOutboundProxy()
         return this.outboundProxySocketAddress;
     }
 
+    /**
+     * Compares an InetAddress against the active outbound proxy. The comparison
+     * is by reference, not equals.
+     * 
+     * @param addressToTest The addres to test.
+     * @return True when the InetAddress is the same as the outbound proxy.
+     */
+    public boolean matchesInetAddress(InetAddress addressToTest)
+    {
+        // if the proxy is not yet initialized then this is not the provider that
+        // caused this comparison
+        if(outboundProxySocketAddress == null)
+            return false;
+        return addressToTest == outboundProxySocketAddress.getAddress();
+    }
+
     /**
      * In case we are using an outbound proxy this method returns the transport
      * we are using to connect to it. The method returns null
@@ -1915,7 +1931,9 @@ void initOutboundProxy(SipAccountID accountID, int ix)
                         PROXY_ADDRESS);
         boolean proxyAddressAndPortEntered = false;
 
-        if(proxyAddressStr == null || proxyAddressStr.trim().length() == 0)
+        if(proxyAddressStr == null || proxyAddressStr.trim().length() == 0
+            || accountID.getAccountPropertyBoolean(
+                ProtocolProviderFactory.PROXY_AUTO_CONFIG, false))
         {
             String userID =  accountID.getAccountPropertyString(
                     ProtocolProviderFactory.USER_ID);
@@ -2907,6 +2925,7 @@ public void resolveSipAddress(
             // resulting InetSocketAddress because its constructor
             // suppresses UnknownHostException-s and we want to know if
             // something goes wrong.
+            @SuppressWarnings("unused")
             InetAddress addressObj = InetAddress.getByName(address);
         }
     }
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java
index fe3e74904..d6f3a9cd4 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java
@@ -11,7 +11,9 @@
 import net.java.sip.communicator.service.packetlogging.*;
 import org.osgi.framework.*;
 
+import net.java.sip.communicator.service.certificate.*;
 import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.fileaccess.*;
 import net.java.sip.communicator.service.gui.*;
 import net.java.sip.communicator.service.neomedia.*;
 import net.java.sip.communicator.service.hid.*;
@@ -38,8 +40,10 @@ public class SipActivator
     private static MediaService         mediaService          = null;
     private static VersionService       versionService        = null;
     private static UIService            uiService             = null;
-    private static HIDService            hidService           = null;
+    private static HIDService           hidService            = null;
     private static PacketLoggingService packetLoggingService  = null;
+    private static CertificateService   certService           = null;
+    private static FileAccessService    fileService           = null;
 
     /**
      * The resource service. Used for checking for default values
@@ -68,7 +72,7 @@ public void start(BundleContext context) throws Exception
 
         SipActivator.bundleContext = context;
 
-        sipProviderFactory = new ProtocolProviderFactorySipImpl();
+        sipProviderFactory = createProtocolProviderFactory();
 
         /*
          * Install the UriHandler prior to registering the factory service in
@@ -88,6 +92,34 @@ public void start(BundleContext context) throws Exception
         if (logger.isDebugEnabled())
             logger.debug("SIP Protocol Provider Factory ... [REGISTERED]");
     }
+    
+    /**
+     * Creates the ProtocolProviderFactory for this protocol.
+     * @return The created factory.
+     */
+    protected ProtocolProviderFactorySipImpl createProtocolProviderFactory()
+    {
+    	return new ProtocolProviderFactorySipImpl();
+    }
+
+    /**
+     * Return the certificate verification service impl.
+     * @return the CertificateVerification service.
+     */
+    public static CertificateService getCertificateVerificationService()
+    {
+        if(certService == null)
+        {
+            ServiceReference guiVerifyReference
+                = bundleContext.getServiceReference(
+                    CertificateService.class.getName());
+            if(guiVerifyReference != null)
+                certService = (CertificateService)
+                    bundleContext.getService(guiVerifyReference);
+        }
+
+        return certService;
+    }
 
     /**
      * Returns a reference to a ConfigurationService implementation currently
@@ -269,6 +301,21 @@ public static PacketLoggingService getPacketLogging()
         }
         return packetLoggingService;
     }
+    
+    /**
+     * Return the file access service impl.
+     * @return the FileAccess Service.
+     */
+    public static FileAccessService getFileAccessService()
+    {
+        if(fileService == null)
+        {
+        	fileService = ServiceUtils.getService(
+        		bundleContext, FileAccessService.class);
+        }
+
+        return fileService;
+    }
 
     /**
      * Called when this bundle is stopped so the Framework can perform the
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 4e0d948db..348c17ad1 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
@@ -8,9 +8,11 @@
 
 import java.io.*;
 import java.net.*;
+import java.security.cert.*;
 import java.text.*;
 import java.util.*;
 
+import javax.net.ssl.*;
 import javax.sip.*;
 import javax.sip.address.*;
 import javax.sip.header.*;
@@ -250,8 +252,16 @@ void register()
         catch (Exception exc)
         {
             if(exc.getCause() instanceof SocketException
-               || exc.getCause() instanceof IOException)
+               || exc.getCause() instanceof IOException
+               || exc.getCause() instanceof SSLHandshakeException)
             {
+                if(exc.getCause().getCause() instanceof CertificateException)
+                {
+                    setRegistrationState(RegistrationState.UNREGISTERED
+                        , RegistrationStateChangeEvent.REASON_USER_REQUEST
+                        , exc.getMessage());
+                    return;
+                }
                 if(sipProvider.registerUsingNextAddress())
                     return;
             }
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipStackProperties.java b/src/net/java/sip/communicator/impl/protocol/sip/SipStackProperties.java
index 5a7433b41..7c39afad1 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipStackProperties.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipStackProperties.java
@@ -7,8 +7,9 @@
 package net.java.sip.communicator.impl.protocol.sip;
 
 import java.util.*;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
+import java.util.logging.*;
+
+import net.java.sip.communicator.impl.protocol.sip.net.*;
 
 /**
  * The properties used at the creation of the JAIN-SIP stack.
@@ -291,6 +292,6 @@ else if (logLevel.equals(Level.OFF.getName()))
         this.setProperty(NSPNAME_SERVER_LOGGER, NSPVALUE_SERVER_LOGGER);
 
         this.setProperty("gov.nist.javax.sip.NETWORK_LAYER", 
-            "net.java.sip.communicator.impl.protocol.sip.net.SslNetworkLayer");
+            SslNetworkLayer.class.getName());
     }
 }
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
new file mode 100644
index 000000000..5221a9f22
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
@@ -0,0 +1,214 @@
+/*
+ * 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.impl.protocol.sip.net;
+
+import java.security.cert.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.sip.address.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Matcher that extracts certificate identities according to RFC5922, Section
+ * 7.1 and compares them with the rules from Section 7.2 and 7.3.
+ * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false
+ * 
+ * @author Ingo Bauersachs
+ */
+public class RFC5922Matcher
+    implements CertificateMatcher
+{
+    /**
+     * When set to true, enables strict validation of the hostname according to
+     * RFC5922 Section
+     * 7.2
+     */
+    public final static String PNAME_STRICT_RFC5922 =
+        "net.java.sip.communicator.sip.tls.STRICT_RFC5922";
+
+    private ProtocolProviderServiceSipImpl provider;
+
+    /**
+     * Creates a new instance of this class.
+     * @param provider The SIP Provider to which this matcher belongs.
+     */
+    public RFC5922Matcher(ProtocolProviderServiceSipImpl provider)
+    {
+        this.provider = provider;
+    }
+
+    /** Our class logger. */
+    private static final Logger logger = Logger
+        .getLogger(CertificateMatcher.class);
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * net.java.sip.communicator.service.certificate.CertificateMatcher#verify
+     * (java.lang.Iterable, java.security.cert.X509Certificate)
+     */
+    public void verify(Iterable identitiesToTest, X509Certificate cert)
+        throws CertificateException
+    {
+        boolean strict = SipActivator.getConfigurationService()
+            .getBoolean(PNAME_STRICT_RFC5922, false);
+
+        // if any of the identities is contained in the certificate we're good
+        boolean oneMatched = false;
+        Iterable certIdentities = extractCertIdentities(cert);
+        for (String identity : identitiesToTest)
+        {
+            // check if the intended hostname is contained in one of the
+            // hostnames of the certificate according to
+            // http://tools.ietf.org/html/rfc5922#section-7.2
+            for(String dnsName : certIdentities)
+            {
+                try
+                {
+                    if(NetworkUtils.compareDnsNames(dnsName, identity) == 0)
+                    {
+                        // one of the hostnames matched, we're good to go
+                        return;
+                    }
+
+                    if(!strict
+                        // is a wildcard name
+                        && dnsName.startsWith("*.")
+                        // contains at least two dots (*.example.com) 
+                        && identity.indexOf(".") < identity.lastIndexOf(".")
+                        // compare *.example.com stripped to example.com with
+                        // - foo.example.com stripped to example.com 
+                        // - foo.bar.example.com to bar.example.com
+                        && NetworkUtils.compareDnsNames(
+                            dnsName.substring(2),
+                            identity.substring(identity.indexOf(".")+1)) == 0)
+                    {
+                        // the wildcard matched, we're good to go
+                        return;
+                    }
+                }
+                catch (ParseException e)
+                {} // we don't care - this hostname did not match
+            }
+        }
+        if (!oneMatched)
+            throw new CertificateException("None of <" + identitiesToTest
+                + "> matched by the rules of RFC5922 to the cert with CN="
+                + cert.getSubjectDN());
+    }
+
+    private Iterable extractCertIdentities(X509Certificate cert)
+    {
+        List certIdentities = new ArrayList();
+        Collection> subjAltNames = null;
+        try
+        {
+            subjAltNames = cert.getSubjectAlternativeNames();
+        }
+        catch (CertificateParsingException ex)
+        {
+            logger.error("Error parsing TLS certificate", ex);
+        }
+        // subjAltName types are defined in rfc2459
+        final Integer dnsNameType = 2;
+        final Integer uriNameType = 6;
+        if (subjAltNames != null)
+        {
+            if (logger.isDebugEnabled())
+                logger.debug("found subjAltNames: " + subjAltNames);
+
+            // First look for a URI in the subjectAltName field
+            for (List altName : subjAltNames)
+            {
+                // 0th position is the alt name type
+                // 1st position is the alt name data
+                if (altName.get(0).equals(uriNameType))
+                {
+                    SipURI altNameUri;
+                    try
+                    {
+                        altNameUri =
+                            provider.getAddressFactory().createSipURI(
+                                (String) altName.get(1));
+                        // only sip URIs are allowed
+                        if (!"sip".equals(altNameUri.getScheme()))
+                            continue;
+                        // user certificates are not allowed
+                        if (altNameUri.getUser() != null)
+                            continue;
+                        String altHostName = altNameUri.getHost();
+                        if (logger.isDebugEnabled())
+                        {
+                            logger.debug("found uri " + altName.get(1)
+                                + ", hostName " + altHostName);
+                        }
+                        certIdentities.add(altHostName);
+                    }
+                    catch (ParseException e)
+                    {
+                        logger.error("certificate contains invalid uri: "
+                            + altName.get(1));
+                    }
+                }
+
+            }
+            // DNS An implementation MUST accept a domain name system
+            // identifier as a SIP domain identity if and only if no other
+            // identity is found that matches the "sip" URI type described
+            // above.
+            if (certIdentities.isEmpty())
+            {
+                for (List altName : subjAltNames)
+                {
+                    if (altName.get(0).equals(dnsNameType))
+                    {
+                        if (logger.isDebugEnabled())
+                            logger.debug("found dns " + altName.get(1));
+                        certIdentities.add(altName.get(1).toString());
+                    }
+                }
+            }
+        }
+        else
+        {
+            // If and only if the subjectAltName does not appear in the
+            // certificate, the implementation MAY examine the CN field of the
+            // certificate. If a valid DNS name is found there, the
+            // implementation MAY accept this value as a SIP domain identity.
+            String dname = cert.getSubjectDN().getName();
+            String cname = "";
+            try
+            {
+                Pattern EXTRACT_CN =
+                    Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*");
+                Matcher matcher = EXTRACT_CN.matcher(dname);
+                if (matcher.matches())
+                {
+                    cname = matcher.group(1);
+                    if (logger.isDebugEnabled())
+                    {
+                        logger.debug("found CN: " + cname + " from DN: "
+                            + dname);
+                    }
+                    certIdentities.add(cname);
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.error("exception while extracting CN", ex);
+            }
+        }
+        return certIdentities;
+    }
+}
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 53af81bb4..c955f065d 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
@@ -9,6 +9,7 @@
 import java.io.*;
 import java.net.*;
 import java.security.*;
+import java.util.*;
 
 import javax.net.ssl.*;
 
@@ -16,58 +17,51 @@
 
 import net.java.sip.communicator.impl.protocol.sip.*;
 import net.java.sip.communicator.service.certificate.*;
-import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.Logger;
+
 import org.osgi.framework.*;
 
 /**
- * Manages jain-sip socket creating. When dealing with ssl sockets we interact
+ * Manages jain-sip socket creation. When dealing with ssl sockets we interact
  * with the user when the certificate for some reason is not trusted.
- *
+ * 
  * @author Damian Minkov
+ * @author Ingo Bauersachs
  */
 public class SslNetworkLayer
-    implements NetworkLayer,
-               ServiceListener
+    implements NetworkLayer
 {
-    /**
+     /**
      * Our class logger.
      */
-    private static final Logger logger =
-        Logger.getLogger(SslNetworkLayer.class);
+     private static final Logger logger =
+         Logger.getLogger(SslNetworkLayer.class);
 
     /**
      * The service we use to interact with user.
      */
-    private CertificateVerificationService certificateVerification;
+    private CertificateService certificateVerification;
 
     /**
      * Creates the network layer.
-     * 
-     * @throws GeneralSecurityException
-     * @throws FileNotFoundException
-     * @throws IOException
      */
     public SslNetworkLayer()
-        throws GeneralSecurityException, 
-        FileNotFoundException,
-        IOException
     {
-        SipActivator.getBundleContext().addServiceListener(this);
+        ServiceReference guiVerifyReference =
+            SipActivator.getBundleContext().getServiceReference(
+                CertificateService.class.getName());
 
-        ServiceReference guiVerifyReference
-            = SipActivator.getBundleContext().getServiceReference(
-                CertificateVerificationService.class.getName());
-
-        if(guiVerifyReference != null)
-            certificateVerification
-                = (CertificateVerificationService)SipActivator.getBundleContext()
-                    .getService(guiVerifyReference);
+        if (guiVerifyReference != null)
+            certificateVerification =
+                (CertificateService) SipActivator.getBundleContext().getService(
+                    guiVerifyReference);
     }
 
     /**
-     * Creates a server with the specified port, listen backlog,
-     * and local IP address to bind to.
-     * Comparable to "new java.net.ServerSocket(port,backlog,bindAddress);"
+     * Creates a server with the specified port, listen backlog, and local IP
+     * address to bind to. Comparable to
+     * "new java.net.ServerSocket(port,backlog,bindAddress);"
      * 
      * @param port the port
      * @param backlog backlog
@@ -83,12 +77,12 @@ public ServerSocket createServerSocket(int port, int backlog,
     }
 
     /**
-     * Creates a stream socket and connects it to the specified
-     * port number at the specified IP address.
-     *
+     * Creates a stream socket and connects it to the specified port number at
+     * the specified IP address.
+     * 
      * @param address the address to connect.
      * @param port the port to connect.
-     * @return the socket 
+     * @return the socket
      * @throws IOException problem creating socket.
      */
     public Socket createSocket(InetAddress address, int port)
@@ -99,9 +93,8 @@ public Socket createSocket(InetAddress address, int port)
 
     /**
      * Constructs a datagram socket and binds it to any available port on the
-     * local host machine.
-     * Comparable to "new java.net.DatagramSocket();"
-     *
+     * local host machine. Comparable to "new java.net.DatagramSocket();"
+     * 
      * @return the datagram socket
      * @throws SocketException problem creating socket.
      */
@@ -112,12 +105,12 @@ public DatagramSocket createDatagramSocket()
     }
 
     /**
-     * Creates a datagram socket, bound to the specified local address. 
+     * Creates a datagram socket, bound to the specified local address.
      * Comparable to "new java.net.DatagramSocket(port,laddr);"
-     *
+     * 
      * @param port local port to use
      * @param laddr local address to bind
-     * @return the datagram socket 
+     * @return the datagram socket
      * @throws SocketException problem creating socket.
      */
     public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
@@ -127,8 +120,8 @@ public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
     }
 
     /**
-     * Creates an SSL server with the specified port, listen backlog, 
-     * and local IP address to bind to.
+     * Creates an SSL server with the specified port, listen backlog, and local
+     * IP address to bind to.
      * 
      * @param port the port to listen to
      * @param backlog backlog
@@ -140,111 +133,99 @@ public SSLServerSocket createSSLServerSocket(int port, int backlog,
             InetAddress bindAddress) 
         throws IOException
     {
-        return (SSLServerSocket) getSSLServerSocketFactory(
-            bindAddress.getHostName(), port).createServerSocket(
-                port, backlog, bindAddress);
+        return (SSLServerSocket) getSSLServerSocketFactory()
+            .createServerSocket(port, backlog, bindAddress);
     }
 
     /**
      * Creates a ssl server socket factory.
-     * @param address the address.
-     * @param port the port 
+     * 
      * @return the server socket factory.
      * @throws IOException problem creating factory.
      */
-    private SSLServerSocketFactory getSSLServerSocketFactory(
-        String address, int port)
+    private SSLServerSocketFactory getSSLServerSocketFactory()
         throws IOException
     {
-        return getSSLContext(SipActivator.getResources().
-                getI18NString(
-                    "service.gui.CERT_DIALOG_CLIENT_DESCRIPTION_TXT",
-                    new String[]
-                    {
-                        SipActivator.getResources()
-                            .getSettingsString("service.gui.APPLICATION_NAME")
-                    })
-                ).getServerSocketFactory();
+        try
+        {
+            return certificateVerification.getSSLContext()
+                .getServerSocketFactory();
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new IOException(e.getMessage());
+        }
     }
 
     /**
-     * Creates the ssl context used to create ssl socket factories. Used
-     * to install our custom trust manager which knows the address
-     * we are connecting to.
-     * @param address the address we are connecting to.
-     * @param port the port
-     * @return the ssl context.
-     * @throws IOException problem creating ssl context.
+     * Creates ssl socket factory.
+     * 
+     * @return the socket factory.
+     * @throws IOException problem creating ssl socket factory.
      */
-    private SSLContext getSSLContext(String address, int port)
+    private SSLSocketFactory getSSLSocketFactory(InetAddress address)
         throws IOException
     {
-        if(certificateVerification != null)
-            return certificateVerification.getSSLContext(address, port);
-        else
+        ProtocolProviderServiceSipImpl provider = null;
+        for (ProtocolProviderServiceSipImpl pps : ProtocolProviderServiceSipImpl
+            .getAllInstances())
         {
-            try
+            if (pps.matchesInetAddress(address))
             {
-                SSLContext sslContext;
-                sslContext = SSLContext.getInstance("TLS");
-                String algorithm = KeyManagerFactory.getDefaultAlgorithm();
-                TrustManagerFactory tmFactory =
-                    TrustManagerFactory.getInstance(algorithm);
-                KeyManagerFactory kmFactory =
-                    KeyManagerFactory.getInstance(algorithm);
-                SecureRandom secureRandom   = new SecureRandom();
-                secureRandom.nextInt();
-                tmFactory.init((KeyStore)null);
-                kmFactory.init(null, null);
-                sslContext.init(kmFactory.getKeyManagers(),
-                    tmFactory.getTrustManagers(), secureRandom);
-
-                return sslContext;
+                provider = pps;
+                break;
             }
-            catch (Throwable e)
+        }
+        if (provider == null)
+            throw new IOException(
+                "The provider that requested "
+                + "the SSL Socket could not be found");
+        try
+        {
+            ArrayList identities = new ArrayList(2);
+            SipAccountID id = (SipAccountID) provider.getAccountID();
+            // if the proxy is configured manually, the entered name is valid
+            // for the X.509 certificate
+            if(!id.getAccountPropertyBoolean(
+                ProtocolProviderFactory.PROXY_AUTO_CONFIG, false))
+            {
+                String proxy = id.getAccountPropertyString(
+                    ProtocolProviderFactory.PROXY_ADDRESS);
+                if(proxy != null)
+                    identities.add(proxy);
+                if (logger.isDebugEnabled())
+                    logger.debug("Added <" + proxy
+                        + "> to list of valid SIP TLS server identities.");
+            }
+            // the domain part of the user id is always valid
+            String userID =
+                id.getAccountPropertyString(ProtocolProviderFactory.USER_ID);
+            int index = userID.indexOf('@');
+            if (index > -1)
             {
-                throw new IOException("Cannot init SSLContext: " +
-                    e.getMessage());
+                identities.add(userID.substring(index + 1));
+                if (logger.isDebugEnabled())
+                    logger.debug("Added <" + userID.substring(index + 1)
+                        + "> to list of valid SIP TLS server identities.");
             }
+
+            return certificateVerification.getSSLContext(
+                certificateVerification.getTrustManager(
+                    identities,
+                    null,
+                    new RFC5922Matcher(provider)
+                )).getSocketFactory();
         }
-    }
-    
-    /**
-     * Creates the ssl context used to create ssl socket factories. Used
-     * to install our custom trust manager which knows the address
-     * we are connecting to.
-     * @param message the message to show on the verification GUI
-     * @return the ssl context.
-     * @throws IOException problem creating ssl context.
-     */
-    private SSLContext getSSLContext(String message)
-        throws IOException
-    {
-        if(certificateVerification != null)
-            return certificateVerification.getSSLContext(message);
-        else
+        catch (GeneralSecurityException e)
         {
-            //goes to the non-service case which doesn't use the a message/port
-        	return getSSLContext(null, 0);
+            throw new IOException(e.getMessage());
         }
     }
 
     /**
-     * Creates ssl socket factory.
-     * @param address the address we are connecting to.
-     * @param port the port we use.
-     * @return the socket factory.
-     * @throws IOException problem creating ssl socket factory.
-     */
-    private SSLSocketFactory getSSLSocketFactory(String address, int port)
-        throws IOException
-    {
-        return getSSLContext(address, port).getSocketFactory();
-    }
-
-    /**
-     * Creates a stream SSL socket and connects it to the specified
-     * port number at the specified IP address.
+     * Creates a stream SSL socket and connects it to the specified port number
+     * at the specified IP address.
+     * 
      * @param address the address we are connecting to.
      * @param port the port we use.
      * @return the socket.
@@ -253,13 +234,14 @@ private SSLSocketFactory getSSLSocketFactory(String address, int port)
     public SSLSocket createSSLSocket(InetAddress address, int port)
         throws IOException
     {
-        return (SSLSocket) getSSLSocketFactory(
-            address.getCanonicalHostName(), port).createSocket(address, port);
+        return (SSLSocket) getSSLSocketFactory(address).createSocket(address,
+            port);
     }
 
     /**
-     * Creates a stream SSL socket and connects it to the specified
-     * port number at the specified IP address.
+     * Creates a stream SSL socket and connects it to the specified port number
+     * at the specified IP address.
+     * 
      * @param address the address we are connecting to.
      * @param port the port we use.
      * @param myAddress the local address to use
@@ -270,15 +252,15 @@ public SSLSocket createSSLSocket(InetAddress address, int port,
             InetAddress myAddress) 
         throws IOException
     {
-        return (SSLSocket) getSSLSocketFactory(
-            address.getCanonicalHostName(), port).createSocket(address, port,
-                myAddress, 0);
+        return (SSLSocket) getSSLSocketFactory(address).createSocket(address,
+            port, myAddress, 0);
     }
 
     /**
      * Creates a stream socket and connects it to the specified port number at
-     * the specified IP address.
-     * Comparable to "new java.net.Socket(address, port,localaddress);"
+     * the specified IP address. Comparable to
+     * "new java.net.Socket(address, port,localaddress);"
+     * 
      * @param address the address to connect to.
      * @param port the port we use.
      * @param myAddress the local address to use.
@@ -298,18 +280,18 @@ public Socket createSocket(InetAddress address, int port,
     /**
      * Creates a new Socket, binds it to myAddress:myPort and connects it to
      * address:port.
-     *
+     * 
      * @param address the InetAddress that we'd like to connect to.
      * @param port the port that we'd like to connect to
-     * @param myAddress the address that we are supposed to bind on or null
-     *        for the "any" address.
+     * @param myAddress the address that we are supposed to bind on or null for
+     *            the "any" address.
      * @param myPort the port that we are supposed to bind on or 0 for a random
-     * one.
-     *
+     *            one.
+     * 
      * @return a new Socket, bound on myAddress:myPort and connected to
-     * address:port.
+     *         address:port.
      * @throws IOException if binding or connecting the socket fail for a reason
-     * (exception relayed from the corresponding Socket methods)
+     *             (exception relayed from the corresponding Socket methods)
      */
     public Socket createSocket(InetAddress address, int port,
                     InetAddress myAddress, int myPort)
@@ -319,7 +301,7 @@ public Socket createSocket(InetAddress address, int port,
             return new Socket(address, port, myAddress, myPort);
         else if (port != 0)
         {
-            //myAddress is null (i.e. any)  but we have a port number
+            // myAddress is null (i.e. any) but we have a port number
             Socket sock = new Socket();
             sock.bind(new InetSocketAddress(port));
             sock.connect(new InetSocketAddress(address, port));
@@ -328,27 +310,4 @@ else if (port != 0)
         else
             return new Socket(address, port);
     }
-
-    /**
-     * Listens for newly registered services. Looking for
-     * CertificateVerificationService.
-     * 
-     * @param event the new event.
-     */
-    public void serviceChanged(ServiceEvent event)
-    {
-        Object sService = SipActivator.getBundleContext().getService(
-            event.getServiceReference());
-
-        // we don't care if the source service is not a plugin component
-        if (! (sService instanceof CertificateVerificationService))
-        {
-            return;
-        }
-
-        if(event.getType() == ServiceEvent.REGISTERED)
-            certificateVerification = (CertificateVerificationService)sService;
-        else if(event.getType() == ServiceEvent.UNREGISTERING)
-            certificateVerification = null;
-    }
 }
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java
index 4f9ee0f0e..3eac45b92 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java
@@ -17,8 +17,9 @@
 import org.apache.http.client.methods.*;
 import org.apache.http.conn.*;
 import org.apache.http.conn.scheme.*;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.*;
+import org.apache.http.impl.client.*;
 import org.apache.http.params.*;
 import org.osgi.framework.*;
 
@@ -102,7 +103,7 @@ public abstract class BaseHttpXCapClient implements HttpXCapClient
     /**
      * The service we use to interact with user regarding certificates.
      */
-    private CertificateVerificationService certificateVerification;
+    private CertificateService certificateVerification;
 
     /**
      * Creates an instance of this XCAP client.
@@ -113,11 +114,11 @@ public BaseHttpXCapClient()
 
         ServiceReference guiVerifyReference
             = SipActivator.getBundleContext().getServiceReference(
-                CertificateVerificationService.class.getName());
+                CertificateService.class.getName());
 
         if(guiVerifyReference != null)
             certificateVerification
-                = (CertificateVerificationService)SipActivator.getBundleContext()
+                = (CertificateService)SipActivator.getBundleContext()
                     .getService(guiVerifyReference);
     }
 
@@ -413,6 +414,7 @@ protected URI getResourceURI(XCapResourceId resourceId)
      */
     private DefaultHttpClient createHttpClient()
     {
+        //TODO: move to HttpUtil
         DefaultHttpClient httpClient = new DefaultHttpClient();
         try
         {
@@ -421,14 +423,13 @@ private DefaultHttpClient createHttpClient()
             // for approval
             ClientConnectionManager ccm = httpClient.getConnectionManager();
             SchemeRegistry sr = ccm.getSchemeRegistry();
-            SSLContext ctx = certificateVerification.getSSLContext(
-                uri.getHost(), uri.getPort() == -1 ? 443 : uri.getPort());
-            org.apache.http.conn.ssl.SSLSocketFactory ssf
-                = new org.apache.http.conn.ssl.SSLSocketFactory(
-                        ctx, new HostNameResolverImpl());
-            ssf.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory
-                .ALLOW_ALL_HOSTNAME_VERIFIER);
-            sr.register(new Scheme("https", ssf, 443));
+            SSLContext ctx =
+                certificateVerification.getSSLContext(
+                    certificateVerification.getTrustManager(uri.getHost()));
+            org.apache.http.conn.ssl.SSLSocketFactory ssf =
+                new org.apache.http.conn.ssl.SSLSocketFactory(ctx,
+                    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+            sr.register(new Scheme("https", 443, ssf));
         }
         catch(Throwable e)
         {
@@ -442,7 +443,7 @@ private DefaultHttpClient createHttpClient()
 
     /**
      * Creates XCAP response from HTTP response.
-     * If HTTP code is 200, 201 or 409 the HTTP content would be readed.
+     * If HTTP code is 200, 201 or 409 the HTTP content would be read.
      *
      * @param response the HTTP response.
      * @return the XCAP response.
@@ -540,42 +541,4 @@ protected String getXCapErrorMessage(XCapHttpResponse response)
             return null;
         }
     }
-
-    /**
-     * Using deprecated HostNameResolver as quick fix,
-     * Make apache http client lib to use our resolver when connecting to hosts.
-     */
-    private class HostNameResolverImpl
-        implements HostNameResolver
-    {
-        /**
-         * Resolves given hostname to its IP address
-         *
-         * @param hostname the hostname.
-         * @return IP address.
-         * @throws java.io.IOException
-         */
-        public InetAddress resolve(String hostname)
-            throws IOException
-        {
-            try
-            {
-                InetSocketAddress addr = null;
-                // use port 80 as we need only the address
-                if(ProtocolProviderServiceSipImpl.checkPreferIPv6Addresses())
-                    addr = NetworkUtils.getAAAARecord(hostname, 80);
-                else
-                    addr = NetworkUtils.getARecord(hostname, 80);
-
-                if(addr != null)
-                    return addr.getAddress();
-                else
-                    return null;
-            }
-            catch(java.text.ParseException e)
-            {
-                throw new IOException(e.getMessage());
-            }
-        }
-    }
 }
\ No newline at end of file
diff --git a/src/net/java/sip/communicator/service/certificate/CertificateMatcher.java b/src/net/java/sip/communicator/service/certificate/CertificateMatcher.java
new file mode 100644
index 000000000..54df494c8
--- /dev/null
+++ b/src/net/java/sip/communicator/service/certificate/CertificateMatcher.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+import java.security.cert.*;
+
+public interface CertificateMatcher
+{
+    /**
+     * Implementations check whether one of the supplied identities is
+     * contained in the certificate.
+     * 
+     * @param identitiesToTest The that are compared against the certificate.
+     * @param cert The X.509 certificate that was supplied by the server or
+     *            client.
+     * @throws CertificateException When any certificate parsing fails.
+     */
+    public void verify(Iterable identitiesToTest, X509Certificate cert)
+        throws CertificateException;
+}
diff --git a/src/net/java/sip/communicator/service/certificate/CertificateService.java b/src/net/java/sip/communicator/service/certificate/CertificateService.java
new file mode 100644
index 000000000..3c4e4c6b7
--- /dev/null
+++ b/src/net/java/sip/communicator/service/certificate/CertificateService.java
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+import java.security.GeneralSecurityException;
+import java.security.cert.*;
+
+import javax.net.ssl.*;
+
+/**
+ * A service which implementors will ask the user for permission for the
+ * certificates which are for some reason not valid and not globally trusted.
+ *
+ * @author Damian Minkov
+ * @author Ingo Bauersachs
+ */
+public interface CertificateService
+{
+    /**
+     * Property for always trust mode. When enabled certificate check is
+     * skipped.
+     */
+    public final static String PNAME_ALWAYS_TRUST =
+        "net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED";
+
+    /**
+     * When set to true, the certificate check is performed. If the check fails
+     * the user is not asked and the error is directly reported to the calling
+     * service.
+     */
+    public final static String PNAME_NO_USER_INTERACTION =
+        "net.java.sip.communicator.service.tls.NO_USER_INTERACTION";
+
+    /**
+     * Result of user interaction. User does not trust this certificate.
+     */
+    public final static int DO_NOT_TRUST = 0;
+
+    /**
+     * Result of user interaction. User will always trust this certificate.
+     */
+    public final static int TRUST_ALWAYS = 1;
+
+    /**
+     * Result of user interaction. User will trust this certificate
+     * only for the current session.
+     */
+    public final static int TRUST_THIS_SESSION_ONLY = 2;
+
+    /**
+     * Get an SSL Context that validates certificates based on the JRE default
+     * check and asks the user when the JRE check fails.
+     * 
+     * CAUTION: Only the certificate itself is validated, no check is performed
+     * whether it is valid for a specific server or client.
+     * 
+     * @return An SSL context based on a user confirming trust manager.
+     * @throws GeneralSecurityException
+     */
+    public SSLContext getSSLContext() throws GeneralSecurityException;
+
+    /**
+     * Get an SSL Context with the specified trustmanager.
+     * 
+     * @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(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
+     * null is passed as the identityToTest then no check is
+     * performed whether the certificate is valid for a specific server or
+     * client. The passed identities are checked by applying a behavior similar
+     * to the on regular browsers use.
+     * 
+     * @param identitiesToTest when not null, the values are assumed
+     *            to be hostnames for invocations of checkServerTrusted and
+     *            e-mail addresses for invocations of checkClientTrusted
+     * @return TrustManager to use in an SSLContext
+     * @throws GeneralSecurityException
+     */
+    public X509TrustManager getTrustManager(Iterable identitiesToTest)
+        throws GeneralSecurityException;
+
+    /**
+     * @see #getTrustManager(Iterable)
+     * 
+     * @param identityToTest when not null, the value is assumed to
+     *            be a hostname for invocations of checkServerTrusted and an
+     *            e-mail address for invocations of checkClientTrusted
+     * @return TrustManager to use in an SSLContext
+     * @throws GeneralSecurityException
+     */
+    public X509TrustManager getTrustManager(String identityToTest)
+        throws GeneralSecurityException;
+
+    /**
+     * @see #getTrustManager(Iterable, CertificateMatcher, CertificateMatcher)
+     * 
+     * @param identityToTest The identity to match against the supplied
+     *            verifiers.
+     * @param clientVerifier The verifier to use in calls to checkClientTrusted
+     * @param serverVerifier The verifier to use in calls to checkServerTrusted
+     * @return TrustManager to use in an SSLContext
+     * @throws GeneralSecurityException
+     */
+    public X509TrustManager getTrustManager(
+        final String identityToTest,
+        final CertificateMatcher clientVerifier,
+        final CertificateMatcher serverVerifier)
+        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
+     * null is passed as the identityToTest then no check is
+     * performed whether the certificate is valid for a specific server or
+     * client.
+     * 
+     * @param identitiesToTest The identities to match against the supplied
+     *            verifiers.
+     * @param clientVerifier The verifier to use in calls to checkClientTrusted
+     * @param serverVerifier The verifier to use in calls to checkServerTrusted
+     * @return TrustManager to use in an SSLContext
+     * @throws GeneralSecurityException
+     */
+    public X509TrustManager getTrustManager(
+        final Iterable identitiesToTest,
+        final CertificateMatcher clientVerifier,
+        final CertificateMatcher serverVerifier)
+        throws GeneralSecurityException;
+
+    /**
+     * Adds a certificate to the local trust store.
+     * 
+     * @param cert The certificate to add to the trust store.
+     * @param trustMode Whether to trust the certificate permanently or only
+     *            for the current session.
+     * @throws CertificateException when the thumbprint could not be calculated
+     */
+    public void addCertificateToTrust(Certificate cert, String trustFor,
+        int trustMode) throws CertificateException;
+}
\ No newline at end of file
diff --git a/src/net/java/sip/communicator/service/certificate/CertificateVerificationService.java b/src/net/java/sip/communicator/service/certificate/CertificateVerificationService.java
index 93586e3c5..d7630490c 100644
--- a/src/net/java/sip/communicator/service/certificate/CertificateVerificationService.java
+++ b/src/net/java/sip/communicator/service/certificate/CertificateVerificationService.java
@@ -15,8 +15,11 @@
  * A service which implementors will ask the user for permission for the
  * certificates which are for some reason not valid and not globally trusted.
  *
+ * @deprecated Use the new {@link CertificateService}
+ *
  * @author Damian Minkov
  */
+@Deprecated
 public interface CertificateVerificationService
 {
     /**
diff --git a/src/net/java/sip/communicator/service/httputil/HttpUtilActivator.java b/src/net/java/sip/communicator/service/httputil/HttpUtilActivator.java
index 8ef5bd6b2..9398f78d7 100644
--- a/src/net/java/sip/communicator/service/httputil/HttpUtilActivator.java
+++ b/src/net/java/sip/communicator/service/httputil/HttpUtilActivator.java
@@ -19,7 +19,7 @@ public class HttpUtilActivator
     /**
      * The service we use to interact with user regarding certificates.
      */
-    private static CertificateVerificationService guiCertificateVerification;
+    private static CertificateService guiCertificateVerification;
 
     /**
      * Reference to the credentials service
@@ -46,16 +46,16 @@ public class HttpUtilActivator
      * Return the certificate verification service impl.
      * @return the CertificateVerification service.
      */
-    public static CertificateVerificationService
+    public static CertificateService
         getCertificateVerificationService()
     {
         if(guiCertificateVerification == null)
         {
             ServiceReference guiVerifyReference
                 = bundleContext.getServiceReference(
-                CertificateVerificationService.class.getName());
+                    CertificateService.class.getName());
             if(guiVerifyReference != null)
-                guiCertificateVerification = (CertificateVerificationService)
+                guiCertificateVerification = (CertificateService)
                     bundleContext.getService(guiVerifyReference);
         }
 
diff --git a/src/net/java/sip/communicator/service/httputil/HttpUtils.java b/src/net/java/sip/communicator/service/httputil/HttpUtils.java
index af96587a9..4b51e7e32 100644
--- a/src/net/java/sip/communicator/service/httputil/HttpUtils.java
+++ b/src/net/java/sip/communicator/service/httputil/HttpUtils.java
@@ -30,6 +30,7 @@
 import javax.net.ssl.*;
 import java.io.*;
 import java.net.*;
+import java.security.*;
 import java.util.*;
 
 /**
@@ -519,7 +520,7 @@ else if(i == passwordParamIx && creds != null)
     private static DefaultHttpClient getHttpClient(
         String usernamePropertyName,
         String passwordPropertyName,
-        String address)
+        final String address)
         throws IOException
     {
         HttpParams params = new BasicHttpParams();
@@ -534,23 +535,25 @@ private static DefaultHttpClient getHttpClient(
                 + "/"
                 + System.getProperty("sip-communicator.version"));
 
-        SSLContext sslCtx = HttpUtilActivator
-                .getCertificateVerificationService().getSSLContext(
-                    HttpUtilActivator.getResources().
-                        getI18NString(
-                            "service.gui.CERT_DIALOG_DESCRIPTION_TXT",
-                        new String[]{
-                            HttpUtilActivator.getResources().getSettingsString(
-                                "service.gui.APPLICATION_NAME"),
-                            address,
-                            Integer.toString(443)
-                        }
-                    ));
+        SSLContext sslCtx;
+        try
+        {
+            sslCtx = HttpUtilActivator.getCertificateVerificationService()
+                .getSSLContext(
+                    HttpUtilActivator.getCertificateVerificationService()
+                        .getTrustManager(address));
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new IOException(e.getMessage());
+        }
 
-        Scheme sch = new Scheme("https", 443,
-            new SSLSocketFactory(
-                    sslCtx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER));
+        Scheme sch =
+            new Scheme("https", 443, new SSLSocketFactory(sslCtx,
+                SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER));
         httpClient.getConnectionManager().getSchemeRegistry().register(sch);
+        //TODO: wrap the SSLSocketFactory to use our own DNS resolution
+        //TODO: register socketfactory for http to use our own DNS resolution
 
         // set proxy from default jre settings
         ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
diff --git a/src/net/java/sip/communicator/util/NetworkUtils.java b/src/net/java/sip/communicator/util/NetworkUtils.java
index 7997bc3f2..e6ee8d03a 100644
--- a/src/net/java/sip/communicator/util/NetworkUtils.java
+++ b/src/net/java/sip/communicator/util/NetworkUtils.java
@@ -1190,4 +1190,28 @@ public static void reloadDnsResolverConfig()
             }
         }
     }
+
+    /**
+     * Compares two DNS names against each other. Helper method to avoid the
+     * export of DNSJava.
+     * @param dns1 The first DNS name
+     * @param dns2 The DNS name that is compared against dns1
+     * @return The value 0 if dns2 is a name equivalent to dns1;
+     * a value less than 0 if dns2 is less than dns1 in the canonical ordering,
+     * and a value greater than 0 if dns2 is greater than dns1 in the canonical
+     * ordering.
+     * @throws ParseException if the dns1 or dns2 is not a DNS Name
+     */
+    public static int compareDnsNames(String dns1, String dns2)
+        throws ParseException
+    {
+        try
+        {
+            return Name.fromString(dns1).compareTo(Name.fromString(dns2));
+        }
+        catch(TextParseException e)
+        {
+            throw new ParseException(e.getMessage(), 0);
+        }
+    }
 }
diff --git a/test/net/java/sip/communicator/slick/protocol/jabber/TestAccountInstallation.java b/test/net/java/sip/communicator/slick/protocol/jabber/TestAccountInstallation.java
index 7545dd3de..03de373d1 100644
--- a/test/net/java/sip/communicator/slick/protocol/jabber/TestAccountInstallation.java
+++ b/test/net/java/sip/communicator/slick/protocol/jabber/TestAccountInstallation.java
@@ -82,7 +82,7 @@ public void testInstallAccount()
                 bc.getService(confReference);
 
         configurationService.setProperty(
-            CertificateVerificationService.ALWAYS_TRUST_MODE_ENABLED_PROP_NAME,
+            CertificateService.PNAME_ALWAYS_TRUST,
             Boolean.TRUE);
 
         //Keep the reference for later usage.