Index: tests/ssl-test.c
===================================================================
--- tests/ssl-test.c	(revision 923)
+++ tests/ssl-test.c	(working copy)
@@ -270,7 +270,7 @@
 
 	/* And create the client */
 	sock = soup_socket_client_new_sync ("127.0.0.1", port,
-					    soup_ssl_get_client_credentials (NULL),
+					    soup_ssl_get_client_credentials (NULL, NULL, NULL, NULL, NULL),
 					    &status);
 	if (status != SOUP_STATUS_OK) {
 		g_error ("Could not create client socket: %s",
Index: libsoup/soup-ssl.h
===================================================================
--- libsoup/soup-ssl.h	(revision 923)
+++ libsoup/soup-ssl.h	(working copy)
@@ -7,6 +7,7 @@
 #define SOUP_SSL_H 1
 
 #include <glib.h>
+#include <gnutls/gnutls.h>
 
 /**
  * SoupSSLType:
@@ -20,7 +21,22 @@
 	SOUP_SSL_TYPE_SERVER
 } SoupSSLType;
 
-gpointer    soup_ssl_get_client_credentials  (const char  *ca_file);
+typedef int SoupSSLCertificateRequestFunc (const gnutls_datum_t *req_ca_rdn,
+					   int nreqs,
+					   gnutls_datum_t **cert_ret,
+					   int *cert_ret_len,
+					   gpointer user_data);
+
+typedef int SoupSSLSignDataFunc (gnutls_datum_t *cert_der,
+				 const gnutls_datum_t *hash_data,
+				 gnutls_datum_t *sign_data,
+				 gpointer user_data);
+
+gpointer    soup_ssl_get_client_credentials  (const char  *ca_file,
+					      SoupSSLCertificateRequestFunc cert_func,
+					      SoupSSLSignDataFunc sign_func,
+					      gpointer user_data,
+					      GDestroyNotify destroy_func);
 void        soup_ssl_free_client_credentials (gpointer     creds);
 gpointer    soup_ssl_get_server_credentials  (const char  *cert_file,
 					      const char  *key_file);
Index: libsoup/soup-gnutls.c
===================================================================
--- libsoup/soup-gnutls.c	(revision 923)
+++ libsoup/soup-gnutls.c	(working copy)
@@ -33,6 +33,12 @@
 typedef struct {
 	gnutls_certificate_credentials cred;
 	gboolean have_ca_file;
+
+	SoupSSLCertificateRequestFunc *cert_func;
+	SoupSSLSignDataFunc *sign_func;
+
+	gpointer auth_data;
+	GDestroyNotify auth_destroy;
 } SoupGNUTLSCred;
 
 typedef struct {
@@ -408,6 +414,8 @@
 				    cred->cred) != 0)
 		goto THROW_CREATE_ERROR;
 
+	gnutls_session_set_ptr (chan->session, chan);
+
 	if (type == SOUP_SSL_TYPE_SERVER)
 		gnutls_dh_set_prime_bits (session, DH_BITS);
 
@@ -431,8 +439,12 @@
 	return gchan;
 
  THROW_CREATE_ERROR:
-	if (session)
-		gnutls_deinit (session);
+	if (chan->session) {
+		gnutls_deinit (chan->session);
+	}
+	if (chan) {
+		g_free (chan);
+	}
 	return NULL;
 }
 
@@ -452,6 +464,94 @@
 	soup_gnutls_inited = TRUE;
 }
 
+static int
+soup_ssl_retrieve_cert (gnutls_session_t session,
+			const gnutls_datum_t * req_ca_rdn, int nreqs,
+			const gnutls_pk_algorithm_t * sign_algos,
+			int sign_algos_length, gnutls_retr_st * st)
+{
+	SoupGNUTLSChannel *chan;
+	int cert_ret_len = 0;
+	gnutls_datum_t *cert_ret;
+	gnutls_x509_crt_t *gnutls_cert;
+	int i;
+
+	int ret;
+
+	chan = gnutls_session_get_ptr (session);
+	if (!chan || !chan->cred)
+		return -1;
+
+	g_return_val_if_fail (chan->cred->cert_func != NULL, -1);
+
+	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+		return -1;
+
+	ret = chan->cred->cert_func (req_ca_rdn, nreqs,
+				     &cert_ret, &cert_ret_len,
+				     chan->cred->auth_data);
+
+	if (ret != 0)
+		return -1;
+
+	if (cert_ret_len == 0)
+		return 0;
+
+	gnutls_cert = gnutls_malloc (sizeof (gnutls_x509_crt_t) * cert_ret_len);
+	for (i = 0; i < cert_ret_len; i++) {
+		if (ret == 0) {
+			gnutls_x509_crt_init (&gnutls_cert[i]);
+			ret = gnutls_x509_crt_import (gnutls_cert[i],
+						      &cert_ret[i],
+						      GNUTLS_X509_FMT_DER);
+			if (ret != 0) {
+				int j;
+				for (j = 0; i < i; j++) {
+					gnutls_x509_crt_deinit (gnutls_cert[j]);
+				}
+			}
+		}
+		gnutls_free (cert_ret[i].data);
+	}
+	gnutls_free (cert_ret);
+
+	if (ret != 0) {
+		gnutls_free (gnutls_cert);
+		return -1;
+	}
+
+	st->type = GNUTLS_CRT_X509;
+	st->cert.x509 = gnutls_cert;
+	st->ncerts = cert_ret_len;
+	st->key.x509 = NULL;
+	st->deinit_all = 1;
+
+	return 0;
+}
+
+static int
+soup_ssl_sign_data (gnutls_session_t session,
+		    gnutls_datum_t * cert,
+		    gnutls_certificate_type_t cert_type,
+		    const gnutls_datum_t *hash_concat,
+		    gnutls_datum_t * signature)
+{
+	SoupGNUTLSChannel *chan;
+	int ret;
+
+	chan = gnutls_session_get_ptr (session);
+	if (!chan || !chan->cred)
+		return -1;
+
+	g_return_val_if_fail (chan->cred->sign_func != NULL, -1);
+	g_return_val_if_fail (cert_type == GNUTLS_CRT_X509, -1);
+
+	ret = chan->cred->sign_func (cert, hash_concat, signature,
+				     chan->cred->auth_data);
+
+	return ret;
+}
+
 /**
  * soup_ssl_get_client_credentials:
  * @ca_file: path to a file containing X509-encoded Certificate
@@ -468,7 +568,11 @@
  * soup_ssl_free_client_credentials().
  **/
 gpointer
-soup_ssl_get_client_credentials (const char *ca_file)
+soup_ssl_get_client_credentials (const char *ca_file,
+				 SoupSSLCertificateRequestFunc cert_func,
+				 SoupSSLSignDataFunc sign_func,
+				 gpointer user_data,
+				 GDestroyNotify destroy_func)
 {
 	SoupGNUTLSCred *cred;
 	int status;
@@ -493,7 +597,18 @@
 			 */
 		}
 	}
+	
+	cred->cert_func = cert_func;
+	cred->sign_func = sign_func;
+	cred->auth_data = user_data;
+	cred->auth_destroy = destroy_func;
 
+	if (cert_func)
+		gnutls_certificate_client_set_retrieve_function (cred->cred, soup_ssl_retrieve_cert);
+
+	if (sign_func)
+		gnutls_certificate_client_set_sign_function (cred->cred, soup_ssl_sign_data);
+
 	return cred;
 }
 
