diff --git a/configure.ac b/configure.ac
index 20de382..45aba16 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,7 @@ GLIB_REQUIRED=2.27.2
 GNUTLS_REQUIRED=2.8.5
 GTK_REQUIRED=2.91.3
 KEYRING_REQUIRED=2.26.0
+GCR_REQUIRED=2.91.4
 LIBCANBERRA_GTK_REQUIRED=0.25
 LIBNOTIFY_REQUIRED=0.7.0
 TELEPATHY_FARSIGHT_REQUIRED=0.0.14
@@ -163,7 +164,7 @@ PKG_CHECK_MODULES(EMPATHY,
    gtk+-3.0 >= $GTK_REQUIRED
    libcanberra-gtk3 >= $LIBCANBERRA_GTK_REQUIRED
    libnotify >= $LIBNOTIFY_REQUIRED
-   gcr-3 >= $KEYRING_REQUIRED
+   gcr-3 >= $GCR_REQUIRED
 ])
 
 # -----------------------------------------------------------
@@ -200,38 +201,6 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[Gettext package name])
 AM_GLIB_GNU_GETTEXT
 
 # -----------------------------------------------------------
-# Make CA certificates path configurable
-# Stolen from GIO's TLS
-# -----------------------------------------------------------
-AC_MSG_CHECKING([location of system Certificate Authority list])
-AC_ARG_WITH(ca-file,
-            [AC_HELP_STRING([--with-ca-file=@<:@path@:>@],
-                            [path to system Certificate Authority list])])
-if test "$with_ca_file" = "no"; then
-    AC_MSG_RESULT([disabled])
-else
-    if test -z "$with_ca_file"; then
-        for f in /etc/pki/tls/certs/ca-bundle.crt \
-                 /etc/ssl/certs/ca-certificates.crt; do
-            if test -f "$f"; then
-                with_ca_file="$f"
-            fi
-        done
-        if test -z "$with_ca_file"; then
-            AC_MSG_ERROR([could not find. Use --with-ca-file=path to set, or --without-ca-file to disable])
-        fi
-    fi
-
-    AC_MSG_RESULT($with_ca_file)
-    if ! test -f "$with_ca_file"; then
-        AC_MSG_ERROR([No such file '$with_ca_file'. Use --with-ca-file=path to set, or --without-ca-file to disable])
-    fi
-    GTLS_SYSTEM_CA_FILE="$with_ca_file"
-
-    AC_DEFINE_UNQUOTED([GTLS_SYSTEM_CA_FILE], ["$GTLS_SYSTEM_CA_FILE"], [path to system Certificate Authority list])
-fi
-
-# -----------------------------------------------------------
 # Connectivity integration
 # -----------------------------------------------------------
 AC_ARG_WITH(connectivity,
@@ -528,7 +497,6 @@ Configure summary:
 	Compiler Flags..............:  ${CFLAGS} ${ERROR_CFLAGS}
 	Prefix......................:  ${prefix}
 	Coding style checks.........:  ${ENABLE_CODING_STYLE_CHECKS}
-	CA Cert Path................:  ${GTLS_SYSTEM_CA_FILE}
 
     Features:
 	Spell checking (enchant)....:  ${have_enchant}
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index 7e35929..1e64c75 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -152,7 +152,6 @@ libempathy_gtk_headers =			\
 libempathy_gtk_la_SOURCES =			\
 	$(libempathy_gtk_handwritten_source)	\
 	$(libempathy_gtk_headers)		\
-	gcr-simple-certificate.c gcr-simple-certificate.h \
 	totem-subtitle-encoding.c totem-subtitle-encoding.h
 
 # do not distribute generated files
diff --git a/libempathy-gtk/empathy-tls-dialog.c b/libempathy-gtk/empathy-tls-dialog.c
index 9213ac4..f09d6c5 100644
--- a/libempathy-gtk/empathy-tls-dialog.c
+++ b/libempathy-gtk/empathy-tls-dialog.c
@@ -26,7 +26,7 @@
 #include <gcr/gcr.h>
 #include <telepathy-glib/util.h>
 
-#include "gcr-simple-certificate.h"
+#include <gcr/gcr.h>
 
 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
 #include <libempathy/empathy-debug.h>
diff --git a/libempathy-gtk/gcr-simple-certificate.c b/libempathy-gtk/gcr-simple-certificate.c
deleted file mode 100644
index 072cbac..0000000
--- a/libempathy-gtk/gcr-simple-certificate.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* 
- * gnome-keyring
- * 
- * Copyright (C) 2008 Stefan Walter
- *
- * This file is copied from the gnome-keyring gcr library, which can be
- * found at git://git.gnome.org/gnome-keyring
- * 
- * This program is free software; you can redistribute it and/or modify 
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *  
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *  
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  
- */
-
-#include "config.h"
-
-#include <gcr/gcr.h>
-#include "gcr-simple-certificate.h"
-
-#include <string.h>
-
-struct _GcrSimpleCertificatePrivate {
-	guchar *owned_data;
-	gsize n_owned_data;
-};
-
-static void gcr_certificate_iface (GcrCertificateIface *iface); 
-G_DEFINE_TYPE_WITH_CODE (GcrSimpleCertificate, gcr_simple_certificate, G_TYPE_OBJECT, 
-                         G_IMPLEMENT_INTERFACE (GCR_TYPE_CERTIFICATE, gcr_certificate_iface));
-
-/* -----------------------------------------------------------------------------
- * OBJECT 
- */
-
-static void
-gcr_simple_certificate_init (GcrSimpleCertificate *self)
-{
-	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SIMPLE_CERTIFICATE, GcrSimpleCertificatePrivate);
-}
-
-static void
-gcr_simple_certificate_finalize (GObject *obj)
-{
-	GcrSimpleCertificate *self = GCR_SIMPLE_CERTIFICATE (obj);
-	
-	g_free (self->pv->owned_data);
-	self->pv->owned_data = NULL;
-	self->pv->n_owned_data = 0;
-
-	G_OBJECT_CLASS (gcr_simple_certificate_parent_class)->finalize (obj);
-}
-
-static void
-gcr_simple_certificate_set_property (GObject *obj, guint prop_id, const GValue *value, 
-                                     GParamSpec *pspec)
-{
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-gcr_simple_certificate_get_property (GObject *obj, guint prop_id, GValue *value, 
-                                     GParamSpec *pspec)
-{
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-gcr_simple_certificate_class_init (GcrSimpleCertificateClass *klass)
-{
-	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-    
-	gobject_class->finalize = gcr_simple_certificate_finalize;
-	gobject_class->set_property = gcr_simple_certificate_set_property;
-	gobject_class->get_property = gcr_simple_certificate_get_property;
-	
-	g_type_class_add_private (gobject_class, sizeof (GcrSimpleCertificatePrivate));
-}
-
-static const guchar* 
-gcr_simple_certificate_real_get_der_data (GcrCertificate *base, gsize *n_data)
-{
-	GcrSimpleCertificate *self = GCR_SIMPLE_CERTIFICATE (base);
-	
-	g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
-	g_return_val_if_fail (n_data, NULL);
-	g_return_val_if_fail (self->pv->owned_data, NULL);
-	
-	/* This is called when we're not a base class */
-	*n_data = self->pv->n_owned_data;
-	return self->pv->owned_data;
-}
-
-static void 
-gcr_certificate_iface (GcrCertificateIface *iface) 
-{
-	iface->get_der_data = (gpointer)gcr_simple_certificate_real_get_der_data;
-}
-
-/* -----------------------------------------------------------------------------
- * PUBLIC 
- */
-
-GcrCertificate*
-gcr_simple_certificate_new (const guchar *data, gsize n_data)
-{
-	GcrSimpleCertificate *cert;
-	
-	g_return_val_if_fail (data, NULL);
-	g_return_val_if_fail (n_data, NULL);
-	
-	cert = g_object_new (GCR_TYPE_SIMPLE_CERTIFICATE, NULL);
-	
-	cert->pv->owned_data = g_memdup (data, n_data);
-	cert->pv->n_owned_data = n_data;
-	return GCR_CERTIFICATE (cert);
-}
diff --git a/libempathy-gtk/gcr-simple-certificate.h b/libempathy-gtk/gcr-simple-certificate.h
deleted file mode 100644
index ee46b48..0000000
--- a/libempathy-gtk/gcr-simple-certificate.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 
- * gnome-keyring
- * 
- * Copyright (C) 2008 Stefan Walter
- *
- * This file is copied from the gnome-keyring gcr library, which can be
- * found at git://git.gnome.org/gnome-keyring
- * 
- * This program is free software; you can redistribute it and/or modify 
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *  
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *  
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  
- */
-
-#ifndef __GCR_SIMPLE_CERTIFICATE_H__
-#define __GCR_SIMPLE_CERTIFICATE_H__
-
-#include <gcr/gcr.h>
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define GCR_TYPE_SIMPLE_CERTIFICATE               (gcr_simple_certificate_get_type ())
-#define GCR_SIMPLE_CERTIFICATE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE, GcrSimpleCertificate))
-#define GCR_SIMPLE_CERTIFICATE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE, GcrSimpleCertificateClass))
-#define GCR_IS_SIMPLE_CERTIFICATE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE))
-#define GCR_IS_SIMPLE_CERTIFICATE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE))
-#define GCR_SIMPLE_CERTIFICATE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE, GcrSimpleCertificateClass))
-
-typedef struct _GcrSimpleCertificate GcrSimpleCertificate;
-typedef struct _GcrSimpleCertificateClass GcrSimpleCertificateClass;
-typedef struct _GcrSimpleCertificatePrivate GcrSimpleCertificatePrivate;
-
-struct _GcrSimpleCertificate {
-	GObject parent;
-	GcrSimpleCertificatePrivate *pv;
-};
-
-struct _GcrSimpleCertificateClass {
-	GObjectClass parent_class;
-};
-
-GType               gcr_simple_certificate_get_type               (void);
-
-GcrCertificate*     gcr_simple_certificate_new                    (const guchar *data,
-                                                                   gsize n_data);
-
-G_END_DECLS
-
-#endif /* __GCR_SIMPLE_CERTIFICATE_H__ */
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 9b0dcfb..c774ac9 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -8,6 +8,7 @@ AM_CPPFLAGS =                                           \
 	-DDATADIR=\""$(datadir)"\"              	\
 	-DLOCALEDIR=\""$(datadir)/locale"\"		\
 	-DG_LOG_DOMAIN=\"empathy\"			\
+	-DGCR_API_SUBJECT_TO_CHANGE			\
 	$(EMPATHY_CFLAGS)				\
 	$(GEOCLUE_CFLAGS)				\
 	$(NETWORK_MANAGER_CFLAGS)			\
@@ -106,6 +107,7 @@ nodist_libempathy_la_SOURCES =\
 
 libempathy_la_LIBADD =		\
 	$(top_builddir)/extensions/libemp-extensions.la \
+	$(GCR_LIBS) \
 	$(EMPATHY_LIBS) \
 	$(GEOCLUE_LIBS) \
 	$(NETWORK_MANAGER_LIBS) \
diff --git a/libempathy/empathy-tls-certificate.c b/libempathy/empathy-tls-certificate.c
index 931bbcc..c14deec 100644
--- a/libempathy/empathy-tls-certificate.c
+++ b/libempathy/empathy-tls-certificate.c
@@ -430,130 +430,3 @@ empathy_tls_certificate_reject_finish (EmpathyTLSCertificate *self,
 
   return TRUE;
 }
-
-static gsize
-get_exported_size (gnutls_x509_crt_t cert)
-{
-  gsize retval = 2;
-  guchar fake[2] = { 0, 0 };
-
-  /* fake an export so we get the size to allocate */
-  gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM,
-      fake, &retval);
-
-  DEBUG ("Should allocate %lu bytes", (gulong) retval);
-
-  return retval + 1;
-}
-
-void
-empathy_tls_certificate_store_ca (EmpathyTLSCertificate *self)
-{
-  GArray *last_cert;
-  gnutls_x509_crt_t cert;
-  gnutls_datum_t datum = { NULL, 0 };
-  gsize exported_len;
-  guchar *exported_cert = NULL;
-  gint res, offset;
-  gchar *user_certs_dir = NULL, *filename = NULL, *path = NULL;
-  gchar *hostname = NULL;
-  GError *error = NULL;
-  EmpathyTLSCertificatePriv *priv = GET_PRIV (self);
-
-  last_cert = g_ptr_array_index (priv->cert_data, priv->cert_data->len - 1);
-  datum.data = (guchar *) last_cert->data;
-  datum.size = last_cert->len;
-
-  gnutls_x509_crt_init (&cert);
-  gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER);
-
-  /* make sure it's self-signed, otherwise it's not a CA */
-  if (gnutls_x509_crt_check_issuer (cert, cert) <= 0)
-    {
-      DEBUG ("Can't import the CA, as it's not self-signed");
-      gnutls_x509_crt_deinit (cert);
-
-      return;
-    }
-
-  if (gnutls_x509_crt_get_ca_status (cert, NULL) <= 0)
-    {
-      DEBUG ("Can't import the CA, it's not a valid CA certificate");
-      gnutls_x509_crt_deinit (cert);
-
-      goto out;
-    }
-
-  exported_len = get_exported_size (cert);
-  exported_cert = g_malloc (sizeof (guchar) * exported_len);
-
-  res = gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM,
-      exported_cert, &exported_len);
-
-  if (res < 0)
-    {
-      DEBUG ("Failed to export the CA certificate; GnuTLS returned %d,"
-          "and should be %lu bytes long", res, (gulong) exported_len);
-      gnutls_x509_crt_deinit (cert);
-
-      goto out;
-    }
-
-  hostname = empathy_get_x509_certificate_hostname (cert);
-
-  if (hostname == NULL)
-    hostname = g_strdup ("ca");
-
-  gnutls_x509_crt_deinit (cert);
-
-  /* write the file */
-  user_certs_dir = g_build_filename (g_get_user_config_dir (),
-      "telepathy", "certs", NULL);
-
-  res = g_mkdir_with_parents (user_certs_dir, S_IRWXU | S_IRWXG);
-
-  if (res < 0)
-    {
-      DEBUG ("Failed to create the user certificate directory: %s",
-          g_strerror (errno));
-
-      goto out;
-    }
-
-  offset = 0;
-
-  do
-    {
-      g_free (path);
-
-      if (offset == 0)
-        filename = g_strdup_printf ("cert-%s", hostname);
-      else
-        filename = g_strdup_printf ("cert-%s-%d", hostname, offset);
-
-      path = g_build_filename (user_certs_dir, filename, NULL);
-
-      offset++;
-      g_free (filename);
-    }
-  while (g_file_test (path, G_FILE_TEST_EXISTS));
-
-  DEBUG ("Will save to %s", path);
-
-  g_file_set_contents (path, (const gchar *) exported_cert, exported_len,
-      &error);
-
-  if (error != NULL)
-    {
-      DEBUG ("Can't save the CA certificate to %s: %s",
-          path, error->message);
-
-      g_error_free (error);
-    }
-
- out:
-  g_free (path);
-  g_free (exported_cert);
-  g_free (user_certs_dir);
-  g_free (hostname);
-}
diff --git a/libempathy/empathy-tls-certificate.h b/libempathy/empathy-tls-certificate.h
index d9dd07d..8ad3d20 100644
--- a/libempathy/empathy-tls-certificate.h
+++ b/libempathy/empathy-tls-certificate.h
@@ -88,8 +88,6 @@ gboolean empathy_tls_certificate_reject_finish (EmpathyTLSCertificate *self,
     GAsyncResult *result,
     GError **error);
 
