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 <libsoup/soup-types.h>
 
 /**
  * SoupSSLType:
@@ -20,8 +21,31 @@
 	SOUP_SSL_TYPE_SERVER
 } SoupSSLType;
 
+typedef int SoupSSLAuthenticateCallback (GByteArray *req_ca_rdn_der,
+					 int nreqs,
+					 gpointer sign_algos,
+					 int sign_algos_length,
+					 SoupSSLCertificateType cert_type,
+					 GByteArray **cert_ders_ret,
+					 int *cert_ders_ret_length,
+					 gpointer user_data);
+
+typedef int SoupSSLSignCallback (GByteArray *cert_der,
+				 SoupSSLCertificateType cert_type,
+				 const GByteArray *hash_data,
+				 GByteArray *sign_data,
+				 gpointer user_data);
+
 gpointer    soup_ssl_get_client_credentials  (const char  *ca_file);
 void        soup_ssl_free_client_credentials (gpointer     creds);
+void        soup_ssl_set_authenticate_callback (gpointer creds,
+						SoupSSLAuthenticateCallback auth_func,
+						gpointer user_data,
+						GDestroyNotify destroy_func);
+void        soup_ssl_set_sign_callback (gpointer creds,
+					SoupSSLSignCallback sign_func,
+					gpointer user_data,
+					GDestroyNotify destroy_func);
 gpointer    soup_ssl_get_server_credentials  (const char  *cert_file,
 					      const char  *key_file);
 void        soup_ssl_free_server_credentials (gpointer     creds);
Index: libsoup/soup-types.h
===================================================================
--- libsoup/soup-types.h	(revision 923)
+++ libsoup/soup-types.h	(working copy)
@@ -25,6 +25,10 @@
 typedef struct SoupSocket            SoupSocket;
 typedef struct SoupUri               SoupUri;
 
+typedef enum {
+	SOUP_SSL_CERT_X509 = 0,
+} SoupSSLCertificateType;
+
 #define SOUP_MAKE_INTERFACE(type_name,TypeName,base_init) \
 GType type_name##_get_type(void)\
 {\
Index: libsoup/soup-gnutls.c
===================================================================
--- libsoup/soup-gnutls.c	(revision 923)
+++ libsoup/soup-gnutls.c	(working copy)
@@ -33,6 +33,14 @@
 typedef struct {
 	gnutls_certificate_credentials cred;
 	gboolean have_ca_file;
+
+	SoupSSLAuthenticateCallback *auth_func;
+	gpointer auth_data;
+	GDestroyNotify auth_destroy;
+
+	SoupSSLSignCallback *sign_func;
+	gpointer sign_data;
+	GDestroyNotify sign_destroy;
 } SoupGNUTLSCred;
 
 typedef struct {
@@ -46,6 +54,8 @@
 	SoupSSLType type;
 } SoupGNUTLSChannel;
 
+static GHashTable *session_to_cred = NULL;
+
 static gboolean
 verify_certificate (gnutls_session session, const char *hostname, GError **err)
 {
@@ -288,7 +298,6 @@
 			ret = gnutls_bye (chan->session, GNUTLS_SHUT_WR);
 		} while (ret == GNUTLS_E_INTERRUPTED);
 	}
-
 	return chan->real_sock->funcs->io_close (channel, err);
 }
 
@@ -309,6 +318,7 @@
 	g_io_channel_unref (chan->real_sock);
 	gnutls_deinit (chan->session);
 	g_free (chan->hostname);
+	g_hash_table_remove (session_to_cred, chan->session);
 	g_free (chan);
 }
 
@@ -382,7 +392,6 @@
 {
 	SoupGNUTLSChannel *chan = NULL;
 	GIOChannel *gchan = NULL;
-	gnutls_session session = NULL;
 	SoupGNUTLSCred *cred = credentials;
 	int sockfd;
 	int ret;
@@ -408,6 +417,11 @@
 				    cred->cred) != 0)
 		goto THROW_CREATE_ERROR;
 
+	if (!session_to_cred) {
+		session_to_cred = g_hash_table_new (g_direct_hash, g_direct_equal);
+	}
+	g_hash_table_insert (session_to_cred, chan->session, cred);
+
 	if (type == SOUP_SSL_TYPE_SERVER)
 		gnutls_dh_set_prime_bits (session, DH_BITS);
 
@@ -431,8 +445,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;
 }
 
@@ -508,11 +526,195 @@
 soup_ssl_free_client_credentials (gpointer creds)
 {
 	SoupGNUTLSCred *cred = creds;
+	if (cred == NULL) {
+		return;
+	}
 
+	if (cred->auth_destroy) {
+		cred->auth_destroy (cred->auth_data);
+	}
+
+	if (cred->sign_destroy) {
+		cred->sign_destroy (cred->sign_data);
+	}
+
 	gnutls_certificate_free_credentials (cred->cred);
 	g_free (cred);
 }
 
+static GByteArray *
+datum_to_data (GByteArray *data, const gnutls_datum_t *datum)
+{
+	data->data = datum->data;
+	data->len = datum->size;
+	return data;
+}
+
+static gnutls_datum_t *
+data_to_datum (gnutls_datum_t *datum, const GByteArray *data)
+{
+	datum->data = data->data;
+	datum->size = data->len;
+	return datum;
+}
+
+static GByteArray *
+datum_to_data_array (const gnutls_datum_t *datum, int ndata)
+{
+	int i;
+	GByteArray *data;
+
+	data = g_new0 (GByteArray, ndata);
+	for (i = 0; i < ndata; i++) {
+		datum_to_data (&data[i], &datum[i]);
+	}
+	return data;
+}
+
+static int
+soup_ssl_authenticate_cb (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)
+{
+	SoupGNUTLSCred *cred;
+	GByteArray *req_cas;
+	GByteArray *cert_data = NULL;
+	int cert_data_len = 0;
+	gnutls_datum_t cert_datum;
+	gnutls_x509_crt_t *gnutls_cert;
+	int i;
+
+	int ret;
+
+	g_return_val_if_fail (session_to_cred != NULL, -1);
+	cred = g_hash_table_lookup (session_to_cred, session);
+	if (!cred) {
+		return -1;
+	}
+
+	g_return_val_if_fail (cred->auth_func != NULL, -1);
+
+	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
+		return -1;
+	}
+
+	req_cas = datum_to_data_array (req_ca_rdn, nreqs);
+
+	ret = cred->auth_func (req_cas, nreqs, NULL, 0, 
+			       SOUP_SSL_CERT_X509, 
+			       &cert_data, &cert_data_len,
+			       cred->auth_data);
+
+	g_free (req_cas);
+
+	if (ret != 0 || cert_data_len == 0) {
+		return -1;
+	}
+
+	gnutls_cert = gnutls_malloc (sizeof (gnutls_x509_crt_t) * cert_data_len);
+	for (i = 0; i < cert_data_len; i++) {
+		if (ret == 0) {
+			gnutls_x509_crt_init (&gnutls_cert[i]);
+			ret = gnutls_x509_crt_import (gnutls_cert[i],
+						      data_to_datum (&cert_datum, &cert_data[i]),
+						      GNUTLS_X509_FMT_DER);
+			if (ret != 0) {
+				int j;
+				for (j = 0; i < i; j++) {
+					gnutls_x509_crt_deinit (gnutls_cert[j]);
+				}
+			}
+		}
+		g_free (cert_data[i].data);
+	}
+	g_free (cert_data);
+
+	if (ret != 0) {
+		gnutls_free (gnutls_cert);
+		return -1;
+	}
+
+	st->type = GNUTLS_CRT_X509;
+	st->cert.x509 = gnutls_cert;
+	st->ncerts = cert_data_len;
+	st->key.x509 = NULL;
+	st->deinit_all = 1;
+	ret = 0;
+
+	return ret;
+}
+
+void
+soup_ssl_set_authenticate_callback (gpointer creds,
+				    SoupSSLAuthenticateCallback auth_func,
+				    gpointer user_data,
+				    GDestroyNotify destroy_func)
+{
+	SoupGNUTLSCred *cred = creds;
+
+	g_return_if_fail (cred != NULL);
+
+	cred->auth_func = auth_func;
+	cred->auth_data = user_data;
+	cred->auth_destroy = destroy_func;
+
+	gnutls_certificate_client_set_retrieve_function (cred->cred,
+							 auth_func ? soup_ssl_authenticate_cb : NULL);
+							 
+}
+
+static int
+soup_ssl_sign_cb (gnutls_session_t session,
+		  gnutls_datum_t * cert,
+		  gnutls_certificate_type_t cert_type,
+		  const gnutls_datum_t *hash_concat,
+		  gnutls_datum_t * signature)
+{
+	SoupGNUTLSCred *cred;
+	int ret;
+	GByteArray cert_data, hash_data, sign_data = { NULL, 0 };
+
+	g_return_val_if_fail (session_to_cred != NULL, -1);
+	cred = g_hash_table_lookup (session_to_cred, session);
+
+	g_return_val_if_fail (cred->auth_func != NULL, -1);
+	g_return_val_if_fail (cert_type == GNUTLS_CRT_X509, -1);
+
+	ret = cred->sign_func (datum_to_data (&cert_data, cert),
+			       SOUP_SSL_CERT_X509,
+			       datum_to_data (&hash_data, hash_concat),
+			       &sign_data,
+			       cred->auth_data);
+
+	if (ret == 0) {
+		signature->size = sign_data.len;
+		signature->data = gnutls_malloc (sign_data.len);
+		memcpy (signature->data, sign_data.data, sign_data.len);
+		g_free (sign_data.data);
+	}
+
+	return ret;
+}
+
+void
+soup_ssl_set_sign_callback (gpointer creds,
+			    SoupSSLSignCallback sign_func,
+			    gpointer user_data,
+			    GDestroyNotify destroy_func)
+{
+	SoupGNUTLSCred *cred = creds;
+
+	g_return_if_fail (cred != NULL);
+
+	cred->sign_func = sign_func;
+	cred->sign_data = user_data;
+	cred->sign_destroy = destroy_func;
+
+	gnutls_certificate_client_set_sign_function (cred->cred,
+						     sign_func ? soup_ssl_sign_cb : NULL);
+}
+
 /**
  * soup_ssl_get_server_credentials:
  * @cert_file: path to a file containing an X509-encoded server
@@ -566,6 +768,10 @@
 {
 	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,
+	CERT_AUTHENTICATE,
+	CERT_SIGN,
 	LAST_SIGNAL
 };
 
@@ -292,6 +294,33 @@
 			      G_TYPE_POINTER,
 			      G_TYPE_POINTER);
 
+	signals[CERT_AUTHENTICATE] =
+		g_signal_new ("cert-authenticate",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      soup_marshal_INT__POINTER_INT_POINTER_INT_INT_POINTER_POINTER,
+			      G_TYPE_INT, 7,
+			      G_TYPE_POINTER,
+			      G_TYPE_INT,
+			      G_TYPE_POINTER,
+			      G_TYPE_INT,
+			      G_TYPE_INT,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER);
+
+	signals[CERT_SIGN] =
+		g_signal_new ("cert-sign",
+			      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);
+
 	/* properties */
 	g_object_class_install_property (
 		object_class, PROP_PROXY_URI,
@@ -550,6 +579,50 @@
 	return strcmp (one->host, two->host) == 0;
 }
 