@@ -508,7 +623,12 @@
 soup_ssl_free_client_credentials (gpointer creds)
 {
 	SoupGNUTLSCred *cred = creds;
+	if (cred == NULL)
+		return;
 
+	if (cred->auth_destroy)
+		cred->auth_destroy (cred->auth_data);
+
 	gnutls_certificate_free_credentials (cred->cred);
 	g_free (cred);
 }
@@ -566,6 +686,9 @@
 {
 	SoupGNUTLSCred *cred = creds;
 
+	if (cred == NULL)
+		return;
+
 	gnutls_certificate_free_credentials (cred->cred);
 	g_free (cred);
 }
Index: libsoup/soup-session.c
===================================================================
--- libsoup/soup-session.c	(revision 923)
+++ libsoup/soup-session.c	(working copy)
@@ -89,6 +89,8 @@
 enum {
 	AUTHENTICATE,
 	REAUTHENTICATE,
+	CERTIFICATE_REQUESTED,
+	SIGN_DATA,
 	LAST_SIGNAL
 };
 
@@ -292,6 +294,29 @@
 			      G_TYPE_POINTER,
 			      G_TYPE_POINTER);
 
+	signals[CERTIFICATE_REQUESTED] =
+		g_signal_new ("certificate-requested",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      soup_marshal_INT__POINTER_INT_POINTER_POINTER,
+			      G_TYPE_INT, 4,
+			      G_TYPE_POINTER,
+			      G_TYPE_INT,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER);
+
+	signals[SIGN_DATA] =
+		g_signal_new ("sign-data",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      soup_marshal_INT__POINTER_POINTER_POINTER,
+			      G_TYPE_INT, 3,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER);
+
 	/* properties */
 	g_object_class_install_property (
 		object_class, PROP_PROXY_URI,
@@ -550,6 +575,44 @@
 	return strcmp (one->host, two->host) == 0;
 }
 