-void empathy_tls_certificate_store_ca (EmpathyTLSCertificate *self);
-
 G_END_DECLS
 
 #endif /* #ifndef __EMPATHY_TLS_CERTIFICATE_H__*/
diff --git a/libempathy/empathy-tls-verifier.c b/libempathy/empathy-tls-verifier.c
index ca17261..057c412 100644
--- a/libempathy/empathy-tls-verifier.c
+++ b/libempathy/empathy-tls-verifier.c
@@ -2,6 +2,7 @@
  * empathy-tls-verifier.c - Source for EmpathyTLSVerifier
  * Copyright (C) 2010 Collabora Ltd.
  * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
+ * @author Stef Walter <stefw@collabora.co.uk>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,6 +28,8 @@
 
 #include "empathy-tls-verifier.h"
 
+#include <gcr/gcr.h>
+
 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
 #include "empathy-debug.h"
 #include "empathy-utils.h"
@@ -43,17 +46,7 @@ enum {
   LAST_PROPERTY,
 };
 
-static const gchar* system_ca_paths[] = {
-  GTLS_SYSTEM_CA_FILE,
-  NULL,
-};
-
 typedef struct {
-  GPtrArray *cert_chain;
-
-  GPtrArray *trusted_ca_list;
-  GPtrArray *trusted_crl_list;
-
   EmpathyTLSCertificate *certificate;
   gchar *hostname;
 
@@ -63,20 +56,6 @@ typedef struct {
   gboolean dispose_run;
 } EmpathyTLSVerifierPriv;
 
-static gnutls_x509_crt_t *
-ptr_array_to_x509_crt_list (GPtrArray *chain)
-{
-  gnutls_x509_crt_t *retval;
-  gint idx;
-
-  retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * chain->len);
-
-  for (idx = 0; idx < (gint) chain->len; idx++)
-    retval[idx] = g_ptr_array_index (chain, idx);
-
-  return retval;
-}
-
 static gboolean
 verification_output_to_reason (gint res,
     guint verify_output,
@@ -132,62 +111,78 @@ verification_output_to_reason (gint res,
   return retval;
 }
 
-static gboolean
-verify_last_certificate (EmpathyTLSVerifier *self,
-    gnutls_x509_crt_t cert,
-    EmpTLSCertificateRejectReason *reason)
+static void
+build_certificate_list_for_gnutls (GcrCertificateChain *chain,
+        gnutls_x509_crt_t **list,
+        guint *n_list,
+        gnutls_x509_crt_t **anchors,
+        guint *n_anchors)
 {
-  guint verify_output;
-  gint res;
-  gnutls_x509_crt_t *trusted_ca_list;
-  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+  GcrCertificate *cert;
+  guint idx, length;
+  gnutls_x509_crt_t *retval;
+  gnutls_x509_crt_t gcert;
+  gnutls_datum_t datum;
+  gsize n_data;
+
+  g_assert (list);
+  g_assert (n_list);
+  g_assert (anchors);
+  g_assert (n_anchors);
+
+  *list = *anchors = NULL;
+  *n_list = *n_anchors = 0;
 
-  if (priv->trusted_ca_list->len > 0)
+  length = gcr_certificate_chain_get_length (chain);
+  retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * length);
+
+  /* Convert the main body of the chain to gnutls */
+  for (idx = 0; idx < length; ++idx)
     {
-      trusted_ca_list = ptr_array_to_x509_crt_list (priv->trusted_ca_list);
-      res = gnutls_x509_crt_verify (cert, trusted_ca_list,
-          priv->trusted_ca_list->len, 0, &verify_output);
+      cert = gcr_certificate_chain_get_certificate (chain, idx);
+      datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
+      datum.size = n_data;
 
-      DEBUG ("Checking last certificate %p against trusted CAs, output %u",
-          cert, verify_output);
+      gnutls_x509_crt_init (&gcert);
+      if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
+        g_return_if_reached ();
 
-      g_free (trusted_ca_list);
+      retval[idx] = gcert;
     }
-  else
+
+  *list = retval;
+  *n_list = length;
+
+  /* See if we have an anchor */
+  if (gcr_certificate_chain_get_status (chain) ==
+          GCR_CERTIFICATE_CHAIN_ANCHORED)
     {
-      /* check it against itself to see if it's structurally valid */
-      res = gnutls_x509_crt_verify (cert, &cert, 1, 0, &verify_output);
+      cert = gcr_certificate_chain_get_anchor (chain);
+      g_return_if_fail (cert);
 
-      DEBUG ("Checking last certificate %p against itself, output %u", cert,
-          verify_output);
+      datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
+      datum.size = n_data;
 
-      /* if it's valid, return the SelfSigned error, so that we can add it
-       * later to our trusted CAs whitelist.
-       */
-      if (res == GNUTLS_E_SUCCESS)
-        {
-          *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
-          return FALSE;
-        }
-    }
+      gnutls_x509_crt_init (&gcert);
+      if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
+        g_return_if_reached ();
 
-  return verification_output_to_reason (res, verify_output, reason);
+      retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * 1);
+      retval[0] = gcert;
+      *anchors = retval;
+      *n_anchors = 1;
+    }
 }
 