+static int
+soup_session_ssl_authenticate (GByteArray *req_ca_rdn_der,
+			       int nreqs,
+			       gpointer sign_algos,
+			       int sign_algos_length,
+			       SoupSSLCertificateType cert_type,
+			       GByteArray **cert_der_ret,
+			       int *cert_der_ret_length,
+			       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[CERT_AUTHENTICATE], 0,
+		       req_ca_rdn_der, nreqs,
+		       sign_algos, sign_algos_length,
+		       cert_type, cert_der_ret, cert_der_ret_length,
+		       &ret);
+
+	return ret;
+}
+
+static int
+soup_session_ssl_sign (GByteArray *cert_der,
+		       SoupSSLCertificateType cert_type,
+		       const GByteArray *hash_data,
+		       GByteArray *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[CERT_SIGN], 0,
+		       cert_der, cert_type,
+		       hash_data, sign_data,
+		       &ret);
+
+	return ret;
+}
+
 static SoupSessionHost *
 soup_session_host_new (SoupSession *session, const SoupUri *source_uri)
 {
@@ -563,6 +636,12 @@
 	    !priv->ssl_creds) {
 		priv->ssl_creds =
 			soup_ssl_get_client_credentials (priv->ssl_ca_file);
+		soup_ssl_set_authenticate_callback (priv->ssl_creds,
+						    soup_session_ssl_authenticate,
+						    session, NULL);
+		soup_ssl_set_sign_callback (priv->ssl_creds,
+					    soup_session_ssl_sign,
+					    session, NULL);
 	}
 
 	return host;