+static int
+soup_session_ssl_certificate_requested (const gnutls_datum_t *req_ca_rdn,
+					int nreqs,
+					gnutls_datum_t **cert_ret,
+					int *cert_ret_len,
+					gpointer user_data)
+{
+	SoupSession *session = user_data;
+	int ret = 0;
+
+	g_return_val_if_fail (SOUP_IS_SESSION (user_data), -1);
+
+	g_signal_emit (session, signals[CERTIFICATE_REQUESTED], 0,
+		       req_ca_rdn, nreqs,
+		       cert_ret, cert_ret_len,
+		       &ret);
+
+	return ret;
+}
+
+static int
+soup_session_ssl_sign_data (gnutls_datum_t *cert_der,
+			    const gnutls_datum_t *hash_data,
+			    gnutls_datum_t *sign_data,
+			    gpointer user_data)
+{
+	SoupSession *session = user_data;
+	int ret = 0;
+
+	g_return_val_if_fail (SOUP_IS_SESSION (user_data), -1);
+
+	g_signal_emit (session, signals[SIGN_DATA], 0,
+		       cert_der, hash_data, sign_data,
+		       &ret);
+
+	return ret;
+}
+
 static SoupSessionHost *
 soup_session_host_new (SoupSession *session, const SoupUri *source_uri)
 {
@@ -562,7 +625,10 @@
 	if (host->root_uri->protocol == SOUP_PROTOCOL_HTTPS &&
 	    !priv->ssl_creds) {
 		priv->ssl_creds =
-			soup_ssl_get_client_credentials (priv->ssl_ca_file);
+			soup_ssl_get_client_credentials (priv->ssl_ca_file,
+							 soup_session_ssl_certificate_requested,
+							 soup_session_ssl_sign_data,
+							 session, NULL);
 	}
 
 	return host;
Index: libsoup/soup-nossl.c
===================================================================
--- libsoup/soup-nossl.c	(revision 923)
+++ libsoup/soup-nossl.c	(working copy)
@@ -24,8 +24,15 @@
 }
 
 gpointer
-soup_ssl_get_client_credentials (const char *ca_file)
+soup_ssl_get_client_credentials (const char *ca_file,
+				 SoupSSLCertificateRequestedCallback cert_func,
+				 SoupSSLSignDataCallback sign_func,
+				 gpointer user_data,
+				 GDestroyNotify destroy_func);
 {
+	if (destroy_func) {
+		destroy_func (user_data);
+	}
 	return NULL;
 }
 
Index: libsoup/soup-marshal.list
===================================================================
--- libsoup/soup-marshal.list	(revision 923)
+++ libsoup/soup-marshal.list	(working copy)
@@ -2,3 +2,5 @@
 NONE:INT
 NONE:OBJECT
 NONE:OBJECT,STRING,STRING,POINTER,POINTER
+INT:POINTER,INT,POINTER,POINTER
+INT:POINTER,POINTER,POINTER