-static gboolean
-verify_certificate (EmpathyTLSVerifier *self,
-    gnutls_x509_crt_t cert,
-    gnutls_x509_crt_t issuer,
-    EmpTLSCertificateRejectReason *reason)
+static void
+free_certificate_list_for_gnutls (gnutls_x509_crt_t *list,
+        guint n_list)
 {
-  guint verify_output;
-  gint res;
-
-  res = gnutls_x509_crt_verify (cert, &issuer, 1, 0, &verify_output);
-
-  DEBUG ("Verifying %p against %p, output %u", cert, issuer, verify_output);
+  guint idx;
 
-  return verification_output_to_reason (res, verify_output, reason);
+  for (idx = 0; idx < n_list; idx++)
+    gnutls_x509_crt_deinit (list[idx]);
+  g_free (list);
 }
 
 static void
@@ -219,26 +214,90 @@ abort_verification (EmpathyTLSVerifier *self,
 }
 
 static void
-real_start_verification (EmpathyTLSVerifier *self)
+debug_certificate (GcrCertificate *cert)
+{
+    gchar *subject = gcr_certificate_get_subject_dn (cert);
+    DEBUG ("Certificate: %s", subject);
+    g_free (subject);
+}
+
+static void
+debug_certificate_chain (GcrCertificateChain *chain)
 {
-  gnutls_x509_crt_t first_cert, last_cert;
-  gint idx;
-  gboolean res = FALSE;
-  gint num_certs;
+    GEnumClass *enum_class;
+    GEnumValue *enum_value;
+    gint idx, length;
+    GcrCertificate *cert;
+
+    enum_class = G_ENUM_CLASS
+            (g_type_class_peek (GCR_TYPE_CERTIFICATE_CHAIN_STATUS));
+    enum_value = g_enum_get_value (enum_class,
+            gcr_certificate_chain_get_status (chain));
+    length = gcr_certificate_chain_get_length (chain);
+    DEBUG ("Certificate chain: length %u status %s",
+            length, enum_value ? enum_value->value_nick : "XXX");
+
+    for (idx = 0; idx < length; ++idx)
+      {
+        cert = gcr_certificate_chain_get_certificate (chain, idx);
+        debug_certificate (cert);
+      }
+}
+
+static void
+perform_verification (EmpathyTLSVerifier *self,
+        GcrCertificateChain *chain)
+{
+  gboolean ret = FALSE;
   EmpTLSCertificateRejectReason reason =
     EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+  gnutls_x509_crt_t *list, *anchors;
+  guint n_list, n_anchors;
+  guint verify_output;
+  gint res;
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
-  DEBUG ("Starting verification");
+  DEBUG ("Performing verification");
+  debug_certificate_chain (chain);
 
-  /* check if the certificate matches the hostname first. */
-  first_cert = g_ptr_array_index (priv->cert_chain, 0);
-  if (gnutls_x509_crt_check_hostname (first_cert, priv->hostname) == 0)
+  /*
+   * If the first certificate is an pinned certificate then we completely
+   * ignore the rest of the verification process.
+   */
+  if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
+    {
+      DEBUG ("Found pinned certificate for %s", priv->hostname);
+      complete_verification (self);
+      goto out;
+  }
+
+  build_certificate_list_for_gnutls (chain, &list, &n_list,
+          &anchors, &n_anchors);
+  if (list == NULL || n_list == 0) {
+      g_warn_if_reached ();
+      abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
+      goto out;
+  }
+
+  verify_output = 0;
+  res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors,
+           NULL, 0, 0, &verify_output);
+  ret = verification_output_to_reason (res, verify_output, &reason);
+
+  DEBUG ("Certificate verification gave result %d with reason %u", ret,
+          reason);
+
+  if (!ret) {
+      abort_verification (self, reason);
+      goto out;
+  }
+
+  /* now check if the certificate matches the hostname. */
+  if (gnutls_x509_crt_check_hostname (list[0], priv->hostname) == 0)
     {
       gchar *certified_hostname;
 
-      reason = EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH;
-      certified_hostname = empathy_get_x509_certificate_hostname (first_cert);
+      certified_hostname = empathy_get_x509_certificate_hostname (list[0]);
       tp_asv_set_string (priv->details,
           "expected-hostname", priv->hostname);
       tp_asv_set_string (priv->details,
@@ -248,277 +307,40 @@ real_start_verification (EmpathyTLSVerifier *self)
           certified_hostname, priv->hostname);
 
       g_free (certified_hostname);
+      abort_verification (self,
+              EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
       goto out;
     }
 
   DEBUG ("Hostname matched");
-
-  num_certs = priv->cert_chain->len;
-
-  if (priv->trusted_ca_list->len > 0)
-    {
-      /* if the last certificate is self-signed, and we have a list of
-       * trusted CAs, ignore it, as we want to check the chain against our
-       * trusted CAs list first.
-       * if we have only one certificate in the chain, don't ignore it though,
-       * as it's the CA certificate itself.
-       */
-      last_cert = g_ptr_array_index (priv->cert_chain, num_certs - 1);
-
-      if (gnutls_x509_crt_check_issuer (last_cert, last_cert) > 0 &&
-          num_certs > 1)
-        num_certs--;
-    }
-
-  for (idx = 1; idx < num_certs; idx++)
-    {
-      res = verify_certificate (self,
-          g_ptr_array_index (priv->cert_chain, idx -1),
-          g_ptr_array_index (priv->cert_chain, idx),
-          &reason);
-
-      DEBUG ("Certificate verification %d gave result %d with reason %u", idx,
-          res, reason);
-
-      if (!res)
-        {
-          abort_verification (self, reason);
-          return;
-        }
-    }
-
-  res = verify_last_certificate (self,
-      g_ptr_array_index (priv->cert_chain, num_certs - 1),
-      &reason);
-
-  DEBUG ("Last verification gave result %d with reason %u", res, reason);
-
- out:
-  if (!res)
-    {
-      abort_verification (self, reason);
-      return;
-    }
-
   complete_verification (self);
-}
-
-static gboolean
-start_verification (gpointer user_data)
-{
-  EmpathyTLSVerifier *self = user_data;
-
-  real_start_verification (self);
 
-  return FALSE;
+ out:
+  free_certificate_list_for_gnutls (list, n_list);
+  free_certificate_list_for_gnutls (anchors, n_anchors);
 }
 
 static void