Index: libsoup/soup-nossl.c
===================================================================
--- libsoup/soup-nossl.c	(revision 923)
+++ libsoup/soup-nossl.c	(working copy)
@@ -16,6 +16,13 @@
 
 gboolean soup_ssl_supported = FALSE;
 
+typedef struct {
+	gpointer auth_data;
+	GDestroyNotify auth_destroy;
+	gpointer sign_data;
+	GDestroyNotify sign_destroy;
+} NoSSLData;
+
 GIOChannel *
 soup_ssl_wrap_iochannel (GIOChannel *sock, SoupSSLType type,
 			 const char *hostname, gpointer cred_pointer)
@@ -26,15 +33,49 @@
 gpointer
 soup_ssl_get_client_credentials (const char *ca_file)
 {
-	return NULL;
+	return g_new0 (NoSSLData, 1);
 }
 
 void
 soup_ssl_free_client_credentials (gpointer client_creds)
 {
-	;
+	NoSSLData *creds = (NoSSLData *)client_creds;
+	if (!creds) {
+		return;
+	}
+	if (creds->auth_destroy) {
+		creds->auth_destroy (creds->auth_data);
+	}
+	if (creds->sign_destroy) {
+		creds->sign_destroy (creds->sign_data);
+	}
+	g_free (creds)
 }
 
+void
+soup_ssl_set_authenticate_callback (gpointer client_creds,
+				    SoupSSLAuthenticateCallback auth_func,
+				    gpointer user_data,
+				    GDestroyNotify destroy_func)
+{
+	NoSSLData *creds = (NoSSLData *)client_creds;
+	g_return_if_fail (creds != NULL);
+	creds->auth_data = user_data;
+	creds->auth_destroy = destroy_func;
+}
+
+void
+soup_ssl_set_sign_callback (gpointer client_creds,
+			    SoupSSLSignCallback sign_func,
+			    gpointer user_data,
+			    GDestroyNotify destroy_func)
+{
+	NoSSLData *creds = (NoSSLData *)client_creds;
+	g_return_if_fail (creds != NULL);
+	creds->sign_data = user_data;
+	creds->sign_destroy = destroy_func;
+}
+
 gpointer
 soup_ssl_get_server_credentials (const char *cert_file, const char *key_file)
 {
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,INT,INT,POINTER,POINTER
+INT:POINTER,INT,POINTER,POINTER