-build_gnutls_cert_list (EmpathyTLSVerifier *self)
+perform_verification_cb (GObject *object,
+        GAsyncResult *res,
+        gpointer user_data)
 {
-  guint num_certs;
-  guint idx;
-  GPtrArray *certificate_data = NULL;
-  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
-  g_object_get (priv->certificate,
-      "cert-data", &certificate_data,
-      NULL);
-  num_certs = certificate_data->len;
-
-  priv->cert_chain = g_ptr_array_new_with_free_func (
-      (GDestroyNotify) gnutls_x509_crt_deinit);
-
-  for (idx = 0; idx < num_certs; idx++)
-    {
-      gnutls_x509_crt_t cert;
-      GArray *one_cert;
-      gnutls_datum_t datum = { NULL, 0 };
-
-      one_cert = g_ptr_array_index (certificate_data, idx);
-      datum.data = (guchar *) one_cert->data;
-      datum.size = one_cert->len;
-
-      gnutls_x509_crt_init (&cert);
-      gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER);
-
-      g_ptr_array_add (priv->cert_chain, cert);
-    }
-}
-
-static gint
-get_number_and_type_of_certificates (gnutls_datum_t *datum,
-    gnutls_x509_crt_fmt_t *format)
-{
-  gnutls_x509_crt_t fake;
-  guint retval = 1;
-  gint res;
-
-  res = gnutls_x509_crt_list_import (&fake, &retval, datum,
-      GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
-
-  if (res == GNUTLS_E_SHORT_MEMORY_BUFFER || res > 0)
-    {
-      DEBUG ("Found PEM, with %u certificates", retval);
-      *format = GNUTLS_X509_FMT_PEM;
-      return retval;
-    }
-
-  /* try DER */
-  res = gnutls_x509_crt_list_import (&fake, &retval, datum,
-      GNUTLS_X509_FMT_DER, 0);
-
-  if (res > 0)
-    {
-      *format = GNUTLS_X509_FMT_DER;
-      return retval;
-    }
-
-  return res;
-}
-
-static gboolean
-build_gnutls_ca_and_crl_lists (GIOSchedulerJob *job,
-    GCancellable *cancellable,
-    gpointer user_data)
-{
-  gint idx;
-  gchar *user_certs_dir;
-  GDir *dir;
   GError *error = NULL;
-  EmpathyTLSVerifier *self = user_data;
-  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
-  priv->trusted_ca_list = g_ptr_array_new_with_free_func
-    ((GDestroyNotify) gnutls_x509_crt_deinit);
-
-  for (idx = 0; idx < (gint) G_N_ELEMENTS (system_ca_paths) - 1; idx++)
-    {
-      const gchar *path;
-      gchar *contents = NULL;
-      gsize length = 0;
-      gint res, n_certs;
-      gnutls_x509_crt_t *cert_list;
-      gnutls_datum_t datum = { NULL, 0 };
-      gnutls_x509_crt_fmt_t format = 0;
-
-      path = system_ca_paths[idx];
-      g_file_get_contents (path, &contents, &length, &error);
-
-      if (error != NULL)
-        {
-          DEBUG ("Unable to read system CAs from path %s: %s", path,
-              error->message);
-          g_clear_error (&error);
-          continue;
-        }
-
-      datum.data = (guchar *) contents;
-      datum.size = length;
-      n_certs = get_number_and_type_of_certificates (&datum, &format);
-
-      if (n_certs < 0)
-        {
-          DEBUG ("Unable to parse the system CAs from path %s: GnuTLS "
-              "returned error %d", path, n_certs);
-
-          g_free (contents);
-          continue;
-        }
-
-      cert_list = g_malloc0 (sizeof (gnutls_x509_crt_t) * n_certs);
-      res = gnutls_x509_crt_list_import (cert_list, (guint *) &n_certs, &datum,
-          format, 0);
-
-      if (res < 0)
-        {
-          DEBUG ("Unable to import system CAs from path %s; "
-              "GnuTLS returned error %d", path, res);
 
-          g_free (contents);
-          continue;
-        }
-
-      DEBUG ("Successfully imported %d system CA certificates from path %s",
-          n_certs, path);
-
-      /* append the newly created cert structutes into the global GPtrArray */
-      for (idx = 0; idx < n_certs; idx++)
-        g_ptr_array_add (priv->trusted_ca_list, cert_list[idx]);
-
-      g_free (contents);
-      g_free (cert_list);
-    }
-
-  /* user certs */
-  user_certs_dir = g_build_filename (g_get_user_config_dir (),
-      "telepathy", "certs", NULL);
-  dir = g_dir_open (user_certs_dir, 0, &error);
+  GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
+  EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
 
-  if (error != NULL)
+  /* Even if building the chain fails, try verifying what we have */
+  if (!gcr_certificate_chain_build_finish (chain, res, &error))
     {
-      DEBUG ("Can't open the user certs dir at %s: %s", user_certs_dir,
-          error->message);
-
-      g_error_free (error);
+      DEBUG ("Building of certificate chain failed: %s", error->message);
+      g_clear_error (&error);
     }
-  else
-    {
-      const gchar *cert_name;
 
-      while ((cert_name = g_dir_read_name (dir)) != NULL)
-        {
-          gchar *contents = NULL, *cert_path = NULL;
-          gsize length = 0;
-          gint res;
-          gnutls_datum_t datum = { NULL, 0 };
-          gnutls_x509_crt_t cert;
-
-          cert_path = g_build_filename (user_certs_dir, cert_name, NULL);
-
-          g_file_get_contents (cert_path, &contents, &length, &error);
-
-          if (error != NULL)
-            {
-              DEBUG ("Can't open the certificate file at path %s: %s",
-                  cert_path, error->message);
-
-              g_clear_error (&error);
-              g_free (cert_path);
-              continue;
-            }
-
-          datum.data = (guchar *) contents;
-          datum.size = length;
-
-          gnutls_x509_crt_init (&cert);
-          res = gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_PEM);
-
-          if (res != GNUTLS_E_SUCCESS)
-            {
-              DEBUG ("Can't import the certificate at path %s: "
-                  "GnuTLS returned %d", cert_path, res);
-            }
-          else
-            {
-              g_ptr_array_add (priv->trusted_ca_list, cert);
-            }
-
-          g_free (contents);
-          g_free (cert_path);
-        }
+  perform_verification (self, chain);
 
-      g_dir_close (dir);
-    }
-
-  g_free (user_certs_dir);
-
-  /* TODO: do the CRL too */
-
-  g_io_scheduler_job_send_to_mainloop_async (job,
-      start_verification, self, NULL);
-
-  return FALSE;
+  /* Matches ref when staring chain build */
+  g_object_unref (self);
 }
 
 static void
@@ -587,8 +409,6 @@ empathy_tls_verifier_finalize (GObject *object)
 
   DEBUG ("%p", object);
 
-  tp_clear_pointer (&priv->trusted_ca_list, g_ptr_array_unref);
-  tp_clear_pointer (&priv->cert_chain, g_ptr_array_unref);
   tp_clear_boxed (G_TYPE_HASH_TABLE, &priv->details);
   g_free (priv->hostname);
 
@@ -596,17 +416,6 @@ empathy_tls_verifier_finalize (GObject *object)
 }
 
 static void
-empathy_tls_verifier_constructed (GObject *object)
-{
-  EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (object);
-
-  build_gnutls_cert_list (self);
-
-  if (G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed != NULL)
-    G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed (object);
-}
-
-static void
 empathy_tls_verifier_init (EmpathyTLSVerifier *self)
 {
   EmpathyTLSVerifierPriv *priv;
@@ -628,7 +437,6 @@ empathy_tls_verifier_class_init (EmpathyTLSVerifierClass *klass)
   oclass->get_property = empathy_tls_verifier_get_property;
   oclass->finalize = empathy_tls_verifier_finalize;
   oclass->dispose = empathy_tls_verifier_dispose;
-  oclass->constructed = empathy_tls_verifier_constructed;
 
   pspec = g_param_spec_object ("certificate", "The EmpathyTLSCertificate",
       "The EmpathyTLSCertificate to be verified.",
@@ -661,15 +469,37 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
     GAsyncReadyCallback callback,
     gpointer user_data)
 {
+  GcrCertificateChain *chain;
+  GcrCertificate *cert;
+  GPtrArray *cert_data = NULL;
+  GArray *data;
+  guint idx;
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
+  DEBUG ("Starting verification");
+
   g_return_if_fail (priv->verify_result == NULL);
 
+  g_object_get (priv->certificate, "cert-data", &cert_data, NULL);
+  g_return_if_fail (cert_data);
+
   priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
       callback, user_data, NULL);
 
-  g_io_scheduler_push_job (build_gnutls_ca_and_crl_lists,
-      self, NULL, G_PRIORITY_DEFAULT, NULL);
+  /* Create a certificate chain */
+  chain = gcr_certificate_chain_new ();
+  for (idx = 0; idx < cert_data->len; ++idx) {
+    data = g_ptr_array_index (cert_data, idx);
+    cert = gcr_simple_certificate_new (data->data, data->len);
+    gcr_certificate_chain_add (chain, cert);
+    g_object_unref (cert);
+  }
+
+  gcr_certificate_chain_build_async (chain, GCR_PURPOSE_CLIENT_AUTH, priv->hostname, 0,
+          NULL, perform_verification_cb, g_object_ref (self));
+
+  g_object_unref (chain);
+  g_boxed_free (TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, cert_data);
 }
 
 gboolean
@@ -703,3 +533,36 @@ empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
 
   return TRUE;
 }
+
+void
+empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self)
+{
+  GArray *data;
+  GcrCertificate *cert;
+  GPtrArray *cert_data = NULL;
+  GError *error = NULL;
+  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+
+  g_object_get (priv->certificate, "cert-data", &cert_data, NULL);
+  g_return_if_fail (cert_data);
+
+  if (!cert_data->len)
+    {
+      DEBUG ("No certificate to pin.");
+      return;
+    }
+
+  /* The first certificate in the chain is for the host */
+  data = g_ptr_array_index (cert_data, 0);
+  cert = gcr_simple_certificate_new ((gpointer)data->data, data->len);
+
+  DEBUG ("Storing pinned certificate:");
+  debug_certificate (cert);
+
+  if (!gcr_trust_add_pinned_certificate (cert, GCR_PURPOSE_CLIENT_AUTH,
+          priv->hostname, NULL, &error))
+      DEBUG ("Can't store the pinned certificate: %s", error->message);
+
+  g_object_unref (cert);
+  g_boxed_free (TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, cert_data);
+}
diff --git a/libempathy/empathy-tls-verifier.h b/libempathy/empathy-tls-verifier.h
index e73a71a..e333bc8 100644
--- a/libempathy/empathy-tls-verifier.h
+++ b/libempathy/empathy-tls-verifier.h
@@ -74,6 +74,8 @@ gboolean empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
     GHashTable **details,
     GError **error);
 
+void empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self);
+
 G_END_DECLS
 
 #endif /* #ifndef __EMPATHY_TLS_VERIFIER_H__*/
diff --git a/src/Makefile.am b/src/Makefile.am
index d57035b..8c51ded 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,6 +22,7 @@ LDADD =								\
 	$(top_builddir)/libempathy-gtk/libempathy-gtk.la	\
 	$(top_builddir)/libempathy/libempathy.la		\
 	$(top_builddir)/extensions/libemp-extensions.la		\
+	$(GCR_LIBS) 						\
 	$(EMPATHY_LIBS)						\
 	$(LIBCHAMPLAIN_LIBS)					\
 	$(WEBKIT_LIBS)
diff --git a/src/empathy-auth-client.c b/src/empathy-auth-client.c
index 17b66a5..1754016 100644
--- a/src/empathy-auth-client.c
+++ b/src/empathy-auth-client.c
@@ -90,6 +90,7 @@ tls_dialog_response_cb (GtkDialog *dialog,
   GHashTable *details = NULL;
   EmpathyTLSDialog *tls_dialog = EMPATHY_TLS_DIALOG (dialog);
   gboolean remember = FALSE;
+  EmpathyTLSVerifier *verifier = EMPATHY_TLS_VERIFIER (user_data);
 
   DEBUG ("Response %d", response_id);
 
@@ -114,7 +115,7 @@ tls_dialog_response_cb (GtkDialog *dialog,
     }
 
   if (remember)
-    empathy_tls_certificate_store_ca (certificate);
+    empathy_tls_verifier_store_exception (verifier);
 
   g_object_unref (certificate);
   g_hash_table_unref (details);
@@ -130,6 +131,7 @@ tls_dialog_response_cb (GtkDialog *dialog,
 
 static void
 display_interactive_dialog (EmpathyTLSCertificate *certificate,
+    EmpathyTLSVerifier *verifier,
     EmpTLSCertificateRejectReason reason,
     GHashTable *details)
 {
@@ -140,8 +142,9 @@ display_interactive_dialog (EmpathyTLSCertificate *certificate,
   stop_timer ();
 
   tls_dialog = empathy_tls_dialog_new (certificate, reason, details);
-  g_signal_connect (tls_dialog, "response",
-      G_CALLBACK (tls_dialog_response_cb), NULL);
+  g_signal_connect_data (tls_dialog, "response",
+      G_CALLBACK (tls_dialog_response_cb), g_object_ref (verifier),
+      (GClosureNotify)g_object_unref, 0);
 
   gtk_widget_show (tls_dialog);
 }
@@ -156,6 +159,7 @@ verifier_verify_cb (GObject *source,
   GError *error = NULL;
   EmpathyTLSCertificate *certificate = NULL;
   GHashTable *details = NULL;
+  gchar *hostname = NULL;
 
   g_object_get (source,
       "certificate", &certificate,
@@ -167,7 +171,8 @@ verifier_verify_cb (GObject *source,
   if (error != NULL)
     {
       DEBUG ("Error: %s", error->message);
-      display_interactive_dialog (certificate, reason, details);
+      display_interactive_dialog (certificate, EMPATHY_TLS_VERIFIER (source),
+              reason, details);
 
       g_error_free (error);
     }
@@ -176,6 +181,7 @@ verifier_verify_cb (GObject *source,
       empathy_tls_certificate_accept_async (certificate, NULL, NULL);
     }
 
+  g_free (hostname);
   g_object_unref (certificate);
 }
 
diff --git a/tests/.gitignore b/tests/.gitignore
index 0951740..b079812 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -7,4 +7,5 @@ empathy-chatroom-test
 empathy-chatroom-manager-test
 empathy-parser-test
 empathy-live-search-test
+empathy-tls-test
 test-report.xml
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a3bb71c..a9fc0cb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -12,6 +12,7 @@ AM_CPPFLAGS =						\
 	$(ERROR_CFLAGS)					\
 	-I$(top_srcdir)                          	\
 	-DPKGDATADIR=\""$(pkgdatadir)"\"		\
+	-DGCR_API_SUBJECT_TO_CHANGE			\
 	$(EMPATHY_CFLAGS)				\
 	$(WARN_CFLAGS)					\
 	$(DISABLE_DEPRECATED)				\
@@ -31,11 +32,15 @@ TEST_PROGS =                                     \
      empathy-chatroom-test                       \
      empathy-chatroom-manager-test               \
      empathy-parser-test                         \
-     empathy-live-search-test
+     empathy-live-search-test                    \
+     empathy-tls-test
 
 empathy_utils_test_SOURCES = empathy-utils-test.c \
      test-helper.c test-helper.h
 
+empathy_tls_test_SOURCES = empathy-tls-test.c \
+     test-helper.c test-helper.h
+
 empathy_irc_server_test_SOURCES = empathy-irc-server-test.c \
      test-helper.c test-helper.h                       \
      test-irc-helper.h test-irc-helper.c
diff --git a/tests/certificates/collabora-ca/collabora-ca.cer b/tests/certificates/collabora-ca/collabora-ca.cer
new file mode 100644
index 0000000..2842c69
Binary files /dev/null and b/tests/certificates/collabora-ca/collabora-ca.cer differ
diff --git a/tests/certificates/dhansak-collabora.cer b/tests/certificates/dhansak-collabora.cer
new file mode 100644
index 0000000..c411e7d
Binary files /dev/null and b/tests/certificates/dhansak-collabora.cer differ
diff --git a/tests/empathy-tls-test.c b/tests/empathy-tls-test.c
new file mode 100644
index 0000000..31cadfb
--- /dev/null
+++ b/tests/empathy-tls-test.c
@@ -0,0 +1,614 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libempathy/empathy-tls-certificate.h>
+#include <libempathy/empathy-tls-verifier.h>
+#include "test-helper.h"
+
+#include <gcr/gcr.h>
+
+#include <gnutls/gnutls.h>
+
+#include <telepathy-glib/dbus-properties-mixin.h>
+#include <telepathy-glib/enums.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/svc-tls.h>
+#include <telepathy-glib/svc-generic.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#define MOCK_TLS_CERTIFICATE_PATH "/mock/certificate"
+
+/* Forward decl */
+GType mock_tls_certificate_get_type (void);
+
+#define MOCK_TLS_CERTIFICATE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), mock_tls_certificate_get_type (), \
+    MockTLSCertificate))
+
+typedef struct _MockTLSCertificate {
+  GObject parent;
+  guint state;
+  GPtrArray *rejections;
+  gchar *cert_type;
+  GPtrArray *cert_data;
+} MockTLSCertificate;
+
+typedef struct _MockTLSCertificateClass {
+  GObjectClass parent;
+  TpDBusPropertiesMixinClass dbus_props_class;
+} MockTLSCertificateClass;
+
+enum {
+  PROP_0,
+  PROP_STATE,
+  PROP_REJECTIONS,
+  PROP_CERTIFICATE_TYPE,
+  PROP_CERTIFICATE_CHAIN_DATA
+};
+
+static void mock_tls_certificate_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE(MockTLSCertificate, mock_tls_certificate, G_TYPE_OBJECT,
+        G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE,
+                mock_tls_certificate_iface_init)
+        G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+                tp_dbus_properties_mixin_iface_init)
+)
+
+static void
+mock_tls_certificate_init (MockTLSCertificate *self)
+{
+  self->state = TP_TLS_CERTIFICATE_STATE_PENDING;
+  self->cert_type = g_strdup ("x509");
+  self->cert_data = g_ptr_array_new_with_free_func((GDestroyNotify) g_array_unref);
+  self->rejections = g_ptr_array_new ();
+}
+
+static void
+mock_tls_certificate_get_property (GObject *object,
+        guint property_id,
+        GValue *value,
+        GParamSpec *pspec)
+{
+  MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
+
+  switch (property_id)
+    {
+    case PROP_STATE:
+      g_value_set_uint (value, self->state);
+      break;
+    case PROP_REJECTIONS:
+      g_value_set_boxed (value, self->rejections);
+      break;
+    case PROP_CERTIFICATE_TYPE:
+      g_value_set_string (value, self->cert_type);
+      break;
+    case PROP_CERTIFICATE_CHAIN_DATA:
+      g_value_set_boxed (value, self->cert_data);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static void
+mock_tls_certificate_finalize (GObject *object)
+{
+  MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
+
+  tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
+      &self->rejections);
+  g_free (self->cert_type);
+  self->cert_type = NULL;
+  g_ptr_array_free (self->cert_data, TRUE);
+  self->cert_data = NULL;
+
+  G_OBJECT_CLASS (mock_tls_certificate_parent_class)->finalize (object);
+}
+
+static void
+mock_tls_certificate_class_init (MockTLSCertificateClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  static TpDBusPropertiesMixinPropImpl object_props[] = {
+          { "State", "state", NULL },
+          { "Rejections", "rejections", NULL },
+          { "CertificateType", "certificate-type", NULL },
+          { "CertificateChainData", "certificate-chain-data", NULL },
+          { NULL }
+  };
+
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+    { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE,
+      tp_dbus_properties_mixin_getter_gobject_properties,
+      NULL,
+      object_props,
+    },
+    { NULL }
+  };
+
+  oclass->get_property = mock_tls_certificate_get_property;
+  oclass->finalize = mock_tls_certificate_finalize;
+
+  pspec = g_param_spec_uint ("state",
+      "State of this certificate",
+      "The state of this TLS certificate.",
+      0, NUM_TP_TLS_CERTIFICATE_STATES - 1,
+      TP_TLS_CERTIFICATE_STATE_PENDING,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_STATE, pspec);
+
+  pspec = g_param_spec_boxed ("rejections",
+      "The reject reasons",
+      "The reasons why this TLS certificate has been rejected",
+      TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_REJECTIONS, pspec);
+
+  pspec = g_param_spec_string ("certificate-type",
+      "The certificate type",
+      "The type of this certificate.",
+      NULL,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec);
+
+  pspec = g_param_spec_boxed ("certificate-chain-data",
+      "The certificate chain data",
+      "The raw PEM-encoded trust chain of this certificate.",
+      TP_ARRAY_TYPE_UCHAR_ARRAY_LIST,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec);
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (oclass,
+      G_STRUCT_OFFSET (MockTLSCertificateClass, dbus_props_class));
+}
+
+static void
+mock_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *base,
+        DBusGMethodInvocation *context)
+{
+  MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
+  self->state = TP_TLS_CERTIFICATE_STATE_ACCEPTED;
+  tp_svc_authentication_tls_certificate_emit_accepted (self);
+  tp_svc_authentication_tls_certificate_return_from_accept (context);
+}
+
+static void
+mock_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *base,
+        const GPtrArray *in_Rejections,
+        DBusGMethodInvocation *context)
+{
+  MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
+  self->state = TP_TLS_CERTIFICATE_STATE_REJECTED;
+  tp_svc_authentication_tls_certificate_emit_rejected (self, in_Rejections);
+  tp_svc_authentication_tls_certificate_return_from_reject (context);
+}
+
+static void
+mock_tls_certificate_iface_init (gpointer g_iface,
+        gpointer iface_data)
+{
+  TpSvcAuthenticationTLSCertificateClass *klass =
+    (TpSvcAuthenticationTLSCertificateClass*)g_iface;
+
+  tp_svc_authentication_tls_certificate_implement_accept (klass,
+      mock_tls_certificate_accept);
+  tp_svc_authentication_tls_certificate_implement_reject (klass,
+      mock_tls_certificate_reject);
+}
+
+#if 0
+static void
+mock_tls_certificate_assert_rejected (MockTLSCertificate *self,
+        EmpTLSCertificateRejectReason reason)
+{
+  GValueArray *rejection;
+  EmpTLSCertificateRejectReason rejection_reason;
+  gchar *rejection_error;
+  GHashTable *rejection_details;
+  guint i;
+
+  g_assert (self->state == TP_TLS_CERTIFICATE_STATE_REJECTED);
+  g_assert (self->rejections);
+  g_assert (self->rejections->len > 0);
+
+  for (i = 0; i < self->rejections->len; ++i)
+    {
+      rejection = g_ptr_array_index (self->rejections, i);
+      tp_value_array_unpack (rejection, 3,
+              G_TYPE_UINT, &rejection_reason,
+              G_TYPE_STRING, &rejection_error,
+              TP_HASH_TYPE_STRING_VARIANT_MAP, &rejection_details,
+              NULL);
+      g_free (rejection_error);
+      g_hash_table_destroy (rejection_details);
+
+      if (rejection_reason == reason)
+        return;
+    }
+
+  g_assert ("Certificate was not rejected for right reason" && 0);
+}
+#endif
+
+static MockTLSCertificate*
+mock_tls_certificate_new_and_register (TpDBusDaemon *dbus,
+        const gchar *path,
+        ...)
+{
+  MockTLSCertificate *cert;
+  GError *error = NULL;
+  gchar *filename, *contents;
+  GArray *der;
+  gsize length;
+  va_list va;
+
+  cert = g_object_new (mock_tls_certificate_get_type (), NULL);
+
+  va_start (va, path);
+  while (path != NULL) {
+      filename = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
+              "tests", "certificates", path, NULL);
+      g_file_get_contents (filename, &contents, &length, &error);
+      g_assert_no_error (error);
+
+      der = g_array_sized_new (TRUE, TRUE, sizeof (guchar), length);
+      g_array_append_vals (der, contents, length);
+      g_ptr_array_add (cert->cert_data, der);
+
+      g_free (contents);
+      g_free (filename);
+
+      path = va_arg (va, gchar*);
+  }
+  va_end (va);
+
+  tp_dbus_daemon_register_object (dbus, MOCK_TLS_CERTIFICATE_PATH, cert);
+  return cert;
+}
+
+/* ----------------------------------------------------------------------------
+ * TESTS
+ */
+
+typedef struct {
+  GMainLoop *loop;
+  TpDBusDaemon *dbus;
+  const gchar *dbus_name;
+  MockTLSCertificate *mock;
+  EmpathyTLSCertificate *cert;
+  GAsyncResult *result;
+} Test;
+
+static void
+setup (Test *test, gconstpointer data)
+{
+  GError *error = NULL;
+  test->loop = g_main_loop_new (NULL, FALSE);
+
+  test->dbus = tp_dbus_daemon_dup (&error);
+  g_assert_no_error (error);
+
+  test->dbus_name = tp_dbus_daemon_get_unique_name (test->dbus);
+
+  test->result = NULL;
+  test->cert = NULL;
+
+  /* No PKCS#11 modules by default, tests add them */
+  gcr_pkcs11_set_modules (NULL);
+}
+
+static void
+teardown (Test *test, gconstpointer data)
+{
+  test->dbus_name = NULL;
+
+  if (test->mock)
+    {
+      tp_dbus_daemon_unregister_object (test->dbus, test->mock);
+      g_object_unref (test->mock);
+      test->mock = NULL;
+    }
+
+  if (test->result)
+    g_object_unref (test->result);
+  test->result = NULL;
+
+  if (test->cert)
+    g_object_unref (test->cert);
+  test->cert = NULL;
+
+  g_main_loop_unref (test->loop);
+  test->loop = NULL;
+
+  g_object_unref (test->dbus);
+  test->dbus = NULL;
+}
+
+static void
+add_pkcs11_module_for_testing (Test *test,
+        const gchar *filename,
+        const gchar *subdir)
+{
+  GError *error = NULL;
+  gchar *args, *path, *directory;
+  gchar *standalone, *error_output;
+  gint exit_status;
+
+  directory = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
+          "tests", "certificates", subdir, NULL);
+
+  /*
+   * Lookup the directory for standalone pkcs11 modules installed by
+   * gnome-keyring. We use these for testing our implementation.
+   */
+  g_spawn_command_line_sync ("pkg-config --variable=pkcs11standalonedir gcr-3",
+          &standalone, &error_output, &exit_status, &error);
+  g_assert_no_error (error);
+  if (exit_status != 0)
+    {
+      g_warning ("couldn't determine standalone pkcs11 module directory: %d: %s",
+              exit_status, error_output);
+      g_assert_not_reached ();
+    }
+
+  g_strstrip (standalone);
+  args = g_strdup_printf ("directory=\"%s\"", directory);
+  path = g_build_filename (standalone, filename, NULL);
+  gcr_pkcs11_add_module_from_file (path, args, &error);
+  g_assert_no_error (error);
+
+  g_free (directory);
+  g_free (standalone);
+  g_free (error_output);
+  g_free (args);
+  g_free (path);
+}
+
+static void
+fetch_callback_result (GObject *object,
+        GAsyncResult *res,
+        gpointer user_data)
+{
+  Test *test = user_data;
+  g_assert (!test->result);
+  test->result = g_object_ref (res);
+  g_main_loop_quit (test->loop);
+}
+
+static void
+ensure_certificate_proxy (Test *test)
+{
+  GError *error = NULL;
+
+  if (test->cert)
+    return;
+
+  /* Create and prepare a certificate */
+  test->cert = empathy_tls_certificate_new (test->dbus, test->dbus_name,
+          MOCK_TLS_CERTIFICATE_PATH, &error);
+  g_assert_no_error (error);
+  empathy_tls_certificate_prepare_async (test->cert, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+  empathy_tls_certificate_prepare_finish (test->cert, test->result, &error);
+  g_assert_no_error (error);
+
+  /* Clear for any future async stuff */
+  g_object_unref (test->result);
+  test->result = NULL;
+}
+
+/* A simple test to make sure the test infrastructure is working */
+static void
+test_certificate_mock_basics (Test *test,
+        gconstpointer data G_GNUC_UNUSED)
+{
+  GError *error = NULL;
+
+  test->mock = mock_tls_certificate_new_and_register (test->dbus,
+          "dhansak-collabora.cer", NULL);
+
+  ensure_certificate_proxy (test);
+
+  empathy_tls_certificate_accept_async (test->cert, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+  empathy_tls_certificate_accept_finish (test->cert, test->result, &error);
+  g_assert_no_error (error);
+
+  g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
+}
+
+static void
+test_certificate_verify_success_with_pkcs11_lookup (Test *test,
+        gconstpointer data G_GNUC_UNUSED)
+{
+  EmpTLSCertificateRejectReason reason = 0;
+  GError *error = NULL;
+  EmpathyTLSVerifier *verifier;
+
+  /*
+   * In this test the mock TLS connection only has one certificate
+   * not a full certificat echain. The root anchor certificate is
+   * retrieved from PKCS#11 storage.
+   */
+
+  test->mock = mock_tls_certificate_new_and_register (test->dbus,
+          "dhansak-collabora.cer", NULL);
+
+  /* We add teh collabora directory with the collabora root */
+  add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
+          "collabora-ca");
+
+  ensure_certificate_proxy (test);
+
+  verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk");
+  empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+  if (!empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+          NULL, &error))
+    g_assert_not_reached ();
+
+  /* Yay the verification was a success! */
+
+  g_clear_error (&error);
+  g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_success_with_full_chain (Test *test,
+        gconstpointer data G_GNUC_UNUSED)
+{
+  EmpTLSCertificateRejectReason reason = 0;
+  GError *error = NULL;
+  EmpathyTLSVerifier *verifier;
+
+  /*
+   * In this test the mock TLS connection has a full certificate
+   * chain. We look for an anchor certificate in the chain.
+   */
+
+  test->mock = mock_tls_certificate_new_and_register (test->dbus,
+          "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
+
+  /* We add teh collabora directory with the collabora root */
+  add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
+          "collabora-ca");
+
+  ensure_certificate_proxy (test);
+
+  verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk");
+  empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+  if (!empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+          NULL, &error))
+    g_assert_not_reached ();
+
+  /* Yay the verification was a success! */
+
+  g_clear_error (&error);
+  g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_root_not_found (Test *test,
+        gconstpointer data G_GNUC_UNUSED)
+{
+  EmpTLSCertificateRejectReason reason = 0;
+  GError *error = NULL;
+  EmpathyTLSVerifier *verifier;
+
+  test->mock = mock_tls_certificate_new_and_register (test->dbus,
+          "dhansak-collabora.cer", NULL);
+
+  /* Note that we're not adding any place to find root certs */
+
+  ensure_certificate_proxy (test);
+
+  verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk");
+  empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+
+  if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+          NULL, &error))
+    g_assert_not_reached ();
+
+  /* And it should say we're self-signed (oddly enough) */
+  g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+
+  g_clear_error (&error);
+  g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_root_not_anchored (Test *test,
+        gconstpointer data G_GNUC_UNUSED)
+{
+  EmpTLSCertificateRejectReason reason = 0;
+  GError *error = NULL;
+  EmpathyTLSVerifier *verifier;
+
+  test->mock = mock_tls_certificate_new_and_register (test->dbus,
+          "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
+
+  /* Note that we're not adding any place to find root certs */
+
+  ensure_certificate_proxy (test);
+
+  verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk");
+  empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+
+  if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+          NULL, &error))
+    g_assert_not_reached ();
+
+  /* And it should say we're self-signed (oddly enough) */
+  g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+
+  g_clear_error (&error);
+  g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_hostname_invalid (Test *test,
+        gconstpointer data G_GNUC_UNUSED)
+{
+  EmpTLSCertificateRejectReason reason = 0;
+  GError *error = NULL;
+  EmpathyTLSVerifier *verifier;
+
+  test->mock = mock_tls_certificate_new_and_register (test->dbus,
+          "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
+
+  /* We add teh collabora directory with the collabora root */
+  add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
+          "collabora-ca");
+
+  ensure_certificate_proxy (test);
+
+  verifier = empathy_tls_verifier_new (test->cert, "invalid.host.name");
+  empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+  g_main_loop_run (test->loop);
+
+  if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+          NULL, &error))
+    g_assert_not_reached ();
+
+  /* And it should say we're self-signed (oddly enough) */
+  g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
+
+  g_clear_error (&error);
+  g_object_unref (verifier);
+}
+
+int
+main (int argc,
+    char **argv)
+{
+  int result;
+
+  test_init (argc, argv);
+  gnutls_global_init ();
+
+  g_test_add ("/tls/certificate_basics", Test, NULL,
+          setup, test_certificate_mock_basics, teardown);
+  g_test_add ("/tls/certificate_verify_success_with_pkcs11_lookup", Test, NULL,
+          setup, test_certificate_verify_success_with_pkcs11_lookup, teardown);
+  g_test_add ("/tls/certificate_verify_success_with_full_chain", Test, NULL,
+          setup, test_certificate_verify_success_with_full_chain, teardown);
+  g_test_add ("/tls/certificate_verify_root_not_found", Test, NULL,
+          setup, test_certificate_verify_root_not_found, teardown);
+  g_test_add ("/tls/certificate_verify_root_not_anchored", Test, NULL,
+          setup, test_certificate_verify_root_not_anchored, teardown);
+  g_test_add ("/tls/certificate_verify_hostname_invalid", Test, NULL,
+          setup, test_certificate_verify_hostname_invalid, teardown);
+
+  result = g_test_run ();
+  test_deinit ();
+  return result;
+}
