On the multiple_ssl_impls branch: Merge trunk up to r2024.
Revs 2022 and 2023 were already merged manually, so this revision only records
the merged revisions.


git-svn-id: https://svn.apache.org/repos/asf/serf/branches/multiple_ssl_impls@1699562 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/SConstruct b/SConstruct
index 1e6dab4..111a666 100644
--- a/SConstruct
+++ b/SConstruct
@@ -18,6 +18,7 @@
 import sys
 import os
 import re
+import platform
 
 HEADER_FILES = ['serf.h',
                 'serf_bucket_types.h',
@@ -41,10 +42,6 @@
                "Path to apu-1-config, or to APR's install area",
                '/usr',
                PathVariable.PathAccept),
-  PathVariable('OPENSSL',
-               "Path to OpenSSL's install area",
-               '/usr',
-               PathVariable.PathIsDir),
   PathVariable('ZLIB',
                "Path to zlib's install area",
                '/usr',
@@ -58,6 +55,19 @@
                False),
   )
 
+if sys.platform == 'darwin':
+  opts.AddVariables(
+    PathVariable('OPENSSL',
+                 "Path to OpenSSL's install area",
+                 None,
+                 None))
+else:
+  opts.AddVariables(
+    PathVariable('OPENSSL',
+                 "Path to OpenSSL's install area",
+                 '/usr',
+                 PathVariable.PathIsDir))
+
 env = Environment(variables=opts,
                   tools=('default', 'textfile',),
                   CPPPATH=['.', ],
@@ -95,6 +105,11 @@
     gssapi = krb5_config
     env['GSSAPI'] = krb5_config
 
+openssl = env.get('OPENSSL', None)
+if openssl and os.path.isdir(str(openssl)):
+  env.Append(CPPPATH='$OPENSSL/include')
+  env.Append(LIBPATH='$OPENSSL/lib')
+  env.Append(CFLAGS='-DSERF_HAVE_OPENSSL')
 debug = env.get('DEBUG', None)
 
 Help(opts.GenerateHelpText(env))
@@ -134,6 +149,14 @@
   linkflags.append('-Wl,-compatibility_version,%d' % (MINOR+1,))
   linkflags.append('-Wl,-current_version,%d.%d' % (MINOR+1, PATCH,))
 
+  # enable macosxssl_buckets only on Mac OS X 10.7+
+  macosxssl = False
+  ver, _, _ = platform.mac_ver()
+  ver = float('.'.join(ver.split('.')[:2]))
+  if ver >= 10.7:
+    macosxssl = True
+    env.Append(CFLAGS='-DSERF_HAVE_MACOSXSSL')
+
 if sys.platform == 'win32':
   ### we should create serf.def for Windows DLLs and add it into the link
   ### step somehow.
@@ -168,6 +191,11 @@
   ### works for Mac OS. probably needs to change
   libs = ['ssl', 'crypto', 'z', ]
 
+  if macosxssl:
+    env.Append(FRAMEWORKS=['Security', 'SecurityInterface', 'CoreFoundation',
+                           'AppKit'])
+    libs.append('objc')
+
   if sys.platform == 'sunos5':
     libs.append('m')
 
@@ -180,6 +208,8 @@
 # PLAN THE BUILD
 
 SOURCES = Glob('*.c') + Glob('buckets/*.c') + Glob('auth/*.c')
+if macosxssl:
+    SOURCES += ['buckets/macosxssl_helper.m']
 
 lib_static = env.StaticLibrary(LIBNAMESTATIC, SOURCES)
 lib_shared = env.SharedLibrary(LIBNAME, SOURCES)
diff --git a/buckets/bucket_private.h b/buckets/bucket_private.h
new file mode 100644
index 0000000..b77608d
--- /dev/null
+++ b/buckets/bucket_private.h
@@ -0,0 +1,273 @@
+/* Copyright 2013 Justin Erenkrantz and Greg Stein
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BUCKET_PRIVATE_H_
+#define _BUCKET_PRIVATE_H_
+
+typedef struct serf_ssl_bucket_type_t serf_ssl_bucket_type_t;
+
+struct serf_ssl_bucket_type_t {
+    void * (*decrypt_create)(serf_bucket_t *bucket,
+                             serf_bucket_t *stream,
+                             void *impl_ctx,
+                             serf_bucket_alloc_t *allocator);
+
+    void * (*decrypt_context_get)(serf_bucket_t *bucket);
+
+    void * (*encrypt_create)(serf_bucket_t *bucket,
+                             serf_bucket_t *stream,
+                             void *impl_ctx,
+                             serf_bucket_alloc_t *allocator);
+
+    void * (*encrypt_context_get)(serf_bucket_t *bucket);
+
+    /**
+     * Allow SNI indicators to be sent to the server.
+     */
+    apr_status_t (*set_hostname)(void *impl_ctx, const char *hostname);
+
+    void (*client_cert_provider_set)(void *impl_ctx,
+                                     serf_ssl_need_client_cert_t callback,
+                                     void *data,
+                                     void *cache_pool);
+
+    void (*identity_provider_set)(void *impl_ctx,
+                                  serf_ssl_need_identity_t callback,
+                                  void *data,
+                                  void *cache_pool);
+
+    void (*client_cert_password_set)(void *impl_ctx,
+                                     serf_ssl_need_cert_password_t callback,
+                                     void *data,
+                                     void *cache_pool);
+    /**
+     * Set a callback to override the default SSL server certificate validation
+     * algorithm.
+     */
+    void (*server_cert_callback_set)(void *impl_ctx,
+                                     serf_ssl_need_server_cert_t callback,
+                                     void *data);
+    /**
+     * Set callbacks to override the default SSL server certificate validation
+     * algorithm for the current certificate or the entire certificate chain.
+     */
+    void (*server_cert_chain_callback_set)(void *impl_ctx,
+                                           serf_ssl_need_server_cert_t cert_callback,
+                                           serf_ssl_server_cert_chain_cb_t cert_chain_callback,
+                                           void *data);
+    /**
+     * Use the default root CA certificates as included with the OpenSSL library.
+     * TODO: fix comment!
+     */
+    apr_status_t (*use_default_certificates)(void *impl_ctx);
+    
+    /**
+     * Load a CA certificate file from a path @a file_path. If the file was
+     * loaded and parsed correctly, a certificate @a cert will be created and
+     * returned.
+     * This certificate object will be alloced in @a pool.
+     */
+    apr_status_t (*load_CA_cert_from_file)(serf_ssl_certificate_t **cert,
+                                           const char *file_path,
+                                           apr_pool_t *pool);
+
+    apr_status_t (*load_identity_from_file)(void *impl_ctx,
+                                            const serf_ssl_identity_t **identity,
+                                            const char *file_path,
+                                            apr_pool_t *pool);
+
+    /**
+     * Adds the certificate @a cert to the list of trusted certificates in
+     * @a ssl_ctx that will be used for verification.
+     * See also @a serf_ssl_load_cert_file.
+     */
+    apr_status_t (*trust_cert)(void *impl_ctx,
+                               serf_ssl_certificate_t *cert);
+
+    /**
+     * Extract the fields of the issuer in a table with keys (E, CN, OU, O, L,
+     * ST and C). The returned table will be allocated in @a pool.
+     */
+    apr_hash_t * (*cert_issuer)(const serf_ssl_certificate_t *cert,
+                                apr_pool_t *pool);
+
+    /**
+     * Extract the fields of the subject in a table with keys (E, CN, OU, O, L,
+     * ST and C). The returned table will be allocated in @a pool.
+     */
+    apr_hash_t * (*cert_subject)(const serf_ssl_certificate_t *cert,
+                                 apr_pool_t *pool);
+
+    /**
+     * Extract the fields of the certificate in a table with keys (sha1,
+     * notBefore, notAfter, array of subjectAltName's). The returned table will
+     * be allocated in @a pool.
+     */
+    apr_hash_t * (*cert_certificate)(const serf_ssl_certificate_t *cert,
+                                     apr_pool_t *pool);
+    
+    /**
+     * Export a certificate to base64-encoded, zero-terminated string.
+     * The returned string is allocated in @a pool. Returns NULL on failure.
+     */
+    const char * (*cert_export)(const serf_ssl_certificate_t *cert,
+                                apr_pool_t *pool);
+    /**
+     * Enable or disable SSL compression on a SSL session.
+     * @a enabled = 1 to enable compression, 0 to disable compression.
+     * Default = disabled.
+     */
+    apr_status_t (*use_compression)(void *impl_ctx,
+                                    int enabled);
+
+    /**
+     * Exports @a session to continous memory block.
+     */
+    apr_status_t (*session_export)(void **data,
+                                   apr_size_t *len,
+                                   const serf_ssl_session_t *session,
+                                   apr_pool_t *pool);
+
+    /**
+     * Restores previously saved session from continuous memory block @a data
+     * with @a len length.
+     */
+    apr_status_t (*session_import)(const serf_ssl_session_t **session,
+                                   void *data,
+                                   apr_size_t len,
+                                   apr_pool_t *pool);
+
+    /**
+     * TODO: comment
+     */
+    void (*new_session_callback_set)(void *impl_ctx,
+                                     serf_ssl_new_session_t new_session_cb,
+                                     void *baton);
+
+    /* Configure @a ssl_ctx to attempt resume exisiting @a ssl_session. */
+    apr_status_t (*resume_session)(void *impl_ctx,
+                                   const serf_ssl_session_t *ssl_session,
+                                   apr_pool_t *pool);
+};
+
+/* Implementation independent certificate object. */
+struct serf_ssl_certificate_t {
+    /** bucket implementation that can parse this certificate. */
+    const serf_ssl_bucket_type_t *type;
+
+    /** implementation specific certificate data */
+    /* Note: non-const, as required by OpenSSL. */
+    void *impl_cert;
+
+    /** Depth in the chain where an error was found. */
+    int depth_of_error;
+};
+
+/* Loads a certificate from a memory buffer.
+   Defined here so it can be used by the test suite. */
+apr_status_t
+load_CA_cert_from_buffer(serf_ssl_certificate_t **cert,
+                         const char *buf,
+                         apr_size_t len,
+                         apr_pool_t *pool);
+
+/* Creates a serf_ssl_certificate_t object, caller takes ownership. */
+serf_ssl_certificate_t *
+serf__create_certificate(serf_bucket_alloc_t *allocator,
+                         const serf_ssl_bucket_type_t *type,
+                         void *impl_cert,
+                         int depth);
+
+/* Implementation independent identity object. An identity is a combination
+   of a certificate and a private key, typically stored in a .p12 file. */
+struct serf_ssl_identity_t {
+    /** bucket implementation that can parse this identity. */
+    const serf_ssl_bucket_type_t *type;
+
+    /** implementation specific client certificate */
+    void *impl_cert;
+
+    /** implementation specific private key */
+    void *impl_pkey;
+};
+
+/* Creates a serf_ssl_identity_t object, caller takes ownership. */
+serf_ssl_identity_t *
+serf__create_identity(const serf_ssl_bucket_type_t *type,
+                      void *impl_cert,
+                      void *impl_pkey,
+                      apr_pool_t *pool);
+
+void *serf__ssl_get_impl_context(serf_ssl_context_t *ssl_ctx);
+
+/* Implementation independent serialized SSL session object */
+struct serf_ssl_session_t {
+#if 0
+    /* Not needed as long as each API takes a serf_ssl_context_t. */
+    /** bucket implementation that can parse this session object. */
+    const serf_ssl_bucket_type_t *type;
+#endif
+
+    /** implementation specific serialized SSL session object. */
+    void *impl_session_obj;
+};
+
+
+/* macosxssl_bucket internal functions */
+#ifdef SERF_HAVE_MACOSXSSL
+
+#include <Security/SecCertificate.h>
+
+/* macosxssl_bucket private certificate structure. Wrapper around the 
+   SecCertificateRef ptr, with content the cached parsed information from the
+   certificate. */
+typedef struct macosxssl_certificate_t {
+    SecCertificateRef certref;
+
+    apr_hash_t *content;
+} macosxssl_certificate_t;
+
+apr_status_t
+serf__macosxssl_read_X509_DER_DN(apr_hash_t **o, CFDataRef ptr,
+                                 apr_pool_t *pool);
+
+apr_status_t
+serf__macosxssl_read_X509_DER_certificate(apr_hash_t **o,
+                                          const macosxssl_certificate_t *cert,
+                                          apr_pool_t *pool);
+
+#endif
+
+/* ==================================================================== */
+
+#ifdef SERF_HAVE_OPENSSL
+
+extern const serf_ssl_bucket_type_t serf_ssl_bucket_type_openssl;
+
+#endif /* SERF_HAVE_OPENSSL */
+
+/* ==================================================================== */
+
+#if SERF_HAVE_MACOSXSSL
+
+extern const serf_ssl_bucket_type_t serf_ssl_bucket_type_macosxssl;
+
+#endif /* SERF_HAVE_MACOSXSSL */
+
+/* ==================================================================== */
+
+
+
+#endif
\ No newline at end of file
diff --git a/buckets/buckets.c b/buckets/buckets.c
index a64e9bd..28d64ba 100644
--- a/buckets/buckets.c
+++ b/buckets/buckets.c
@@ -14,10 +14,77 @@
  */
 
 #include <apr_pools.h>
+#include <apr_version.h>
+#include <apr_atomic.h>
 
 #include "serf.h"
 #include "serf_bucket_util.h"
 #include "serf_private.h"
+#include "serf_bucket_types.h"
+
+/* ==================================================================== */
+
+/* Code to select specific implementations for buckets (currently only
+   for SSL buckets).
+ */
+
+static apr_uint32_t available_bucket_impls;
+static apr_uint32_t bucket_impls;
+static apr_uint32_t have_init_bucket_impls = 0;
+
+/* Initializes the list of chosen bucket implementation libraries with those
+   included at compile time. */
+static void init_bucket_impls()
+{
+    apr_uint32_t val;
+#if APR_VERSION_AT_LEAST(1,0,0)
+    val = apr_atomic_xchg32(&have_init_bucket_impls, 1);
+#else
+    val = apr_atomic_cas(&have_init_bucket_impls, 1, 0);
+#endif
+
+    if (!val)
+    {
+        available_bucket_impls = 0;
+#ifdef SERF_HAVE_OPENSSL
+        available_bucket_impls |= SERF_IMPL_SSL_OPENSSL;
+#endif
+#ifdef SERF_HAVE_MACOSXSSL
+        available_bucket_impls |= SERF_IMPL_SSL_MACOSXSSL;
+#endif
+
+        bucket_impls = available_bucket_impls;
+    }
+}
+
+apr_uint32_t serf_config_enable_bucket_impls(apr_uint32_t selected)
+{
+    init_bucket_impls();
+
+    bucket_impls |= selected;
+    bucket_impls &= available_bucket_impls;
+
+    return bucket_impls;
+}
+
+apr_uint32_t serf_config_disable_bucket_impls(apr_uint32_t selected)
+{
+    init_bucket_impls();
+
+    bucket_impls &= ~selected;
+    bucket_impls &= available_bucket_impls;
+
+    return bucket_impls;
+}
+
+apr_uint32_t serf_config_get_bucket_impls(void)
+{
+    init_bucket_impls();
+
+    return bucket_impls;
+}
+
+/* ==================================================================== */
 
 serf_bucket_t *serf_bucket_create(
     const serf_bucket_type_t *type,
diff --git a/buckets/macosxssl_buckets.c b/buckets/macosxssl_buckets.c
new file mode 100644
index 0000000..96be161
--- /dev/null
+++ b/buckets/macosxssl_buckets.c
@@ -0,0 +1,2471 @@
+/* Copyright 2013 Justin Erenkrantz and Greg Stein
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This code implements the ssl bucket API using the services provided by Apple
+   on Mac OS X 10.7+:
+   - Secure Transport
+   - Keychain Services
+   - Certificate, Key and Trust Services.
+ 
+   Reference documentation can be found on http://developer.apple.com .
+ 
+   Source code can be found on http://www.opensource.apple.com . Search for the
+   Security-xxxxxx package, where xxxxxx is a version number.
+
+   Note: unfortunately, the reference documentation seems to be a specification
+   more than a correct representation of the actual implementation. So
+   analysis of the source code of the services is needed to understand its exact
+   behavior.
+
+   Furthermore, the implementation of the services has some bugs that needed
+   workarounds. This page is very helpful in identifying these bugs:
+   https://github.com/lorentey/LKSecurity/blob/master/Framework%20Bugs.markdown
+ 
+ 
+   A macosxssl_bucket implements following API's
+   - a serf_bucket_t for encryption
+   - a serf_bucket_t for decryption
+   - a serf_ssl_bucket_type_t for certificate handling, general SSL/TLS stuff.
+   - extra serf_macosxssl_ functions to integrate with Keychain.
+ */
+
+#ifdef SERF_HAVE_MACOSXSSL
+
+#include "serf.h"
+#include "serf_private.h"
+#include "serf_bucket_util.h"
+#include "bucket_private.h"
+
+#include <apr_strings.h>
+#include <apr_base64.h>
+#include <apr_file_io.h>
+
+#include <Security/SecureTransport.h>
+#include <Security/SecPolicy.h>
+#include <Security/SecImportExport.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecItem.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+
+/* The minimum amount of data we try to read and decrypt in one pass. */
+#define READ_BUFSIZE 8096
+
+typedef struct macosxssl_ssl_stream_t {
+    /* For an encrypt stream: data encrypted & not yet written to the network.
+       For a decrypt stream: data decrypted & not yet read by the application.*/
+    serf_bucket_t *pending;
+
+    /* For an encrypt stream: the outgoing data provided by the application.
+       For a decrypt stream: encrypted data read from the network. */
+    serf_bucket_t *stream;
+} macosxssl_ssl_stream_t;
+
+
+/* States for the different stages in the lifecyle of an SSL session. */
+typedef enum {
+    SERF_MACOSXSSL_INIT,       /* no SSL handshake yet */
+    SERF_MACOSXSSL_HANDSHAKE,  /* SSL handshake in progress */
+    SERF_MACOSXSSL_CONNECTED,  /* SSL handshake successfully finished */
+    SERF_MACOSXSSL_CLOSING,    /* SSL session closing */
+} macosxssl_session_state_t;
+
+typedef struct macosxssl_context_t {
+    /* How many open buckets refer to this context. */
+    int refcount;
+
+    /* Pool that stays alive during the whole ssl session */
+    apr_pool_t *pool;
+
+    /* Pool that is alive only during the hanshake phase. */
+    apr_pool_t *handshake_pool;
+
+    serf_bucket_alloc_t *allocator;
+
+    SSLContextRef st_ctxr;
+
+    /* Temporary keychain created when importing a client certificate. */
+    SecKeychainRef tempKeyChainRef;
+    char *keychain_temp_file;
+
+    /* Trust object created during validation of the server certificate. */
+    SecTrustRef trust;
+
+    /* stream of (to be) encrypted data, outgoing to the network. */
+    macosxssl_ssl_stream_t encrypt;
+
+    /* stream of (to be) decrypted data, read from the network. */
+    macosxssl_ssl_stream_t decrypt;
+
+    macosxssl_session_state_t state;
+
+    /* name of the peer, used with TLS's Server Name Indication extension. */
+    char *hostname;
+
+    /* Client cert callbacks */
+    serf_ssl_need_client_cert_t client_cert_callback;
+    serf_ssl_need_identity_t identity_callback;
+    void *identity_userdata;
+#if 0
+    apr_pool_t *client_cert_cache_pool;
+    const char *cert_file_success;
+#endif
+
+    /* Client cert PW callbacks */
+    serf_ssl_need_cert_password_t identity_pw_callback;
+    void *identity_pw_userdata;
+#if 0
+    apr_pool_t *cert_pw_cache_pool;
+    const char *cert_pw_success;
+#endif
+
+    /* Server cert callbacks */
+    serf_ssl_need_server_cert_t server_cert_callback;
+    serf_ssl_server_cert_chain_cb_t server_cert_chain_callback;
+    void *server_cert_userdata;
+
+    /* Flag, if set use the root certificates stored in the System keychain
+       when evaluating server certificates. */
+    int use_system_roots;
+
+    /* cache of the trusted certificates, added via serf_ssl_trust_cert(). */
+    apr_array_header_t *anchor_certs;
+    CFArrayRef anchor_certrefs;
+
+    /* Flag set when an asynchronous evaluation of the server certificate chain
+       is ongoing. */
+    int evaluate_in_progress;
+
+    /* Result of the evaluation of the server certificate chain. */
+    SecTrustResultType result;
+} macosxssl_context_t;
+
+/* Some forward declarations */
+static apr_status_t
+serf__macosxssl_load_identity_from_file(void *impl_ctx,
+                                        const serf_ssl_identity_t **identity,
+                                        const char *file_path,
+                                        apr_pool_t *pool);
+
+
+/* Copies the unicode string from a CFStringRef to a new buffer allocated
+ from pool. */
+static const char *
+CFStringToChar(CFStringRef str, apr_pool_t *pool)
+{
+    const char *ptr = CFStringGetCStringPtr(str, kCFStringEncodingMacRoman);
+
+    if (ptr == NULL) {
+        const int strlen = CFStringGetLength(str) * 2;
+        char *buf = apr_pcalloc(pool, strlen);
+        if (CFStringGetCString(str, buf, strlen, kCFStringEncodingMacRoman))
+            return buf;
+    } else {
+        return apr_pstrdup(pool, ptr);
+    }
+
+    return NULL;
+}
+
+static apr_status_t
+translate_macosxssl_status(OSStatus osstatus)
+{
+    apr_status_t status;
+
+    switch (osstatus)
+    {
+        case noErr:
+            return APR_SUCCESS;
+        case errSSLWouldBlock:
+            return APR_EAGAIN;
+        case errSSLClosedGraceful:
+            /* Server sent a */
+            status = APR_EOF;
+            break;
+        case errSSLClosedAbort:
+            status = APR_ECONNABORTED;
+            break;
+        default:
+            status = APR_EGENERAL;
+    }
+
+#if SSL_VERBOSE
+    {
+        apr_pool_t *temppool;
+        CFStringRef errref = SecCopyErrorMessageString(osstatus, NULL);
+        apr_pool_create(&temppool, NULL);
+
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "Unknown Secure Transport error: %d,%s.\n",
+                  osstatus, CFStringToChar(errref, temppool));
+        apr_pool_destroy(temppool);
+    }
+#endif
+
+    return status;
+}
+
+static apr_status_t cfrelease_ref(void *data)
+{
+    CFTypeRef tr = data;
+
+    if (tr)
+        CFRelease(tr);
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t cfrelease_trust(void *data)
+{
+    macosxssl_context_t *ssl_ctx = data;
+
+    if (ssl_ctx->trust)
+        CFRelease(ssl_ctx->trust);
+    ssl_ctx->trust = NULL;
+
+    return APR_SUCCESS;
+}
+
+/**
+ * Note for both read and write callback functions, from SecureTransport.h:
+ * "Data's memory is allocated by caller; on entry to these two functions
+ *  the *length argument indicates both the size of the available data and the
+ *  requested byte count. Number of bytes actually transferred is returned in
+ *  *length."
+ **/
+
+/** Secure Transport callback function.
+    Reads encrypted data from the network. **/
+static OSStatus
+macosxssl_read_cb(SSLConnectionRef connection,
+                  void *data,
+                  size_t *dataLength)
+{
+    const macosxssl_context_t *ssl_ctx = connection;
+    apr_status_t status = 0;
+    const char *buf;
+    char *outbuf = data;
+    size_t requested = *dataLength, buflen = 0;
+
+    serf__log(SSL_VERBOSE, __FILE__, "macosxssl_read_cb called for "
+              "%d bytes.\n", requested);
+
+    *dataLength = 0;
+    while (!status && requested) {
+        status = serf_bucket_read(ssl_ctx->decrypt.stream, requested,
+                                  &buf, &buflen);
+
+        if (SERF_BUCKET_READ_ERROR(status)) {
+            serf__log(SSL_VERBOSE, __FILE__, "Returned status %d.\n", status);
+            return -1;
+        }
+
+        serf__log(SSL_VERBOSE, __FILE__, "Read %d bytes with status %d.\n",
+                  buflen, status);
+
+        if (buflen) {
+            /* Copy the data in the buffer provided by the caller. */
+            memcpy(outbuf, buf, buflen);
+            outbuf += buflen;
+            requested -= buflen;
+            (*dataLength) += buflen;
+        }
+    }
+
+    if (APR_STATUS_IS_EAGAIN(status))
+        return errSSLWouldBlock;
+
+    if (!status)
+        return noErr;
+
+    /* TODO: map apr status to Mac OS X error codes(??) */
+    return -1;
+}
+
+/** Secure Transport callback function.
+    Writes encrypted data to the network. **/
+static OSStatus
+macosxssl_write_cb(SSLConnectionRef connection,
+                   const void *data,
+                   size_t *dataLength)
+{
+    serf_bucket_t *tmp;
+    const macosxssl_context_t *ctx = connection;
+
+    serf__log(SSL_VERBOSE, __FILE__, "macosxssl_write_cb called for "
+              "%d bytes.\n", *dataLength);
+
+    tmp = serf_bucket_simple_copy_create(data, *dataLength,
+                                         ctx->encrypt.pending->allocator);
+
+    serf_bucket_aggregate_append(ctx->encrypt.pending, tmp);
+
+    return noErr;
+}
+
+#pragma mark VALIDATE SERVER CERTIFICATES
+#pragma mark -
+
+/* Creates a macosxssl_certificate_t allocated on pool. */
+static apr_status_t
+create_macosxssl_certificate(macosxssl_certificate_t **out_macosxssl_cert,
+                             SecCertificateRef certref,
+                             int parse_content,
+                             apr_pool_t *pool)
+{
+    macosxssl_certificate_t *macosxssl_cert;
+    apr_status_t status = APR_SUCCESS;
+
+    macosxssl_cert = apr_pcalloc(pool, sizeof(macosxssl_certificate_t));
+    macosxssl_cert->certref = certref;
+
+    if (parse_content)
+        status = serf__macosxssl_read_X509_DER_certificate(
+                           &macosxssl_cert->content,
+                           macosxssl_cert,
+                           pool);
+    *out_macosxssl_cert = macosxssl_cert;
+
+    return status;
+}
+
+/* Creates a serf_ssl_certificate_t at depth allocated on pool. */
+static serf_ssl_certificate_t *
+create_ssl_certificate(SecCertificateRef certref,
+                       int depth,
+                       apr_pool_t *pool)
+{
+    macosxssl_certificate_t *macosxssl_cert;
+    serf_bucket_alloc_t *allocator;
+
+    /* Since we're not asking to parse the content we can ignore the status. */
+    (void) create_macosxssl_certificate(&macosxssl_cert, certref, 0, pool);
+
+    allocator = serf_bucket_allocator_create(pool, NULL, NULL);
+    return serf__create_certificate(allocator,
+                                    &serf_ssl_bucket_type_macosxssl,
+                                    macosxssl_cert,
+                                    depth);
+}
+
+/* Read the contents of a file in memory in a CFDataRef buffer. */
+static apr_status_t
+load_data_from_file(const char *file_path, CFDataRef *databuf, apr_pool_t *pool)
+{
+    apr_file_t *fp;
+    apr_finfo_t file_info;
+    apr_size_t len;
+    char *buf;
+    apr_status_t status;
+
+    status = apr_file_open(&fp, file_path,
+                           APR_FOPEN_READ | APR_FOPEN_BINARY,
+                           APR_FPROT_OS_DEFAULT, pool);
+    if (status)
+        return status;
+
+    /* Read the file in memory */
+    apr_file_info_get(&file_info, APR_FINFO_SIZE, fp);
+    buf = apr_palloc(pool, file_info.size);
+
+    status = apr_file_read_full(fp, buf, file_info.size, &len);
+    if (status)
+        return status;
+
+    *databuf = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                           (unsigned char *)buf,
+                                           file_info.size,
+                                           kCFAllocatorNull);
+
+    apr_file_close(fp);
+
+    return APR_SUCCESS;
+}
+
+/* Use Keychain Services to extract one or multiple SecCertificateRef's from
+   a data buffer.
+ */
+static apr_status_t
+load_certificate_from_databuf(CFDataRef databuf,
+                              CFArrayRef *items,
+                              apr_pool_t *pool)
+{
+    SecExternalItemType itemType;
+    OSStatus osstatus;
+    apr_status_t status = APR_SUCCESS;
+
+    osstatus = SecItemImport(databuf, NULL,
+                             kSecFormatUnknown,
+                             &itemType,
+                             0,    /* SecItemImportExportFlags */
+                             NULL, /* SecItemImportExportKeyParameters */
+                             NULL, /* SecKeychainRef */
+                             items);
+    if (osstatus != errSecSuccess)
+    {
+        /* TODO: should be handled in translate_... */
+        status = SERF_ERROR_SSL_CERT_FAILED;
+    }
+    
+    return status;
+}
+
+/* Logs the issuer and subject of cert. */
+static void
+log_certificate(macosxssl_certificate_t *cert, const char *msg)
+{
+#if SSL_VERBOSE
+    apr_hash_t *subject, *issuer;
+    apr_pool_t *tmppool;
+
+    apr_pool_create(&tmppool, NULL);
+    if (!cert->content) {
+        apr_status_t status;
+        status = serf__macosxssl_read_X509_DER_certificate(&cert->content,
+                                                           cert,
+                                                           tmppool);
+        if (status)
+            goto cleanup;
+    }
+
+    subject = (apr_hash_t *)apr_hash_get(cert->content,
+                                         "subject", APR_HASH_KEY_STRING);
+
+    serf__log(SSL_VERBOSE, __FILE__, msg);
+    serf__log(SSL_VERBOSE, __FILE__, "Subject:\n");
+    serf__log(SSL_VERBOSE, __FILE__, " CN:%s,",
+                     apr_hash_get(subject, "CN", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " OU:%s,",
+                     apr_hash_get(subject, "OU", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " O:%s,",
+                     apr_hash_get(subject, "O", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " L:%s,",
+                     apr_hash_get(subject, "L", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " ST:%s,",
+                     apr_hash_get(subject, "ST", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " C:%s,",
+                     apr_hash_get(subject, "C", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " E:%s\n",
+                     apr_hash_get(subject, "E", APR_HASH_KEY_STRING));
+
+    issuer = (apr_hash_t *)apr_hash_get(cert->content,
+                                        "issuer", APR_HASH_KEY_STRING);
+
+    serf__log(SSL_VERBOSE, __FILE__, "Issuer:\n");
+    serf__log(SSL_VERBOSE, __FILE__, " CN:%s,",
+              apr_hash_get(issuer, "CN", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " OU:%s,",
+                     apr_hash_get(issuer, "OU", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " O:%s,",
+                     apr_hash_get(issuer, "O", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " L:%s,",
+                     apr_hash_get(issuer, "L", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " ST:%s,",
+                     apr_hash_get(issuer, "ST", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " C:%s,",
+                     apr_hash_get(issuer, "C", APR_HASH_KEY_STRING));
+    serf__log_nopref(SSL_VERBOSE, " E:%s\n",
+                     apr_hash_get(issuer, "E", APR_HASH_KEY_STRING));
+
+cleanup:
+    apr_pool_destroy(tmppool);
+#endif
+}
+
+/* Finds the issuer certificate of cert in the provided list of 
+   SecCertificateRef's. *outcert is allocated in pool. */
+static apr_status_t
+find_issuer_cert_in_array(serf_ssl_certificate_t **outcert,
+                          macosxssl_certificate_t *cert,
+                          CFArrayRef certref_list,
+                          apr_pool_t *pool)
+{
+    CFDataRef issuer;
+    apr_pool_t *tmppool;
+    serf_bucket_alloc_t *tmpalloc;
+    int i;
+    apr_status_t status;
+
+    apr_pool_create(&tmppool, pool);
+    tmpalloc = serf_bucket_allocator_create(tmppool, NULL, NULL);
+
+    /* Get the issuer DER encoded data buffer of the provided certificate. */
+    log_certificate(cert, "Search for issuer of this cert:\n");
+    issuer = apr_hash_get(cert->content, "_issuer_der", APR_HASH_KEY_STRING);
+
+    /* Get the subject DER encoded data buffer of each cert in the list and
+       compare it with the issuer data buffer. */
+    for (i = 0; i < CFArrayGetCount(certref_list); i++)
+    {
+        macosxssl_certificate_t *list_cert;
+        CFDataRef subject;
+        SecCertificateRef certref;
+
+        certref = (SecCertificateRef)CFArrayGetValueAtIndex(certref_list, i);
+        status = create_macosxssl_certificate(&list_cert, certref, 1,
+                                              tmppool);
+        if (status)
+            goto cleanup;
+
+        subject = apr_hash_get(list_cert->content, "_subject_der",
+                               APR_HASH_KEY_STRING);
+
+        if (CFEqual(subject, issuer))
+        {
+            CFTypeRef outcertref;
+
+            /* This is the one. */
+            outcertref = CFArrayGetValueAtIndex(certref_list, i);
+
+            *outcert = create_ssl_certificate((SecCertificateRef)outcertref,
+                                              i,
+                                              pool);
+            status = APR_SUCCESS;
+            goto cleanup;
+        }
+    }
+
+    /* Nothing found. */
+    status = SERF_ERROR_SSL_CERT_FAILED;
+
+cleanup:
+    apr_pool_destroy(tmppool);
+    return status;
+}
+
+/* Certificate validation errors are only available as string. Convert them
+   to serf's failure codes. */
+static int
+convert_certerr_to_failure(const char *errstr)
+{
+    if (strcmp(errstr, "CSSMERR_TP_INVALID_ANCHOR_CERT") == 0)
+        return SERF_SSL_CERT_SELF_SIGNED;
+    if (strcmp(errstr, "CSSMERR_TP_CERT_EXPIRED") == 0)
+        return SERF_SSL_CERT_EXPIRED;
+    if (strcmp(errstr, "CSSMERR_TP_CERT_NOT_VALID_YET") == 0)
+        return SERF_SSL_CERT_NOTYETVALID;
+    if ((strcmp(errstr, "CSSMERR_TP_NOT_TRUSTED") == 0) ||
+        (strcmp(errstr, "CSSMERR_TP_VERIFICATION_FAILURE") == 0))
+        return SERF_SSL_CERT_UNKNOWNCA;
+
+    return SERF_SSL_CERT_UNKNOWN_FAILURE;
+}
+
+/* Validate a server certificate. Call back to the application if needed.
+   Returns APR_SUCCESS if the server certificate is accepted.
+   Otherwise returns an error.
+ */
+static int
+validate_server_certificate(macosxssl_context_t *ssl_ctx)
+{
+    CFArrayRef anchor_certrefs = NULL;
+    size_t depth_of_error, chain_depth;
+    int failures = 0;
+    OSStatus osstatus;
+    apr_status_t status;
+
+    serf__log(SSL_VERBOSE, __FILE__, "validate_server_certificate called.\n");
+    osstatus = SSLCopyPeerTrust(ssl_ctx->st_ctxr, &ssl_ctx->trust);
+    if (osstatus != noErr) {
+        status = translate_macosxssl_status(osstatus);
+        goto cleanup;
+    }
+    apr_pool_cleanup_register(ssl_ctx->handshake_pool, ssl_ctx,
+                              cfrelease_trust, cfrelease_trust);
+
+    if (!ssl_ctx->evaluate_in_progress)
+    {
+        CFArrayRef root_certrefs = NULL;
+        void *macosxssl_cls;
+        id tmp;
+
+        /* If the application provided certificates to trust, use them here. */
+        if (ssl_ctx->anchor_certs)
+        {
+            int anchor_certs = ssl_ctx->anchor_certs->nelts;
+            int i;
+            SecCertificateRef certs[anchor_certs];
+
+            for (i = 0; i < anchor_certs; i++)
+                certs[i] = APR_ARRAY_IDX(ssl_ctx->anchor_certs, i,
+                                         SecCertificateRef);
+
+            anchor_certrefs = CFArrayCreate(kCFAllocatorDefault,
+                                            (void *)certs,
+                                            anchor_certs,
+                                            NULL);
+            ssl_ctx->anchor_certrefs = anchor_certrefs;
+        }
+
+        /* If we can use the system's default root certificates, copy them
+           here. */
+        if (ssl_ctx->use_system_roots)
+        {
+            osstatus = SecTrustCopyAnchorCertificates(&root_certrefs);
+            if (osstatus != noErr) {
+                status = translate_macosxssl_status(osstatus);
+                goto cleanup;
+            }
+        }
+
+        /* Make one list of custom anchor and system root certificates and
+           add them to the trust object to use them during evaluation of the
+           server certificate chain. */
+        if (anchor_certrefs || root_certrefs)
+        {
+            CFIndex anchors, roots;
+            CFMutableArrayRef all_certrefs;
+
+            anchors = anchor_certrefs ? CFArrayGetCount(anchor_certrefs) : 0;
+            roots = root_certrefs ? CFArrayGetCount(root_certrefs) : 0;
+
+            all_certrefs = CFArrayCreateMutable(kCFAllocatorDefault,
+                                                anchors + roots, NULL);
+            if (anchor_certrefs)
+                CFArrayAppendArray(all_certrefs, anchor_certrefs,
+                                   CFRangeMake(0, anchors));
+            if (root_certrefs)
+                CFArrayAppendArray(all_certrefs, root_certrefs,
+                                   CFRangeMake(0, roots));
+
+            osstatus = SecTrustSetAnchorCertificates(ssl_ctx->trust,
+                                                     all_certrefs);
+            if (osstatus != noErr) {
+                status = translate_macosxssl_status(osstatus);
+                goto cleanup;
+            }
+        }
+
+        ssl_ctx->evaluate_in_progress = 1;
+
+        macosxssl_cls = objc_getClass("macosxssl_Buckets");
+        tmp = objc_msgSend(macosxssl_cls,
+                           sel_getUid("evaluate:trustResult:"),
+                           ssl_ctx->trust,
+                           &ssl_ctx->result);
+        osstatus = (OSStatus)(SInt64)tmp;
+        if (osstatus != noErr) {
+            status = translate_macosxssl_status(osstatus);
+            goto cleanup;
+        }
+    }
+
+    if (!ssl_ctx->result) {
+        /* No evaluation results received yet. */
+        return APR_EAGAIN;
+    }
+
+    /* Based on the contents of the user's Keychain, Secure Transport will make
+       a first validation of this certificate chain.
+       The status set here can in some cases be overridden by the application.*/
+    switch (ssl_ctx->result)
+    {
+        /* kSecTrustResultProceed Indicates you may proceed.
+           User trusts this certificate, so continue without calling back to
+           the application. */
+        case kSecTrustResultProceed:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultProceed.\n");
+            status = APR_SUCCESS;
+            goto cleanup;
+
+        /* Non-fatal errors, application decides. */
+
+        /* kSecTrustResultUnspecified Indicates user intent is unknown.
+           (certificate is valid, but user didn't specify trust) */
+        case kSecTrustResultUnspecified:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultUnspecified.\n");
+            status = APR_SUCCESS;
+            failures = 0;
+            break;
+
+        /* kSecTrustResultConfirm Indicates confirmation with the user is
+           required before proceeding. */
+        case kSecTrustResultConfirm:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultConfirm.\n");
+            status = SERF_ERROR_SSL_CERT_FAILED;
+            failures = SERF_SSL_CERT_CONFIRM_NEEDED;
+            break;
+
+        /* kSecTrustResultRecoverableTrustFailure Indicates a trust framework
+           failure; retry after fixing inputs. */
+        case kSecTrustResultRecoverableTrustFailure:
+            serf__log(SSL_VERBOSE, __FILE__,
+                      "kSecTrustResultRecoverableTrustFailure.\n");
+            status = SERF_ERROR_SSL_CERT_FAILED;
+            failures = 0; /* Failure info will be added later */
+            break;
+
+        /* Fatal errors. Since they cannot be overridden, don't call back to the
+           application but return with an error. */
+
+        /* kSecTrustResultInvalid Indicates an invalid setting or result. */
+        case kSecTrustResultInvalid:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultInvalid.\n");
+            status = SERF_ERROR_SSL_FATAL_CERT_INVALID;
+            goto cleanup;
+
+        /* kSecTrustResultDeny Indicates a user-configured deny; do not
+           proceed. */
+        case kSecTrustResultDeny:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultDeny.\n");
+            status = SERF_ERROR_SSL_FATAL_CERT_DENIED_IN_KEYCHAIN;
+            goto cleanup;
+
+        /* kSecTrustResultFatalTrustFailure Indicates a trust framework failure;
+           no "easy" fix. */
+        case kSecTrustResultFatalTrustFailure:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultFatalTrustFailure.\n");
+            status = SERF_ERROR_SSL_FATAL_CERT_TRUST_FAILURE;
+            goto cleanup;
+
+        /* kSecTrustResultOtherError Indicates a failure other than that of
+           trust evaluation. */
+        case kSecTrustResultOtherError:
+            serf__log(SSL_VERBOSE, __FILE__, "kSecTrustResultOtherError.\n");
+            status = SERF_ERROR_SSL_FATAL_CERT_FAILED;
+            goto cleanup;
+        default:
+            serf__log(SSL_VERBOSE, __FILE__, "unknown result from trust "
+                      "evaluation.\n");
+            status = SERF_ERROR_SSL_FATAL_CERT_FAILED;
+            goto cleanup;
+    }
+
+    /* Secure Transport only reports one error per evaluation. This is stored
+       at depth 0 in the result array, so we don't even know at what depth
+       the error occurred.
+       Get the total chain length (incuding anchor) from
+       SecTrustCopyProperties. */
+    {
+        CFArrayRef props = SecTrustCopyProperties(ssl_ctx->trust);
+        CFDictionaryRef dict = CFArrayGetValueAtIndex(props, 0);
+        CFStringRef errref = CFDictionaryGetValue(dict, kSecPropertyTypeError);
+
+        chain_depth = CFArrayGetCount(props); /* length of the full chain,
+                                                 including anchor cert. */
+
+        if (errref) {
+            apr_pool_t *tmppool;
+            const char *errstr;
+
+            apr_pool_create(&tmppool, NULL);
+            errstr = CFStringToChar(errref, tmppool);
+
+            failures |= convert_certerr_to_failure(errstr);
+            serf__log(SSL_VERBOSE, __FILE__,
+                      "Certificate ERROR: %s in chain of length %d.\n", errstr,
+                      chain_depth);
+            apr_pool_destroy(tmppool);
+        } else {
+            serf__log(SSL_VERBOSE, __FILE__, "No certificate validation errors "
+                      "found.\n");
+        }
+
+        CFRelease(props);
+    }
+
+    /* TODO: 0, oh really? How can we know where the error occurred? */
+    depth_of_error = 0;
+
+    /* Ask the application to validate the server certificate at depth 0.
+       TODO: any certificate at other depths with failures. */
+    if (ssl_ctx->server_cert_callback)
+    {
+        serf_ssl_certificate_t *cert;
+        SecCertificateRef certref;
+
+        serf__log(SSL_VERBOSE, __FILE__, "Call application for server cert "
+                  "validation.\n");
+
+        certref = SecTrustGetCertificateAtIndex(ssl_ctx->trust, 0);
+        cert = create_ssl_certificate(certref, 0, ssl_ctx->handshake_pool);
+
+        /* Callback for further verification. */
+        status = ssl_ctx->server_cert_callback(ssl_ctx->server_cert_userdata,
+                                               failures, cert);
+        serf__log(SSL_VERBOSE, __FILE__, "Application returned status %d.\n",
+                  status);
+    }
+
+    /* We need to get the full certificate chain and provide it to the
+       application.
+
+       There are 4 scenario's:
+       1. The server provided all certificates including the root CA.
+       2. The server provided all certificates except the anchor certificate.
+          The anchor certificate is stored in a Keychain. (a root CA provided
+          by Apple or a certificate imported in a Keychain by the user).
+       3. The server provided all certificates except the anchor certificate.
+          The anchor certificate was explicitly trusted by the application via
+          serf_ssl_trust_cert.
+       4. The server provided some certificates but not the root CA. This cert
+          is not available in the Keychain nor in the trusted list set by the
+          application.
+
+       The Keychain API gives us multiple options to get the full chain in
+       scenario 1, 2 and 4. However, when the anchor certificate was provided
+       by the application, it's not included in the chain returned by the
+       Keychain API.
+     
+       We get the total chain length from SecTrustCopyProperties. We get the
+       certificate chain from the trust object via SecTrustGetCertificateCount
+       and SecTrustGetCertificateAtIndex. If the length of the certificate
+       chain is one shorter than the expected total chain length, we know we're
+       in scenario 3.
+     */
+    if (ssl_ctx->server_cert_chain_callback)
+    {
+        serf_ssl_certificate_t **certs;
+        int certs_len, actual_len, i;
+
+        serf__log(SSL_VERBOSE, __FILE__, "Call application for server cert "
+                  "chain validation.\n");
+
+        /* Room for the total chain length and a trailing NULL.  */
+        certs = apr_palloc(ssl_ctx->handshake_pool,
+                           sizeof(*certs) * (chain_depth  + 1));
+
+        /* Copy the certificates as provided by the server + Keychain. */
+        certs_len = SecTrustGetCertificateCount(ssl_ctx->trust);
+        for (i = 0; i < certs_len; ++i)
+        {
+            SecCertificateRef certref;
+
+            certref = SecTrustGetCertificateAtIndex(ssl_ctx->trust, i);
+            certs[i] = create_ssl_certificate(certref, i,
+                                              ssl_ctx->handshake_pool);
+        }
+
+        actual_len = certs_len;
+        if (chain_depth > certs_len)
+        {
+            /* The chain relies on (root) CA certificates not provided by the
+               server or a Keychain (scenario 3). We have to find them in the
+               list of trusted anchor certificates.
+             */
+            SecCertificateRef certref;
+            macosxssl_certificate_t *cert;
+
+            serf__log(SSL_VERBOSE, __FILE__, "Chain length (%d) is longer than "
+                      "what we received from the server (%d). Search the "
+                      "remaining anchor certificate.\n",
+                      chain_depth, certs_len);
+
+            /* Take the last known certificate and search its issuer in the
+               list of trusted anchor certificates. */
+            certref = SecTrustGetCertificateAtIndex(ssl_ctx->trust,
+                                                    certs_len - 1);
+            status = create_macosxssl_certificate(&cert, certref, 1,
+                                                  ssl_ctx->handshake_pool);
+
+            status = find_issuer_cert_in_array(&certs[certs_len],
+                                               cert,
+                                               ssl_ctx->anchor_certrefs,
+                                               ssl_ctx->handshake_pool);
+            if (!status)
+                actual_len++;
+        }
+
+        status =
+            ssl_ctx->server_cert_chain_callback(ssl_ctx->server_cert_userdata,
+                failures, 0, /*depth_of_error,*/
+                (const serf_ssl_certificate_t * const *)certs,
+                actual_len);
+    }
+
+    /* Return a specific error if the server certificate is not accepted by
+       S.T./Keychain and the application has not set callbacks to override
+       this. */
+    if (failures &&
+        !ssl_ctx->server_cert_chain_callback &&
+        !ssl_ctx->server_cert_callback)
+    {
+        status = SERF_ERROR_SSL_CERT_FAILED;
+    }
+
+cleanup:
+    if (anchor_certrefs)
+        CFRelease(anchor_certrefs);
+
+    return status;
+}
+
+/**** Client identity related code ****/
+/**************************************/
+#pragma mark NEED CLIENT IDENTITY 
+#pragma mark -
+
+static apr_status_t delete_temp_keychain(void *data)
+{
+    macosxssl_context_t *ssl_ctx = data;
+    apr_status_t status = APR_SUCCESS;
+    OSStatus osstatus;
+
+    if (!ssl_ctx->tempKeyChainRef)
+        return APR_SUCCESS;
+
+    osstatus = SecKeychainDelete(ssl_ctx->tempKeyChainRef);
+    if (osstatus != errSecSuccess) {
+        status = translate_macosxssl_status(osstatus);
+    }
+    ssl_ctx->tempKeyChainRef = NULL;
+
+    return status;
+}
+
+static apr_status_t create_temp_keychain(macosxssl_context_t *ssl_ctx,
+                                         apr_pool_t *pool)
+{
+    apr_file_t *tmpfile;
+    const char *temp_dir;
+    apr_status_t status;
+    OSStatus osstatus;
+
+    if (ssl_ctx->tempKeyChainRef)
+        return APR_SUCCESS;
+
+    /* The Keychain API only allows us to load an identity (private key +
+       certificate) for use in the SetCertificate call in a keychain.
+       We don't want to load this identity in the login or system keychain,
+       so we need to create a temporary keychain.
+
+       For the duration of the SSL handshake, this keychain will be visible to
+       the user in the Keychain Access tool.
+     */
+
+    /* We need a unique filename for a temporary file. So create an empty
+       file using APR and close it immediately. */
+    status = apr_temp_dir_get(&temp_dir, pool);
+    if (status)
+        return status;
+
+    status = apr_filepath_merge(&ssl_ctx->keychain_temp_file,
+                                temp_dir,
+                                "tempfile_XXXXXX",
+                                APR_FILEPATH_NATIVE | APR_FILEPATH_NOTRELATIVE,
+                                pool);
+    if (status)
+        return status;
+
+    status = apr_file_mktemp(&tmpfile, ssl_ctx->keychain_temp_file,
+                             APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
+                                 APR_DELONCLOSE | APR_BINARY,
+                             pool);
+    if (status)
+        return status;
+
+    status = apr_file_close(tmpfile);
+    if (status)
+        return status;
+
+    serf__log(SSL_VERBOSE, __FILE__, "Creating temporary keychain in %s.\n",
+              ssl_ctx->keychain_temp_file);
+
+    /* TODO: random password */
+    /* TODO: standard access rights gives unlimited access to the keychain for
+       this application. Other applications can also access the keychain,
+       but require confirmation from the user (no pwd needed). Probably better
+       if the keychain is locked down so that e.g. searches for identity
+       objects don't return any from this keychain. */
+    osstatus = SecKeychainCreate(ssl_ctx->keychain_temp_file,
+                                 4,
+                                 "serf",
+                                 FALSE,
+                                 NULL, /* Standard access rights */
+                                 &ssl_ctx->tempKeyChainRef);
+    if (osstatus != errSecSuccess) {
+        return translate_macosxssl_status(osstatus);
+    }
+    apr_pool_cleanup_register(pool, ssl_ctx,
+                              delete_temp_keychain, delete_temp_keychain);
+
+    return APR_SUCCESS;
+}
+
+/* Find the certificate of the issuer of certref in the keychains. */
+static apr_status_t
+find_issuer_certificate_in_keychain(macosxssl_certificate_t **out_cert,
+                                    SecCertificateRef certref,
+                                    apr_pool_t *pool)
+{
+    CFErrorRef error = NULL;
+    CFDataRef issuer;
+    apr_pool_t *tmppool;
+    apr_status_t status;
+
+    apr_pool_create(&tmppool, pool);
+
+    issuer = SecCertificateCopyNormalizedIssuerContent(certref,
+                                                       &error);
+    if (error) {
+        CFStringRef errstr = CFErrorCopyDescription(error);
+
+        serf__log(SSL_VERBOSE, __FILE__, "Can't get issuer DER buffer from "
+                  "certificate, reason: %s.\n",
+                  CFStringToChar(errstr, tmppool));
+        CFRelease(error);
+
+        return SERF_ERROR_SSL_CERT_FAILED;
+    }
+    else
+    {
+        CFDictionaryRef query;
+        macosxssl_certificate_t *cert, *issuer_cert;
+        SecCertificateRef issuer_certref;
+        CFDataRef cert_issuer, issuer_subject;
+        OSStatus osstatus;
+
+        const void *keys[] =   { kSecClass, kSecAttrSubject,
+                                 kSecMatchLimit, kSecReturnRef };
+        const void *values[] = { kSecClassCertificate, issuer,
+                                 kSecMatchLimitOne, kCFBooleanTrue };
+
+        /* Find a certificate with issuer in the keychains. */
+        query = CFDictionaryCreate(kCFAllocatorDefault,
+                                   (const void **)keys, (const void **)values,
+                                   4L, NULL, NULL);
+        osstatus = SecItemCopyMatching(query, (CFTypeRef*)&issuer_certref);
+        CFRelease(query);
+        CFRelease(issuer);
+        if (osstatus != errSecSuccess) {
+            return translate_macosxssl_status(osstatus);
+        }
+
+        /* if SecItemCopyMatching doesn't find a matching certificate, it is
+           known that it returns another (no kidding), so check that we received
+           the right certificate.
+         */
+        status = create_macosxssl_certificate(&cert,
+                                              certref,
+                                              1,
+                                              pool);
+        if (status)
+            return status;
+        status = create_macosxssl_certificate(&issuer_cert,
+                                              issuer_certref,
+                                              1,
+                                              pool);
+        if (status)
+            return status;
+
+        cert_issuer =  apr_hash_get(cert->content, "_issuer_der",
+                                     APR_HASH_KEY_STRING);
+        issuer_subject =  apr_hash_get(issuer_cert->content, "_subject_der",
+                                       APR_HASH_KEY_STRING);
+
+        if (CFEqual(cert_issuer, issuer_subject))
+        {
+            *out_cert = issuer_cert;
+            return APR_SUCCESS;
+        }
+
+        *out_cert = NULL;
+
+        return APR_SUCCESS;
+    }
+}
+
+/* Create a list of all certificates in between certref and any anchor
+   certificate in the list of anchor_certrefs.
+   Caller is responsible for cleanin up *intermediate_ca_certrefs. */
+static apr_status_t
+find_intermediate_cas(CFArrayRef *intermediate_ca_certrefs,
+                      SecCertificateRef certref,
+                      CFArrayRef peer_certrefs,
+                      apr_pool_t *pool)
+{
+    macosxssl_certificate_t *prevcert;
+    CFMutableArrayRef ca_certrefs;
+    apr_pool_t *tmppool;
+    apr_status_t status;
+
+    ca_certrefs = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+
+    if (peer_certrefs == NULL ||
+        CFArrayGetCount(peer_certrefs) == 0)
+    {
+        /* The server didn't provide any certificate at all?? */
+        *intermediate_ca_certrefs = ca_certrefs;
+
+        return APR_SUCCESS;
+    }
+
+    apr_pool_create(&tmppool, pool);
+
+    status = create_macosxssl_certificate(&prevcert, certref, 1, tmppool);
+    if (status)
+        goto cleanup;
+
+    /* Get the issuer DER encoded data buffer of the provided certificate. */
+    while (1)
+    {
+        macosxssl_certificate_t *issuer_cert;
+        CFDataRef issuer, subject;
+        serf_ssl_certificate_t *dummy_cert;
+
+        /* Find issuer in the list of certificates sent by the server. */
+        status = find_issuer_cert_in_array(&dummy_cert,
+                                           prevcert,
+                                           peer_certrefs,
+                                           tmppool);
+        if (status == APR_SUCCESS)
+            goto cleanup;
+#if 0
+        /* We have to send the certificate to the server. Find issuer in the
+           list of anchor certificates set by the application. */
+        status = find_issuer_cert_in_array(&cert,
+                                           prevcert,
+                                           ssl_ctx->anchor_certs,
+                                           tmppool)
+        if (status == APR_SUCCESS)
+            goto cleanup;
+#endif
+
+        /* Issuer certificate was not found in peer_certs, add it to the
+           output list. */
+        status = find_issuer_certificate_in_keychain(&issuer_cert,
+                                                     prevcert->certref,
+                                                     pool);
+        if (status)
+            goto cleanup;
+
+        if (!issuer_cert)
+        {
+            /* The issuer's certificate was not found in the keychain.
+               Send what we have to the server. */
+            status = APR_SUCCESS;
+            goto cleanup;
+        }
+
+        CFArrayAppendValue(ca_certrefs, issuer_cert->certref);
+
+        prevcert = issuer_cert;
+
+        /* break if selfsigned */
+        subject = apr_hash_get(issuer_cert->content, "_subject_der",
+                               APR_HASH_KEY_STRING);
+        issuer = apr_hash_get(issuer_cert->content, "_issuer_der",
+                               APR_HASH_KEY_STRING);
+        if (CFEqual(subject, issuer))
+        {
+            status = APR_SUCCESS;
+            goto cleanup;
+        }
+    }
+
+    /* Nothing found. */
+    status = SERF_ERROR_SSL_CERT_FAILED;
+    
+cleanup:
+    *intermediate_ca_certrefs = ca_certrefs;
+    apr_pool_destroy(tmppool);
+    return status;
+}
+
+static apr_status_t
+callback_for_identity_password(macosxssl_context_t *ssl_ctx,
+                               const char *cert_path,
+                               const char **passphrase)
+{
+    if (ssl_ctx->identity_pw_callback)
+    {
+        apr_status_t status;
+
+        status = ssl_ctx->identity_pw_callback(ssl_ctx->identity_pw_userdata,
+                                               cert_path,
+                                               passphrase);
+        return status;
+    }
+
+    return SERF_ERROR_SSL_CLIENT_CERT_PW_FAILED;
+}
+
+static apr_status_t
+callback_for_identity(macosxssl_context_t *ssl_ctx,
+                      apr_hash_t **dnlist,
+                      apr_size_t dnlen,
+                      SecIdentityRef *identityref,
+                      apr_pool_t *pool)
+{
+    apr_status_t status;
+    const serf_ssl_identity_t *identity;
+
+    if (ssl_ctx->client_cert_callback)
+    {
+        const char *cert_path;
+
+        status = ssl_ctx->client_cert_callback(ssl_ctx->identity_userdata,
+                                               &cert_path);
+        if (status)
+            return status;
+
+        status = serf__macosxssl_load_identity_from_file(ssl_ctx, &identity,
+                                                         cert_path, pool);
+        if (status)
+            return status;
+    } else if (ssl_ctx->identity_callback)
+    {
+        status = ssl_ctx->identity_callback(ssl_ctx->identity_userdata,
+                                            dnlist, dnlen, &identity, pool);
+    } else
+        status = APR_EGENERAL;
+
+    if (status == APR_SUCCESS)
+        *identityref = (SecIdentityRef)identity->impl_cert;
+
+    return status;
+}
+
+/* Get a client certificate for this server from the application. */
+static apr_status_t
+provide_client_certificate(macosxssl_context_t *ssl_ctx)
+{
+    SecIdentityRef identityref = NULL;
+    CFArrayRef dnlistrefs = NULL;
+    apr_hash_t **dnlist = NULL;
+    apr_size_t dnlen = 0;
+    apr_pool_t *tmppool;
+    apr_status_t status;
+    OSStatus osstatus;
+
+    serf__log(SSL_VERBOSE, __FILE__, "provide_client_certificate called.\n");
+
+    /* The server asked for a client certificate but we can't ask the
+       application. Consider this a success, the server decides if the
+       request was optional or mandatory. */
+    if (!ssl_ctx->client_cert_callback &&
+        !ssl_ctx->identity_callback)
+    {
+        serf__log(SSL_VERBOSE, __FILE__, "Server asked for client "
+                  "certificate, but the application didn't set the "
+                  "necessary callback.\n");
+        return APR_SUCCESS;
+    }
+
+    /* The server will send us a list of Distinguished Names, indicating that
+       client certificates issued by one of these DN's are acceptable. */
+    osstatus = SSLCopyDistinguishedNames(ssl_ctx->st_ctxr, &dnlistrefs);
+    if (osstatus != noErr) {
+        return translate_macosxssl_status(osstatus);
+    }
+
+    apr_pool_create(&tmppool, ssl_ctx->pool);
+    if (dnlistrefs && CFArrayGetCount(dnlistrefs) > 0)
+    {
+        int i;
+
+        dnlen = CFArrayGetCount(dnlistrefs);
+        dnlist = apr_palloc(tmppool, dnlen * sizeof(apr_hash_t*));
+
+        for (i = 0; i < dnlen; i++) {
+            CFDataRef cader = CFArrayGetValueAtIndex(dnlistrefs, i);
+            apr_hash_t *ca;
+
+            status = serf__macosxssl_read_X509_DER_DN(&ca, cader, tmppool);
+            if (status)
+                goto cleanup;
+
+            dnlist[i] = ca;
+        }
+    }
+
+    /* Call the application to find an identity trusted by one of the DN's
+       provided by the server. */
+    status = callback_for_identity(ssl_ctx, dnlist, dnlen, &identityref,
+                                   ssl_ctx->pool);
+    if (status)
+        goto cleanup;
+
+    /* If the issuer of the client certificate is not in the list
+       of certificates the server provided, we need to send it along.
+       Otherwise the server can complain that it doesn't trust the
+       client identity.
+       Note: this is what happens with the Belgian Personal ID Card on
+       site https://https://test.eid.belgium.be, where the "Citizen CA"
+       certificate is the issuer of the client certificate, but is not
+       sent by the server. */
+    if (identityref)
+    {
+        CFArrayRef intermediate_certrefs, peer_certrefs;
+        SecCertificateRef cert;
+        CFMutableArrayRef items;
+
+        /* Secure Transport assumes the following:
+           The certificate references remain valid for the lifetime of the
+           session.
+           The identity specified in certRefs[0] is capable of signing. */
+        items = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+        CFArrayAppendValue(items, identityref);
+        apr_pool_cleanup_register(ssl_ctx->pool, items,
+                                  cfrelease_ref, cfrelease_ref);
+
+        osstatus = SecIdentityCopyCertificate(identityref, &cert);
+        if (osstatus != noErr) {
+            status = translate_macosxssl_status(osstatus);
+            goto cleanup;
+        }
+
+        osstatus = SSLCopyPeerCertificates(ssl_ctx->st_ctxr,
+                                           &peer_certrefs);
+        if (osstatus != noErr) {
+            status = translate_macosxssl_status(osstatus);
+            goto cleanup;
+        }
+
+        status = find_intermediate_cas(&intermediate_certrefs,
+                                       cert,
+                                       peer_certrefs,
+                                       ssl_ctx->pool);
+        CFRelease(peer_certrefs);
+        if (status)
+            goto cleanup;
+
+        /* Add the intermediate certificates to the list to send to the
+           server. */
+        CFArrayAppendArray(items, intermediate_certrefs,
+                           CFRangeMake(0,
+                                       CFArrayGetCount(intermediate_certrefs)));
+
+        /* This can show a popup to ask the user if the application is
+           allowed to use the signing key. */
+        osstatus = SSLSetCertificate(ssl_ctx->st_ctxr, items);
+        if (osstatus != noErr) {
+            status = translate_macosxssl_status(osstatus);
+            goto cleanup;
+        }
+
+        status = APR_SUCCESS;
+    }
+
+cleanup:
+    apr_pool_destroy(tmppool);
+    if (dnlistrefs)
+        CFRelease(dnlistrefs);
+
+    return status;
+}
+
+/* Use Keychain Services to extract a SecIndentityRef (client private key +
+   certificate) from a data buffer. Databuf needs to be in PKCS12 format.
+   Caller is responsible to clean up items.
+ */
+static apr_status_t
+load_identity_from_databuf(macosxssl_context_t *ssl_ctx,
+                           const serf_ssl_identity_t **identity,
+                           const char *label,
+                           CFDataRef databuf,
+                           apr_pool_t *pool)
+{
+    SecExternalFormat format;
+    SecItemImportExportKeyParameters keyParams;
+    SecExternalItemType itemType;
+
+    /* SecItemImport will crash if keyUsage member is not set to NULL. */
+    memset(&keyParams, 0, sizeof(SecItemImportExportKeyParameters));
+
+    format = kSecFormatPKCS12;
+    itemType = kSecItemTypeUnknown;
+
+    /* Try importing the identity until it succeeds, fails or the application
+       stops setting passphrases. */
+    while (1)
+    {
+        CFArrayRef items;
+        OSStatus osstatus;
+        apr_status_t status;
+        const char *passphrase;
+
+        osstatus = SecItemImport(databuf,
+                                 NULL,
+                                 &format,
+                                 &itemType,
+                                 0,    /* SecItemImportExportFlags */
+                                 &keyParams,
+                                 ssl_ctx->tempKeyChainRef,
+                                 &items);
+        if (osstatus == errSecSuccess)
+        {
+            *identity = NULL;
+
+            /* Identity successfully imported in the keychain, return the
+               (wrapped) reference to the caller. */
+            if (CFArrayGetCount(items) > 0)
+            {
+                SecIdentityRef identityref;
+
+                identityref = (SecIdentityRef)CFArrayGetValueAtIndex(items, 0);
+
+                if (!identityref)
+                    return SERF_ERROR_SSL_CERT_FAILED;
+
+                *identity = serf__create_identity(&serf_ssl_bucket_type_macosxssl,
+                                                  identityref, NULL, pool);
+
+                return APR_SUCCESS;
+            }
+
+            return SERF_ERROR_SSL_CERT_FAILED;
+        } else if (osstatus == errSecPassphraseRequired)
+        {
+            CFStringRef pwref;
+
+            status = callback_for_identity_password(
+                                                    ssl_ctx,
+                                                    label,
+                                                    &passphrase);
+            if (status)
+                return status;
+
+            pwref = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                                  (unsigned char *)passphrase,
+                                                  strlen(passphrase),
+                                                  kCFStringEncodingMacRoman,
+                                                  false,
+                                                  kCFAllocatorNull);
+            keyParams.passphrase = pwref;
+        } else {
+            /* TODO: should be handled in translate_... */
+            return SERF_ERROR_SSL_CERT_FAILED;
+        }
+    }
+}
+
+/**** Run the SSL handshake. ****/
+/********************************/
+#pragma mark HANDSHAKE AND INITIALIZATION
+#pragma mark -
+static apr_status_t do_handshake(macosxssl_context_t *ssl_ctx)
+{
+    OSStatus osstatus;
+    apr_status_t status = APR_SUCCESS;
+
+    if (ssl_ctx->state == SERF_MACOSXSSL_INIT ||
+        ssl_ctx->state == SERF_MACOSXSSL_HANDSHAKE)
+    {
+        ssl_ctx->state = SERF_MACOSXSSL_HANDSHAKE;
+
+        serf__log(SSL_VERBOSE, __FILE__, "do_handshake called.\n");
+
+        if (ssl_ctx->evaluate_in_progress) {
+            if (ssl_ctx->result) {
+                status = validate_server_certificate(ssl_ctx);
+                ssl_ctx->evaluate_in_progress = 0;
+                if (!status)
+                    return APR_EAGAIN;
+                return status;
+            } else {
+                serf__log(SSL_VERBOSE, __FILE__, "evaluation in progress, but "
+                          "no results were received yet.\n");
+                return APR_EAGAIN;
+            }
+        }
+
+        osstatus = SSLHandshake(ssl_ctx->st_ctxr);
+        if (osstatus)
+            serf__log(SSL_VERBOSE, __FILE__, "do_handshake returned err %d.\n",
+                      osstatus);
+
+        switch(osstatus) {
+            case noErr:
+                status = APR_SUCCESS;
+                break;
+            case errSSLServerAuthCompleted:
+                /* Server's cert validation was disabled, so we can to do this
+                 here. */
+                status = validate_server_certificate(ssl_ctx);
+                if (!status)
+                    return APR_EAGAIN;
+                break;
+            case errSSLClientCertRequested:
+                status = provide_client_certificate(ssl_ctx);
+                if (!status)
+                    return APR_EAGAIN;
+                break;
+            default:
+                status = translate_macosxssl_status(osstatus);
+                break;
+        }
+
+        if (!status)
+        {
+            serf__log(SSL_VERBOSE, __FILE__, "ssl/tls handshake successful.\n");
+            ssl_ctx->state = SERF_MACOSXSSL_CONNECTED;
+
+            /* We can now safely cleanup the temporary resources created during
+               handshake (i.e. the temporary keychain used to load the client
+               identity. */
+            apr_pool_destroy(ssl_ctx->handshake_pool);
+            ssl_ctx->handshake_pool = NULL;
+
+            ssl_ctx->result = 0;
+            ssl_ctx->evaluate_in_progress = 0;
+        }
+    }
+
+    return status;
+}
+
+/* Callback function for the encrypt.pending and decrypt.pending stream-type
+   aggregate buckets.
+ */
+static apr_status_t
+pending_stream_eof(void *baton,
+                   serf_bucket_t *pending)
+{
+    /* Both pending streams have to stay open so that the Secure Transport
+       library can keep appending data buckets. */
+    return APR_EAGAIN;
+}
+
+static macosxssl_context_t *
+macosxssl_init_context(serf_bucket_alloc_t *allocator)
+{
+    macosxssl_context_t *ssl_ctx;
+
+    ssl_ctx = serf_bucket_mem_calloc(allocator, sizeof(*ssl_ctx));
+    ssl_ctx->refcount = 0;
+
+    apr_pool_create(&ssl_ctx->pool, NULL);
+    apr_pool_create(&ssl_ctx->handshake_pool, ssl_ctx->pool);
+
+    /* Set up the stream objects. */
+    ssl_ctx->encrypt.pending = serf__bucket_stream_create(allocator,
+                                                          pending_stream_eof,
+                                                          NULL);
+    ssl_ctx->decrypt.pending = serf__bucket_stream_create(allocator,
+                                                          pending_stream_eof,
+                                                          NULL);
+
+    /* Set up a Secure Transport session. */
+    ssl_ctx->state = SERF_MACOSXSSL_INIT;
+
+    if (SSLNewContext(FALSE, &ssl_ctx->st_ctxr))
+        return NULL;
+
+    if (SSLSetIOFuncs(ssl_ctx->st_ctxr, macosxssl_read_cb, macosxssl_write_cb))
+        return NULL;
+
+    /* Ensure the macosxssl_context will be passed to the read and write callback
+     functions. */
+    if (SSLSetConnection(ssl_ctx->st_ctxr, ssl_ctx))
+        return NULL;
+
+    /* We do our own validation of server certificates.
+       Note that Secure Transport will not do any validation with this option
+       enabled, it's all or nothing. */
+    if (SSLSetSessionOption(ssl_ctx->st_ctxr,
+                            kSSLSessionOptionBreakOnServerAuth,
+                            true))
+        return NULL;
+    if (SSLSetEnableCertVerify(ssl_ctx->st_ctxr, false))
+        return NULL;
+
+    /* If the handshake needs a client identity to continue, break it
+       temporarily so we can call back to the application. */
+    if (SSLSetSessionOption(ssl_ctx->st_ctxr,
+                            kSSLSessionOptionBreakOnCertRequested,
+                            true))
+        return NULL;
+
+    return ssl_ctx;
+}
+
+static apr_status_t
+macosxssl_free_context(macosxssl_context_t *ssl_ctx,
+                       serf_bucket_alloc_t *allocator)
+{
+    apr_status_t status = APR_SUCCESS;
+
+    (void)SSLDisposeContext(ssl_ctx->st_ctxr);
+
+    if (ssl_ctx->handshake_pool)
+        apr_pool_destroy(ssl_ctx->handshake_pool);
+    apr_pool_destroy(ssl_ctx->pool);
+
+    serf_bucket_mem_free(allocator, ssl_ctx);
+
+    if (status) {
+        return APR_EGENERAL;
+    }
+    
+    return APR_SUCCESS;
+}
+
+/**** SSL_BUCKET API ****/
+/************************/
+#pragma mark SSL_BUCKET API
+#pragma mark -
+static void *
+serf__macosxssl_decrypt_create(serf_bucket_t *bucket,
+                               serf_bucket_t *stream,
+                               void *impl_ctx,
+                               serf_bucket_alloc_t *allocator)
+{
+    macosxssl_context_t *ssl_ctx;
+    bucket->type = &serf_bucket_type_macosxssl_decrypt;
+    bucket->allocator = allocator;
+
+    if (impl_ctx)
+        bucket->data = impl_ctx;
+    else
+        bucket->data = macosxssl_init_context(allocator);
+
+    ssl_ctx = bucket->data;
+    ssl_ctx->refcount++;
+    ssl_ctx->decrypt.stream = stream;
+    ssl_ctx->allocator = allocator;
+
+    return bucket->data;
+}
+
+static void *
+serf__macosxssl_encrypt_create(serf_bucket_t *bucket,
+                               serf_bucket_t *stream,
+                               void *impl_ctx,
+                               serf_bucket_alloc_t *allocator)
+{
+    macosxssl_context_t *ssl_ctx;
+    bucket->type = &serf_bucket_type_macosxssl_encrypt;
+    bucket->allocator = allocator;
+
+    if (impl_ctx)
+        bucket->data = impl_ctx;
+    else
+        bucket->data = macosxssl_init_context(allocator);
+
+    ssl_ctx = bucket->data;
+    ssl_ctx->refcount++;
+    ssl_ctx->encrypt.stream = stream;
+    ssl_ctx->allocator = allocator;
+
+    return bucket->data;
+}
+
+static void *
+serf__macosxssl_decrypt_context_get(serf_bucket_t *bucket)
+{
+    return NULL;
+}
+
+static void *
+serf__macosxssl_encrypt_context_get(serf_bucket_t *bucket)
+{
+    return NULL;
+}
+
+
+static void
+serf__macosxssl_client_cert_provider_set(
+        void *impl_ctx,
+        serf_ssl_need_client_cert_t callback,
+        void *data,
+        void *cache_pool)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->client_cert_callback = callback;
+    ssl_ctx->identity_userdata = data;
+}
+
+static void
+serf__macosxssl_identity_provider_set(void *impl_ctx,
+                                      serf_ssl_need_identity_t callback,
+                                      void *data,
+                                      void *cache_pool)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->identity_callback = callback;
+    ssl_ctx->identity_userdata = data;
+}
+
+static void
+serf__macosxssl_client_cert_password_set(
+        void *impl_ctx,
+        serf_ssl_need_cert_password_t callback,
+        void *data,
+        void *cache_pool)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->identity_pw_callback = callback;
+    ssl_ctx->identity_pw_userdata = data;
+}
+
+static void
+serf__macosxssl_server_cert_callback_set(
+        void *impl_ctx,
+        serf_ssl_need_server_cert_t callback,
+        void *data)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->server_cert_callback = callback;
+    ssl_ctx->server_cert_userdata = data;
+}
+
+static void
+serf__macosxssl_server_cert_chain_callback_set(
+        void *impl_ctx,
+        serf_ssl_need_server_cert_t cert_callback,
+        serf_ssl_server_cert_chain_cb_t cert_chain_callback,
+        void *data)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->server_cert_callback = cert_callback;
+    ssl_ctx->server_cert_chain_callback = cert_chain_callback;
+    ssl_ctx->server_cert_userdata = data;
+}
+
+static apr_status_t
+serf__macosxssl_set_hostname(void *impl_ctx, const char * hostname)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+    OSStatus osstatus;
+
+    ssl_ctx->hostname = serf_bstrdup(ssl_ctx->allocator, hostname);
+    osstatus= SSLSetPeerDomainName(ssl_ctx->st_ctxr,
+                                   ssl_ctx->hostname,
+                                   strlen(hostname));
+
+    return translate_macosxssl_status(osstatus);
+}
+
+static apr_status_t
+serf__macosxssl_use_default_certificates(void *impl_ctx)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    /* When constructing the trust object to validate the server certificate
+       chain, extract all root certificates from the System keychain first
+       to include them during validation. */
+    ssl_ctx->use_system_roots = 1;
+
+    return APR_SUCCESS;
+}
+
+apr_status_t
+load_CA_cert_from_buffer(serf_ssl_certificate_t **cert,
+                         const char *buf,
+                         apr_size_t len,
+                         apr_pool_t *pool)
+{
+    CFArrayRef items;
+    CFDataRef databuf;
+    apr_status_t status;
+
+    databuf = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                          (unsigned char *)buf,
+                                          len,
+                                          kCFAllocatorNull);
+
+    status = load_certificate_from_databuf(databuf, &items, pool);
+    if (status)
+        return status;
+
+    if (CFArrayGetCount(items) > 0) {
+        SecCertificateRef ssl_cert =
+            (SecCertificateRef)CFArrayGetValueAtIndex(items, 0);
+
+        if (ssl_cert) {
+            *cert = create_ssl_certificate(ssl_cert,
+                                           0,
+                                           pool);
+            return APR_SUCCESS;
+        }
+    }
+
+    /* TODO: cleanup databuf needed? */
+
+    return SERF_ERROR_SSL_CERT_FAILED;
+}
+
+static apr_status_t
+serf__macosxssl_load_CA_cert_from_file(serf_ssl_certificate_t **cert,
+                                       const char *file_path,
+                                       apr_pool_t *pool)
+{
+    CFArrayRef items;
+    CFDataRef databuf;
+    apr_status_t status;
+
+    status = load_data_from_file(file_path, &databuf, pool);
+    if (status)
+        return status;
+
+    status = load_certificate_from_databuf(databuf, &items, pool);
+    if (status)
+        return status;
+
+    if (CFArrayGetCount(items) > 0) {
+        SecCertificateRef ssl_cert =
+            (SecCertificateRef)CFArrayGetValueAtIndex(items, 0);
+
+        if (ssl_cert) {
+            *cert = create_ssl_certificate(ssl_cert,
+                                           0,
+                                           pool);
+            return APR_SUCCESS;
+        }
+    }
+
+    return SERF_ERROR_SSL_CERT_FAILED;
+}
+
+static apr_status_t
+serf__macosxssl_load_identity_from_file(void *impl_ctx,
+                                        const serf_ssl_identity_t **identity,
+                                        const char *file_path,
+                                        apr_pool_t *pool)
+{
+    apr_status_t status;
+    CFDataRef databuf;
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+
+    status = load_data_from_file(file_path, &databuf, pool);
+    if (status)
+        return status;
+
+    status = create_temp_keychain(ssl_ctx, ssl_ctx->handshake_pool);
+    if (status)
+        return status;
+
+    status = load_identity_from_databuf(ssl_ctx, identity, file_path,
+                                        databuf, pool);
+
+    return status;
+}
+
+static apr_status_t
+serf__macosxssl_trust_cert(void *impl_ctx,
+                           serf_ssl_certificate_t *cert)
+{
+    macosxssl_context_t *ssl_ctx = impl_ctx;
+    macosxssl_certificate_t *macosxssl_cert = cert->impl_cert;
+
+    if (!ssl_ctx->anchor_certs)
+        ssl_ctx->anchor_certs = apr_array_make(ssl_ctx->pool, 1,
+                                               sizeof(SecCertificateRef));
+    APR_ARRAY_PUSH(ssl_ctx->anchor_certs,
+                   SecCertificateRef) = macosxssl_cert->certref;
+
+    return APR_SUCCESS;
+}
+
+static apr_hash_t *
+serf__macosxssl_cert_certificate(const serf_ssl_certificate_t *cert,
+                                 apr_pool_t *pool)
+{
+    apr_hash_t *tgt;
+    const char *date_str, *sha1;
+
+    macosxssl_certificate_t *macosxssl_cert = cert->impl_cert;
+
+    if (!macosxssl_cert->content) {
+        apr_status_t status;
+        status = serf__macosxssl_read_X509_DER_certificate(
+                           &macosxssl_cert->content,
+                           macosxssl_cert,
+                           pool);
+        if (status)
+            return NULL;
+    }
+
+    tgt = apr_hash_make(pool);
+
+    date_str = apr_hash_get(macosxssl_cert->content, "notBefore",
+                            APR_HASH_KEY_STRING);
+    apr_hash_set(tgt, "notBefore", APR_HASH_KEY_STRING, date_str);
+
+    date_str = apr_hash_get(macosxssl_cert->content, "notAfter",
+                            APR_HASH_KEY_STRING);
+    apr_hash_set(tgt, "notAfter", APR_HASH_KEY_STRING, date_str);
+
+    sha1 = apr_hash_get(macosxssl_cert->content, "sha1", APR_HASH_KEY_STRING);
+    apr_hash_set(tgt, "sha1", APR_HASH_KEY_STRING, sha1);
+
+    /* TODO: array of subjectAltName's */
+
+    return tgt;
+}
+
+
+static apr_hash_t *
+serf__macosxssl_cert_issuer(const serf_ssl_certificate_t *cert,
+                            apr_pool_t *pool)
+{
+    macosxssl_certificate_t *macosxssl_cert = cert->impl_cert;
+
+    if (!macosxssl_cert->content) {
+        apr_status_t status;
+        status = serf__macosxssl_read_X509_DER_certificate(
+                           &macosxssl_cert->content,
+                           macosxssl_cert,
+                           pool);
+        if (status)
+            return NULL;
+    }
+
+    return (apr_hash_t *)apr_hash_get(macosxssl_cert->content,
+                                      "issuer", APR_HASH_KEY_STRING);
+}
+
+static apr_hash_t *
+serf__macosxssl_cert_subject(const serf_ssl_certificate_t *cert,
+                             apr_pool_t *pool)
+{
+    macosxssl_certificate_t *macosxssl_cert = cert->impl_cert;
+
+    if (!macosxssl_cert->content) {
+        apr_status_t status;
+        status = serf__macosxssl_read_X509_DER_certificate(
+                           &macosxssl_cert->content,
+                           macosxssl_cert,
+                           pool);
+        if (status)
+            return NULL;
+    }
+
+    return (apr_hash_t *)apr_hash_get(macosxssl_cert->content,
+                                      "subject", APR_HASH_KEY_STRING);
+}
+
+static const char *
+serf__macosxssl_cert_export(const serf_ssl_certificate_t *cert,
+                            apr_pool_t *pool)
+{
+    macosxssl_certificate_t *macosxssl_cert = cert->impl_cert;
+    SecCertificateRef certref = macosxssl_cert->certref;
+    CFDataRef dataref = SecCertificateCopyData(certref);
+    const unsigned char *data = CFDataGetBytePtr(dataref);
+    char *encoded_cert;
+
+    CFIndex len = CFDataGetLength(dataref);
+
+    if (!len)
+        return NULL;
+
+    encoded_cert = apr_palloc(pool, apr_base64_encode_len(len));
+
+    apr_base64_encode(encoded_cert, (char*)data, len);
+
+    return encoded_cert;
+}
+
+static apr_status_t
+serf__macosxssl_use_compression(void *impl_ctx, int enabled)
+{
+    if (enabled) {
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "Secure Transport does not support any type of "
+                  "SSL compression.\n");
+        return APR_ENOTIMPL;
+    } else {
+        return APR_SUCCESS;
+    }
+}
+
+/**** ENCRYPTION BUCKET API *****/
+/********************************/
+#pragma mark ENCRYPTION BUCKET API
+#pragma mark -
+static apr_status_t
+serf_macosxssl_encrypt_read(serf_bucket_t *bucket,
+                            apr_size_t requested,
+                            const char **data, apr_size_t *len)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+    apr_status_t status, status_unenc_stream;
+    const char *unenc_data;
+    struct iovec vecs[32];
+    int vecs_used;
+    size_t unenc_len;
+
+    serf__log(SSL_VERBOSE, __FILE__, "serf_macosxssl_encrypt_read called for "
+              "%d bytes.\n", requested);
+
+    /* Pending handshake? */
+    status = do_handshake(ssl_ctx);
+    if (SERF_BUCKET_READ_ERROR(status))
+        return status;
+    if (status) {
+        /* Maybe the handshake algorithm put some data in the pending
+         outgoing bucket? */
+        return serf_bucket_read(ssl_ctx->encrypt.pending, requested, data, len);
+    }
+
+    /* Handshake successful. */
+
+    /* First use any pending encrypted data. */
+    status = serf_bucket_read(ssl_ctx->encrypt.pending, requested, data, len);
+    if (SERF_BUCKET_READ_ERROR(status))
+        return status;
+
+    if (*len) {
+        /* status can be either APR_EAGAIN or APR_SUCCESS. In both cases,
+           we want the caller to try again as there's probably more data
+           to be encrypted. */
+        return APR_SUCCESS;
+    }
+
+    /* SSLWrite does not write directly to a socket, but to an aggregate bucket
+       which can be infinitely big. To ensure that the outgoing data is 
+       written to the socket regularely, we only read one block at a time and
+       then return to the caller. */
+    if (requested == SERF_READ_ALL_AVAIL)
+        requested = 65536;
+
+    /* Encrypt more data. */
+    status_unenc_stream = serf_bucket_read_iovec(ssl_ctx->encrypt.stream,
+                                                 requested,
+                                                 32, vecs,
+                                                 &vecs_used);
+    if (SERF_BUCKET_READ_ERROR(status_unenc_stream))
+        return status_unenc_stream;
+
+    unenc_data = serf_bstrcatv(ssl_ctx->encrypt.stream->allocator,
+                               vecs, vecs_used, &unenc_len);
+
+    serf__log(SSL_VERBOSE, __FILE__, "ready to encrypt %d bytes with status %d.\n",
+              unenc_len, status_unenc_stream);
+
+    if (unenc_len)
+    {
+        OSStatus osstatus;
+        size_t written;
+
+        osstatus = SSLWrite(ssl_ctx->st_ctxr, unenc_data, unenc_len,
+                            &written);
+        status = translate_macosxssl_status(osstatus);
+        if (SERF_BUCKET_READ_ERROR(status))
+            return status;
+
+        serf__log(SSL_MSG_VERBOSE, __FILE__, "%dB ready with status %d, %d "
+                  "encrypted and written:\n---%.*s-(%d)-\n", unenc_len,
+                  status_unenc_stream, written, written, unenc_data, written);
+
+        /* Less data written than available! This situation can never happen,
+           because SSLWrite loops until all data is sent or macosxssl_write_cb
+           returns errSSLWouldBlock, and the callback will never return this
+           error because it can process all data that it's given. */
+        if (written < unenc_len)
+        {
+            serf__log(SSL_VERBOSE, __FILE__, "Less data written than was "
+                      "available! Aborting, this should not be possible.\n");
+            return APR_EGENERAL;
+        }
+
+        status = serf_bucket_read(ssl_ctx->encrypt.pending, requested,
+                                  data, len);
+        if (SERF_BUCKET_READ_ERROR(status))
+            return status;
+
+        /* Tell the caller there's more data readily available. */
+        if (status == APR_SUCCESS)
+            return status;
+    }
+
+    /* All encrypted data was returned, if there's more available depends
+       on what's pending on the to-be-encrypted stream. */
+    return status_unenc_stream;
+}
+
+static apr_status_t
+serf_macosxssl_encrypt_readline(serf_bucket_t *bucket,
+                                int acceptable, int *found,
+                                const char **data,
+                                apr_size_t *len)
+{
+    serf__log(SSL_VERBOSE, __FILE__,
+              "function serf_macosxssl_encrypt_readline not implemented.\n");
+    return APR_ENOTIMPL;
+}
+
+
+static apr_status_t
+serf_macosxssl_encrypt_peek(serf_bucket_t *bucket,
+                            const char **data,
+                            apr_size_t *len)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+
+    return serf_bucket_peek(ssl_ctx->encrypt.pending, data, len);
+}
+
+static void
+serf_macosxssl_encrypt_destroy_and_data(serf_bucket_t *bucket)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+
+    if (!--ssl_ctx->refcount) {
+        macosxssl_free_context(ssl_ctx, bucket->allocator);
+    }
+
+    serf_bucket_ssl_destroy_and_data(bucket);
+}
+
+/**** DECRYPTION BUCKET API *****/
+/********************************/
+#pragma mark DECRYPTION BUCKET API
+#pragma mark -
+static apr_status_t
+serf_macosxssl_decrypt_peek(serf_bucket_t *bucket,
+                            const char **data,
+                            apr_size_t *len)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+    
+    return serf_bucket_peek(ssl_ctx->decrypt.pending, data, len);
+}
+
+/* Ask Secure Transport to decrypt some more data. If anything was received,
+   add it to the to decrypt.pending buffer.
+   This function will read and decrypt all available data.
+ */
+static apr_status_t
+decrypt_more_data(macosxssl_context_t *ssl_ctx)
+{
+    /* Decrypt more data. */
+    serf_bucket_t *tmp;
+    char *dec_data;
+    size_t dec_len, available_len;
+    OSStatus osstatus;
+    apr_status_t status;
+
+    serf__log(SSL_VERBOSE, __FILE__,
+              "decrypt_more_data called.\n");
+
+    /* Read until the stream has no more data and until SSLRead decrypted
+       all data - which can take multiple calls! */
+    do {
+        /* We have to provide ST with a sufficiently large buffer for the
+           decrypted data. We can ask S.T. how many bytes are still buffered,
+           but this gives no indication on how many are still waiting to
+           be decrypted. Use it only to increase the bufsize over the
+           default (minimum) size READ_BUFSIZE. */
+        osstatus = SSLGetBufferedReadSize(ssl_ctx->st_ctxr, &available_len);
+        if (osstatus != noErr)
+            return translate_macosxssl_status(osstatus);
+        else {
+            if (available_len < READ_BUFSIZE)
+                available_len = READ_BUFSIZE;
+        }
+        /* TODO: SSLRead doesn't always return a lot of data in one go, so
+           it makes sense to continue receiving data in the remainder of this
+           buffer before allocating a new buffer. */
+        dec_data = serf_bucket_mem_alloc(ssl_ctx->decrypt.pending->allocator,
+                                         available_len);
+
+        osstatus = SSLRead(ssl_ctx->st_ctxr, dec_data,
+                           available_len,
+                           &dec_len);
+        status = translate_macosxssl_status(osstatus);
+
+        /* SSLRead can put data in dec_data while returning an error status. */
+        if (SERF_BUCKET_READ_ERROR(status) && !dec_len)
+            return status;
+
+        /* Successfully received and decrypted some data, add to pending. */
+        serf__log(SSL_MSG_VERBOSE, __FILE__, " received and decrypted data:"
+                  "---\n%.*s\n-(%d)-\n", dec_len, dec_data, dec_len);
+
+        tmp = SERF_BUCKET_SIMPLE_STRING_LEN(dec_data, dec_len,
+                                            ssl_ctx->decrypt.pending->allocator);
+        serf_bucket_aggregate_append(ssl_ctx->decrypt.pending, tmp);
+
+    } while (status == APR_SUCCESS);
+
+    return status;
+}
+
+static apr_status_t
+serf_macosxssl_decrypt_read(serf_bucket_t *bucket,
+                            apr_size_t requested,
+                            const char **data, apr_size_t *len)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+    apr_status_t status;
+
+    serf__log(SSL_VERBOSE, __FILE__,
+              "serf_macosxssl_decrypt_read called for %d bytes.\n", requested);
+
+    /* Pending handshake? */
+    status = do_handshake(ssl_ctx);
+    if (status) {
+        *len = 0;
+        return status;
+    }
+
+    /* First use any pending encrypted data. */
+    status = serf_bucket_read(ssl_ctx->decrypt.pending,
+                              requested, data, len);
+    if (SERF_BUCKET_READ_ERROR(status))
+        return status;
+
+    if (*len)
+        return status;
+
+    /* Pending buffer empty, decrypt more. */
+    status = decrypt_more_data(ssl_ctx);
+    if (SERF_BUCKET_READ_ERROR(status))
+        return status;
+
+    /* We should now have more decrypted data in the pending buffer. */
+    return serf_bucket_read(ssl_ctx->decrypt.pending, requested, data,
+                            len);
+}
+
+static apr_status_t
+serf_macosxssl_decrypt_readline(serf_bucket_t *bucket,
+                                int acceptable, int *found,
+                                const char **data,
+                                apr_size_t *len)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+    apr_status_t status;
+
+    /* Pending handshake? */
+    status = do_handshake(ssl_ctx);
+    if (status) {
+        *len = 0;
+        *found = SERF_NEWLINE_NONE;
+        return status;
+    }
+
+    /* First use any pending encrypted data. */
+    status = serf_bucket_readline(ssl_ctx->decrypt.pending, acceptable, found,
+                                  data, len);
+    if (SERF_BUCKET_READ_ERROR(status))
+        return status;
+
+    if (*len) {
+        /* We read a partial or complete line */
+        return status;
+    }
+
+    /* Pending buffer empty, decrypt more. */
+    status = decrypt_more_data(ssl_ctx);
+    if (SERF_BUCKET_READ_ERROR(status))
+        return status;
+
+    /* We have more decrypted data in the pending buffer. */
+    status = serf_bucket_readline(ssl_ctx->decrypt.pending, acceptable, found,
+                                  data, len);
+
+    return status;
+}
+
+static void
+serf_macosxssl_decrypt_destroy_and_data(serf_bucket_t *bucket)
+{
+    macosxssl_context_t *ssl_ctx = bucket->data;
+
+    if (!--ssl_ctx->refcount) {
+        macosxssl_free_context(ssl_ctx, bucket->allocator);
+    }
+
+    serf_bucket_ssl_destroy_and_data(bucket);
+}
+
+/**** Helper function to fetch info from Keychain ****/
+/*****************************************************/
+#pragma mark KEYCHAIN HELPER FUNCTIONS
+#pragma mark -
+apr_status_t
+serf_macosxssl_show_trust_certificate_panel(serf_ssl_context_t *ctx,
+                                            const char *message,
+                                            const char *ok_button,
+                                            const char *cancel_button)
+{
+#ifdef SERF_HAVE_MACOSXSSL
+    macosxssl_context_t *ssl_ctx = serf__ssl_get_impl_context(ctx);
+    SecTrustRef trust = ssl_ctx->trust;
+    apr_status_t status;
+
+    void *macosxssl_cls = objc_getClass("macosxssl_Buckets");
+    id tmp = objc_msgSend(macosxssl_cls,
+                          sel_getUid("showTrustCertificateDialog:message:"
+                                     "ok_button:cancel_button:"),
+                          trust, message, ok_button, cancel_button);
+    status = (apr_status_t)(SInt64)tmp;
+
+    return status;
+#else
+    return APR_ENOTIMPL;
+#endif
+}
+
+apr_status_t
+serf_macosxssl_show_select_identity_panel(serf_ssl_context_t *ctx,
+                                          const serf_ssl_identity_t **identity,
+                                          const char *message,
+                                          const char *ok_button,
+                                          const char *cancel_button,
+                                          apr_pool_t *pool)
+{
+#ifdef SERF_HAVE_MACOSXSSL
+    SecIdentityRef identityref;
+    OSStatus osstatus;
+
+    void *macosxssl_cls = objc_getClass("MacOSXSSL_Buckets");
+    id tmp = objc_msgSend(macosxssl_cls,
+                          sel_getUid("showSelectIdentityDialog:message:"
+                                     "ok_button:cancel_button:"),
+                          &identityref, message, ok_button, cancel_button);
+    osstatus = (OSStatus)(SInt64)tmp;
+    if (osstatus == errSecItemNotFound)
+    {
+        /* There is no single identity in the keychains. */
+        return SERF_ERROR_SSL_NO_IDENTITIES_AVAILABLE;
+    } else if (osstatus != noErr) {
+        return translate_macosxssl_status(osstatus);
+    }
+
+    if (!identityref)
+        return SERF_ERROR_SSL_CERT_FAILED;
+
+    *identity = serf__create_identity(&serf_ssl_bucket_type_macosxssl,
+                                      identityref, NULL,
+                                      pool);
+    return APR_SUCCESS;
+#else
+    return APR_ENOTIMPL;
+#endif
+}
+
+apr_status_t
+serf_macosxssl_find_preferred_identity_in_keychain(
+    serf_ssl_context_t *ctx,
+    const serf_ssl_identity_t **identity,
+    apr_pool_t *pool)
+{
+#ifdef SERF_HAVE_MACOSXSSL
+    apr_pool_t *tmppool;
+    macosxssl_context_t *ssl_ctx = serf__ssl_get_impl_context(ctx);
+    SecIdentityRef identityref = NULL;
+    CFStringRef labelref;
+    const char *label;
+    apr_status_t status;
+
+    /* We can get the distinguished names from the server with
+       SSLCopyDistinguishedNames to filter matching client certificates,
+       but we can't pass this list to the application, and
+       SecIdentityCopyPreferred doesn't have this feature implemented
+       either. So, don't bother. */
+
+    /* We absolutelely need an item that can sign. Otherwise we will get
+       an incomplete identity object, with which SecIdentityCopyCertificate
+       will crash. */
+    const void *keyUsage[] = { kSecAttrCanSign };
+    CFArrayRef keyUsageRef = CFArrayCreate(kCFAllocatorDefault,
+                                           (void *)keyUsage,
+                                           1,
+                                           NULL);
+
+    apr_pool_create(&tmppool, pool);
+
+    /* Find an identity preference using label https://<hostname> */
+    label = apr_pstrcat(tmppool, "https://", ssl_ctx->hostname, NULL);
+    labelref = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                             (unsigned char *)label,
+                                             strlen(label),
+                                             kCFStringEncodingMacRoman,
+                                             false,
+                                             kCFAllocatorNull);
+    identityref = SecIdentityCopyPreferred(labelref,
+                                           keyUsageRef, NULL);
+    if (identityref)
+    {
+
+        *identity = serf__create_identity(&serf_ssl_bucket_type_macosxssl,
+                                          identityref, NULL, pool);
+        status = APR_SUCCESS;
+    } else {
+        *identity = NULL;
+        status = SERF_ERROR_SSL_CERT_FAILED;
+    }
+
+    CFRelease(labelref);
+    CFRelease(keyUsageRef);
+    apr_pool_destroy(tmppool);
+    
+    return status;
+#else
+    return APR_ENOTIMPL;
+#endif
+}
+
+/*****************************************************************************/
+const serf_bucket_type_t serf_bucket_type_macosxssl_encrypt = {
+    "MACOSXSSLENCRYPT",
+    serf_macosxssl_encrypt_read,
+    serf_macosxssl_encrypt_readline,
+    serf_default_read_iovec,
+    serf_default_read_for_sendfile,
+    serf_default_read_bucket,
+    serf_macosxssl_encrypt_peek,
+    serf_macosxssl_encrypt_destroy_and_data,
+};
+
+const serf_bucket_type_t serf_bucket_type_macosxssl_decrypt = {
+    "MACOSXSSLDECRYPT",
+    serf_macosxssl_decrypt_read,
+    serf_macosxssl_decrypt_readline,
+    serf_default_read_iovec,
+    serf_default_read_for_sendfile,
+    serf_default_read_bucket,
+    serf_macosxssl_decrypt_peek,
+    serf_macosxssl_decrypt_destroy_and_data,
+};
+
+const serf_ssl_bucket_type_t serf_ssl_bucket_type_macosxssl = {
+    serf__macosxssl_decrypt_create,
+    serf__macosxssl_decrypt_context_get,
+    serf__macosxssl_encrypt_create,
+    serf__macosxssl_encrypt_context_get,
+    serf__macosxssl_set_hostname,
+    serf__macosxssl_client_cert_provider_set,
+    serf__macosxssl_identity_provider_set,
+    serf__macosxssl_client_cert_password_set,
+    serf__macosxssl_server_cert_callback_set,
+    serf__macosxssl_server_cert_chain_callback_set,
+    serf__macosxssl_use_default_certificates,
+    serf__macosxssl_load_CA_cert_from_file,
+    serf__macosxssl_load_identity_from_file,
+    serf__macosxssl_trust_cert,
+    serf__macosxssl_cert_issuer,
+    serf__macosxssl_cert_subject,
+    serf__macosxssl_cert_certificate,
+    serf__macosxssl_cert_export,
+    serf__macosxssl_use_compression,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+
+#else
+
+const serf_bucket_type_t serf_bucket_type_macosxssl_encrypt = {
+    "MACOSXSSLENCRYPT",
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+const serf_bucket_type_t serf_bucket_type_macosxssl_decrypt = {
+    "MACOSXSSLDECRYPT",
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+#endif /* SERF_HAVE_MACOSXSSL */
diff --git a/buckets/macosxssl_helper.m b/buckets/macosxssl_helper.m
new file mode 100644
index 0000000..9ece1e8
--- /dev/null
+++ b/buckets/macosxssl_helper.m
@@ -0,0 +1,170 @@
+/* Copyright 2013 Justin Erenkrantz and Greg Stein
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+#import <SecurityInterface/SFCertificateTrustPanel.h>
+#import <SecurityInterface/SFChooseIdentityPanel.h>
+
+#import "serf.h"
+
+@interface MacOSXSSL_Buckets : NSObject
+{
+}
++ (OSStatus) evaluate:(SecTrustRef)trust
+          trustResult:(SecTrustResultType *)trustResult;
+
++ (apr_status_t) showTrustCertificateDialog:(SecTrustRef)trust
+                                    message:(const char *)message
+                                  ok_button:(const char *)ok_button
+                              cancel_button:(const char *)cancel_button;
+@end
+
+@implementation MacOSXSSL_Buckets
+
+/* Evaluate the trust object asynchronously. When the results are received,
+   store them in the provided resultPtr address. */
++ (OSStatus) evaluate:(SecTrustRef)trust
+          trustResult:(SecTrustResultType *)resultPtr
+{
+    dispatch_queue_t queue;
+    OSStatus osstatus;
+
+    SecTrustCallback block = ^(SecTrustRef trust, SecTrustResultType trustResult)
+    {
+        *resultPtr = trustResult;
+    };
+
+    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0l);
+    osstatus = SecTrustEvaluateAsync(trust, queue, block);
+
+    return osstatus;
+}
+
++ (apr_status_t) showTrustCertificateDialog:(SecTrustRef)trust
+                                    message:(const char *)message
+                                  ok_button:(const char *)ok_button
+                              cancel_button:(const char *)cancel_button
+{
+    NSString *MessageLbl = [[NSString alloc] initWithUTF8String:message];
+    NSString *OkButtonLbl = [[NSString alloc] initWithUTF8String:ok_button];
+    NSString *CancelButtonLbl;
+    NSApplication *app;
+    SFCertificateTrustPanel *panel;
+    NSInteger result;
+
+    if (cancel_button)
+        CancelButtonLbl = [[NSString alloc] initWithUTF8String:cancel_button];
+
+    app = [NSApplication sharedApplication];
+    panel = [SFCertificateTrustPanel sharedCertificateTrustPanel];
+
+    /* Put the dialog in front of the application, and give it the focus. */
+    [app setActivationPolicy:NSApplicationActivationPolicyRegular];
+    [app activateIgnoringOtherApps:YES];
+
+    [panel setShowsHelp:YES];
+    [panel setDefaultButtonTitle:OkButtonLbl];
+    if (cancel_button)
+        [panel setAlternateButtonTitle:CancelButtonLbl];
+
+    result = [panel runModalForTrust:trust
+                             message:MessageLbl];
+
+    [panel release];
+    [MessageLbl release];
+    [OkButtonLbl release];
+    if (cancel_button)
+        [CancelButtonLbl release];
+
+    if (result)
+        return APR_SUCCESS;
+    else
+        return SERF_ERROR_SSL_USER_DENIED_CERT;
+}
+
++ (OSStatus) showSelectIdentityDialog:(SecIdentityRef *)identity
+                                  message:(const char *)message
+                                ok_button:(const char *)ok_button
+                            cancel_button:(const char *)cancel_button
+{
+    NSString *MessageLbl = [[NSString alloc] initWithUTF8String:message];
+    NSString *OkButtonLbl = [[NSString alloc] initWithUTF8String:ok_button];
+    NSString *CancelButtonLbl;
+    NSApplication *app;
+    SFChooseIdentityPanel *panel;
+    NSArray *identities;
+    NSDictionary *query;
+    NSInteger result;
+    OSStatus osstatus;
+
+    if (cancel_button)
+        CancelButtonLbl = [[NSString alloc] initWithUTF8String:cancel_button];
+
+    /* Find all matching identities in the keychains.
+       Note: SecIdentityRef items are not stored on the keychain but
+       generated when needed if both a certificate and matching private
+       key are available on the keychain. */
+    query = [NSDictionary dictionaryWithObjectsAndKeys:
+             (id)kSecClassIdentity, (id)kSecClass,
+             (id)kCFBooleanTrue, (id)kSecAttrCanSign,
+             (id)kSecMatchLimitAll, (id)kSecMatchLimit,
+             (id)kCFBooleanTrue, (id)kSecReturnRef,
+             nil];
+
+    osstatus = SecItemCopyMatching((CFDictionaryRef)query,
+                                   (CFTypeRef *)&identities);
+    [query release];
+
+    if (osstatus != noErr)
+        return osstatus;
+
+    /* TODO: filter on matching certificates. How?? Distinguished names? */
+
+    /* Found the identities, now let the user choose. */
+    app = [NSApplication sharedApplication];
+
+    panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
+
+    /* Put the dialog in front of the application, and give it the focus. */
+    [app setActivationPolicy:NSApplicationActivationPolicyRegular];
+    [app activateIgnoringOtherApps:YES];
+
+    [panel setShowsHelp:YES];
+    [panel setDefaultButtonTitle:OkButtonLbl];
+    if (cancel_button)
+        [panel setAlternateButtonTitle:CancelButtonLbl];
+    
+    result = [panel runModalForIdentities:identities
+                                  message:MessageLbl];
+
+    if (result) {
+        *identity = [panel identity];
+    }
+    else {
+        *identity = nil;
+    }
+
+    [identities autorelease];
+    [MessageLbl release];
+    [OkButtonLbl release];
+    if (cancel_button)
+        [CancelButtonLbl release];
+    [panel release];
+
+    return noErr;
+}
+
+@end
diff --git a/buckets/macosxssl_x509_cert.c b/buckets/macosxssl_x509_cert.c
new file mode 100644
index 0000000..c081146
--- /dev/null
+++ b/buckets/macosxssl_x509_cert.c
@@ -0,0 +1,621 @@
+/* Copyright 2013 Justin Erenkrantz and Greg Stein
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef SERF_HAVE_MACOSXSSL
+
+#include <stdlib.h>
+
+#include "serf.h"
+#include "serf_private.h"
+#include "serf_bucket_util.h"
+#include "bucket_private.h"
+
+#include <apr_strings.h>
+#include <apr_sha1.h>
+
+#include <Security/SecCertificate.h>
+#include <Security/SecCertificateOIDs.h>
+
+#define ST_DEBUG 0
+
+/* This code reads all items DER-encoded certificate in X.509 format, as
+   provided by the Keychain services API. */
+
+/* Read ASN.1 data type OID from a DER encoded buffer. */
+static const char *
+read_DER_OID(const unsigned char* ptr, int clen, apr_pool_t *pool)
+{
+    unsigned char b;
+    char *value;
+
+    /* First two components of the OID are encoded in the first byte. */
+    b = *ptr++;
+    clen--;
+
+    value = apr_psprintf(pool, "%d.%d", b/40, b%40);
+
+
+    /* one or two bytes per component */
+    while (clen > 0) {
+        b = *ptr++;
+        clen--;
+
+        if (! (b & 0x80)) {
+            value = apr_psprintf(pool, "%s.%d", value, b);
+        } else {
+            unsigned comp = 0;
+
+            while (b & 0x80 && clen > 0) {
+                comp <<= 7;
+                comp |= (b & 0x7f);
+                b = *ptr++;
+                clen--;
+            }
+            comp <<= 7;
+            comp |= (b & 0x7f);
+
+            value = apr_psprintf(pool, "%s.%d", value, comp);
+        }
+    }
+
+    serf__log(ST_DEBUG, __FILE__, "OID of length %d, value: %s .\n",
+              clen, value);
+
+    return value;
+}
+
+/* Read ASN.1 data type PrintableString from a DER encoded buffer.
+   TODO: currently also used for UTF8String, use specific function for those. */
+static const char *
+read_DER_string(const unsigned char* ptr, int clen, apr_pool_t *pool)
+{
+    const char *value = apr_pstrndup(pool, (const char*)ptr, clen);
+
+    serf__log(ST_DEBUG, __FILE__, "string of length %d, value: %s.\n",
+              clen, value);
+
+    return value;
+}
+
+/* Read ASN.1 data type BOOLEAN from a DER encoded buffer. */
+static const char *
+read_DER_boolean(const unsigned char* ptr, int clen, apr_pool_t *pool)
+{
+    unsigned char v = *ptr++;
+
+    serf__log(ST_DEBUG, __FILE__, "Boolean of length %d, value: %s.\n",
+              clen, v ? "TRUE" : "FALSE");
+
+    return v ? "TRUE" : "FALSE";
+}
+
+/* Read ASN.1 data type BIT STRING from a DER encoded buffer. */
+static const char *
+read_DER_bitstring(const unsigned char* ptr, int clen, apr_pool_t *pool)
+{
+    char *value = "";
+
+    /* TODO: take into account unused_bits. */
+    /*    unsigned char unused_bits = *ptr++; */
+    ptr++;
+    clen--;
+
+    while (clen-- > 0) {
+        unsigned char b = *ptr++;
+        value = apr_psprintf(pool, "%s %2x", value, b);
+    }
+
+    serf__log(ST_DEBUG, __FILE__, "Bitstring of length %d, value: %s.\n",
+              clen, value);
+
+    return value;
+}
+
+/* Read ASN.1 data type INTEGER from a DER encoded buffer. */
+static const char *
+read_DER_integer(const unsigned char* ptr, int clen, apr_pool_t *pool)
+{
+    unsigned char lb = *ptr++;
+    int positive = 0;
+    int i;
+    unsigned long value = 0;
+
+    if (lb == 0x00) {
+        positive = 1;
+        clen -= positive;
+
+
+        if (clen <= sizeof(long))
+            for (i = 0; i < clen; i++)
+                value = (value << 8) + *ptr++;
+    } else if (! (lb & 0x80)) {
+        positive = 1;
+        value = lb;
+    } else {
+        /* negative number */
+        return apr_psprintf(pool, "Negative integer not supported.");
+    }
+
+    serf__log(ST_DEBUG, __FILE__, "%s integer of length %d, value: %lx.\n",
+              positive ? "Positive" : "Negative", clen, value);
+
+    return apr_psprintf(pool, "%lx", value);
+}
+
+/* Read DER Tag and Length from a DER encoded buffer. */
+static apr_status_t
+read_DER_TL(const unsigned char* ptr, unsigned char *tag,
+            long *value_len, long *consumed)
+{
+    int constr_enc;
+    char lb1;
+    long clen = 0;
+
+    /* read tag */
+    *tag = *ptr++;
+    constr_enc = *tag & 0x20;
+    *consumed = 1;
+
+    /* read length */
+    lb1 = *ptr++;
+    if (! (lb1 & 0x80)) {
+        clen = lb1;
+        (*consumed)++;
+    } else {
+        int i;
+        lb1 &= 0x7f;
+        if (lb1 > sizeof(long))
+            return APR_ENOTIMPL;
+        for (i = 0; i < lb1; i++)
+            clen = (clen << 8) + *ptr++;
+        *consumed += (lb1 + 1);
+    }
+
+    *value_len = clen;
+
+    serf__log(ST_DEBUG, __FILE__,
+              "tag: %x, value length: %d, header length: %d.\n",
+              *tag, *value_len, *consumed);
+
+    return APR_SUCCESS;
+}
+
+/* Read DER Tag, Length and primitive Value from a DER encoded buffer.
+   Don't use this for constructed types SEQUENCE or SET! */
+static apr_status_t
+read_DER_TLvalue(const unsigned char *ptr, unsigned char *tag, long *consumed,
+                 const char **value, apr_pool_t *pool)
+{
+    long tl_len, value_len;
+
+    read_DER_TL(ptr, tag, &value_len, &tl_len);
+    ptr += tl_len;
+
+    switch (*tag) {
+        case 0x01:
+            *value = read_DER_boolean(ptr, value_len, pool);
+            break;
+        case 0x02:
+            *value = read_DER_integer(ptr, value_len, pool);
+            break;
+        case 0x03:
+            *value = read_DER_bitstring(ptr, value_len, pool);
+            break;
+        case 0x05:
+            *value = "";
+            serf__log(ST_DEBUG, __FILE__, "NULL value.\n");
+            break;
+        case 0x06:
+            *value = read_DER_OID(ptr, value_len, pool);
+            break;
+        case 0x17: /* Date */
+        case 0x16: /* IA5String */
+        case 0x13: /* PrintableString */
+        case 0x14: /* TeletexString */
+        case 0x0c: /* UTF8String */
+            *value = read_DER_string(ptr, value_len, pool);
+            break;
+
+        case 0xa0: /* Explicit tag 0, in X509 used for version. */
+        default:
+        {
+            *value = "ERROR";
+            serf__log(ST_DEBUG, __FILE__, "UNSUPPORTED TAG TYPE %2x.\n", *tag);
+            return APR_ENOTIMPL;
+            break;
+        }
+    }
+
+    *consumed = tl_len + value_len;
+
+    return APR_SUCCESS;
+}
+
+/* Recursively skip an entire Tag-Length-Value block. */
+static apr_status_t
+skip_DER_TLV(const unsigned char *ptr, unsigned char *tag, long *consumed)
+{
+    long tl_len, value_len;
+    apr_status_t status;
+
+    status = read_DER_TL(ptr, tag, &value_len, &tl_len);
+    if (status)
+        return status;
+
+    *consumed = tl_len + value_len;
+
+    return APR_SUCCESS;
+}
+
+#define SERF_ERR(x) status = (x);\
+                    if (status) goto cleanup;
+
+/* Reads an issuer or subject structure from PTR, which should point to the
+   value of tag type 0x30 grouping either issuer or subject.
+   Caller should clean up out_der. */
+static apr_status_t
+read_X509_DER_DistinguishedName(apr_hash_t **o, CFDataRef *out_der,
+                                const unsigned char *ptr,
+                                long *total_len, apr_pool_t *pool)
+{
+    unsigned char tag;
+    long len, object_len, consumed;
+    apr_status_t status;
+    apr_hash_t *tgt;
+    char *tmp;
+
+    tgt = apr_hash_make(pool);
+
+    /* RelativeDistinguishedName Sequence. */
+    SERF_ERR(read_DER_TL(ptr, &tag, &object_len, &consumed));
+
+    /* Copy this whole structure in out_der. */
+    tmp = apr_palloc(pool, object_len);
+    memcpy(tmp, ptr, object_len);
+    *out_der = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                           (unsigned char *)tmp,
+                                           object_len,
+                                           kCFAllocatorNull);
+
+    ptr += consumed;
+
+    *total_len = object_len + consumed;
+    /* For each component */
+    while (object_len > 0) {
+        const char *key, *value;
+        long consumed;
+        CFStringRef keyref;
+
+        /* RelativeDistinguishedName Set. */
+        SERF_ERR(read_DER_TL(ptr, &tag, &len, &consumed));
+        ptr += consumed; object_len -= consumed;
+
+        /* AttributeTypeAndValue, containing OID-value pair. */
+        SERF_ERR(read_DER_TL(ptr, &tag, &len, &consumed));
+        ptr += consumed; object_len -= consumed;
+
+        /* Read key OID */
+        SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &key, pool));
+        ptr += consumed; object_len -= consumed;
+
+        /* OID constants are CFStringRef, so need to use CFStringCompare. */
+        keyref = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
+                                               (unsigned char *)key,
+                                               strlen(key),
+                                               kCFStringEncodingMacRoman,
+                                               false,
+                                               kCFAllocatorNull);
+        
+        if (CFStringCompare(keyref,
+                kSecOIDOrganizationalUnitName, 0) == kCFCompareEqualTo)
+            key = "OU";
+        else if (CFStringCompare(keyref,
+                kSecOIDOrganizationName, 0) == kCFCompareEqualTo)
+            key = "O";
+        else if (CFStringCompare(keyref,
+                kSecOIDLocalityName, 0) == kCFCompareEqualTo)
+            key = "L";
+        else if (CFStringCompare(keyref,
+                kSecOIDStateProvinceName, 0) == kCFCompareEqualTo)
+            key = "ST";
+        else if (CFStringCompare(keyref,
+                kSecOIDCountryName, 0) == kCFCompareEqualTo)
+            key = "C";
+        else if (CFStringCompare(keyref,
+                kSecOIDEmailAddress, 0) == kCFCompareEqualTo)
+            key = "E";
+        else if (CFStringCompare(keyref,
+                kSecOIDCommonName, 0) == kCFCompareEqualTo)
+            key = "CN";
+
+        CFRelease(keyref);
+
+        /* Read value */
+        SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &value, pool));
+        ptr += consumed; object_len -= consumed;
+
+        serf__log(ST_DEBUG, __FILE__, "Remaining: %d.\n", object_len);
+
+        apr_hash_set(tgt, key, APR_HASH_KEY_STRING, value);
+    }
+
+    *o = tgt;
+    return APR_SUCCESS;
+
+cleanup:
+    return status;
+}
+
+/* Reads date/time string formatted as "YYMMDDHHmmSSZ" and converts it to
+   "MMM dd HH:mm:ss yyyy GMT". Allocate output string from pool.
+   Example: in: 130821204210Z out:Aug 13 20:42:10 2013 GMT . */
+static const char *
+convert_cert_date(const char *in, apr_pool_t *pool)
+{
+    apr_size_t len;
+    apr_time_exp_t te;
+    const int bufsize = 64;
+    char *datestr;
+
+    memset(&te, 0, sizeof(te));
+
+    datestr = apr_palloc(pool, bufsize);
+
+    /* Read 2000+yy into 1900+100+yy */
+    te.tm_year = 100 + (*in -'0') * 10 + *(in+1) - '0'; in+=2;
+    te.tm_mon  = (*in -'0') * 10 + *(in+1) - '0' - 1; in+=2;
+    te.tm_mday = (*in -'0') * 10 + *(in+1) - '0'; in+=2;
+    te.tm_hour = (*in -'0') * 10 + *(in+1) - '0'; in+=2;
+    te.tm_min  = (*in -'0') * 10 + *(in+1) - '0'; in+=2;
+    te.tm_sec  = (*in -'0') * 10 + *(in+1) - '0'; in+=2;
+
+    apr_strftime(datestr, &len, bufsize, "%b %d %T %Y GMT", &te);
+
+    return datestr;
+}
+
+/* Calculate the sha1 hash value of data of length len. Resulting string will be
+   allocated from pool and encoded as hex bytes : separated (A1:81:3F ... ). */
+static const unsigned char *
+sha1digest(const unsigned char *data, long len, apr_pool_t *pool)
+{
+    apr_sha1_ctx_t context;
+    unsigned char *sha1 = apr_pcalloc(pool, APR_SHA1_DIGESTSIZE);
+    unsigned char *sha1hex = apr_pcalloc(pool, APR_SHA1_DIGESTSIZE * 3);
+    unsigned char *inptr = sha1, *outptr = sha1hex;
+    const char al[] = "0123456789ABCDEF";
+    int i;
+
+    apr_sha1_init(&context);
+    apr_sha1_update_binary(&context, data, len);
+    apr_sha1_final(sha1, &context);
+
+    /* concert to :-separated hex bytes */
+    for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
+        unsigned char c = *inptr++;
+
+        *outptr++ = al[(c & 0xf0) >> 4];
+        *outptr++ = al[c & 0x0f];
+        *outptr++ = ':';
+    }
+    *(outptr-1) = '\0';
+    
+    return sha1hex;
+}
+
+static apr_status_t dataref_cleanup(void *data)
+{
+    CFDataRef derdata = data;
+
+    CFRelease(derdata);
+
+    return APR_SUCCESS;
+}
+
+/* Read a Distinquished Name from a DER-encoded DN in X.509 format.
+   The resulting hash table will have following keys:
+   - CN, O, OU, L, ST, C, E.
+   Internal use only:
+   - _der
+ */
+
+apr_status_t
+serf__macosxssl_read_X509_DER_DN(apr_hash_t **o, CFDataRef dndata,
+                                 apr_pool_t *pool)
+{
+    CFDataRef dnder;
+    apr_hash_t *dn;
+    long consumed;
+    const unsigned char *data = CFDataGetBytePtr(dndata);
+    apr_status_t status;
+
+    SERF_ERR(read_X509_DER_DistinguishedName(&dn, &dnder,
+                                             data, &consumed, pool));
+
+    apr_hash_set(dn, "_der", APR_HASH_KEY_STRING, dnder);
+
+    *o = dn;
+
+cleanup:
+    return status;
+}
+
+/* Read all interesting data from a DER-encoded certificate in X.509 format.
+   The resulting hash table will have following keys:
+   - sha1
+   - serial_number (TODO: not used, can be removed if that remains so. )
+   - issuer: hash table with keys CN, O, OU, L, ST, C, E.
+   - subject: hash table with keys CN, O, OU, L, ST, C, E. 
+   - notBefore
+   - notAfter
+   Internal use only:
+   - _issuer_der
+   - _subject_der
+ */
+apr_status_t
+serf__macosxssl_read_X509_DER_certificate(apr_hash_t **o,
+                                          const macosxssl_certificate_t *cert,
+                                          apr_pool_t *pool)
+{
+    apr_hash_t *x509_cert, *issuer, *subject;
+    CFDataRef issuer_der, subject_der;
+    long consumed, x509_len, value_len, signature_start;
+    unsigned char tag;
+    apr_status_t status;
+    const char *serial, *tmp, *key, *value;
+    const unsigned char *ptr, *sha1;
+    int version;
+
+    SecCertificateRef certref = cert->certref;
+    CFDataRef dataref = SecCertificateCopyData(certref);
+    const unsigned char *data = CFDataGetBytePtr(dataref);
+    CFIndex totlen = CFDataGetLength(dataref);
+
+    if (!totlen)
+        return SERF_ERROR_SSL_CERT_FAILED;
+
+    x509_cert = apr_hash_make(pool);
+
+    /* SHA1 fingerprint of the full DER encoded cert. */
+    sha1 = sha1digest(data, totlen, pool);
+    apr_hash_set(x509_cert, "sha1", APR_HASH_KEY_STRING, sha1);
+    serf__log(ST_DEBUG, __FILE__, "SHA1 fingerprint:%s.\n", sha1);
+
+    ptr = data;
+    /* 4.1.1  Certificate sequence */
+    SERF_ERR(read_DER_TL(ptr, &tag, &x509_len, &consumed));
+    ptr += consumed;
+    signature_start = consumed;
+
+    serf__log(ST_DEBUG, __FILE__, "Parsing DER encoding of cert length: %d.\n",
+              x509_len + consumed);
+
+    /* 4.1.2  TBSCertificate (required) */
+    serf__log(ST_DEBUG, __FILE__, "---- TBSCertificate ----.\n");
+    SERF_ERR(read_DER_TL(ptr, &tag, &value_len, &consumed));
+    ptr += consumed; x509_len -= consumed;
+    signature_start += (value_len + consumed);
+
+    /* 4.1.2.1  Version (optional, default v1 (0x00)) */
+    serf__log(ST_DEBUG, __FILE__, "---- Version ----.\n");
+    SERF_ERR(read_DER_TL(ptr, &tag, &value_len, &consumed));
+    if (tag == 0xa0) {
+        ptr += consumed; x509_len -= consumed;
+        SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &tmp, pool));
+        version = atoi(tmp) + 1;
+        ptr += consumed; x509_len -= consumed;
+    } else {
+        /* this was another tag than expected. Means that version wasn't set,
+           used the default v1. */
+        version = 1;
+    }
+
+    /* 4.1.2.2  Serial number */
+    serf__log(ST_DEBUG, __FILE__, "---- Serial Number ----.\n");
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &serial, pool));
+    ptr += consumed; x509_len -= consumed;
+    apr_hash_set(x509_cert, "serial_number", APR_HASH_KEY_STRING, serial);
+
+    /* 4.1.2.3  Signature (it's actually the Algorithm used to sign). */
+    serf__log(ST_DEBUG, __FILE__, "---- Algorithm ----.\n");
+    skip_DER_TLV(ptr, &tag, &consumed);
+    ptr += consumed; x509_len -= consumed;
+
+    /* 4.1.2.4  Issuer */
+    serf__log(ST_DEBUG, __FILE__, "---- Issuer ----.\n");
+    SERF_ERR(read_X509_DER_DistinguishedName(&issuer, &issuer_der,
+                                             ptr, &consumed, pool));
+    ptr += consumed; x509_len -= consumed;
+    apr_hash_set(x509_cert, "issuer", APR_HASH_KEY_STRING, issuer);
+    /* store the original der data buffer of the issuer, for internal use
+       (comparison of certificates). */
+    apr_hash_set(x509_cert, "_issuer_der", APR_HASH_KEY_STRING, issuer_der);
+
+    /* 4.1.2.5  Validity */
+    serf__log(ST_DEBUG, __FILE__, "---- Validity ----.\n");
+    SERF_ERR(read_DER_TL(ptr, &tag, &value_len, &consumed));
+    ptr += consumed; x509_len -= consumed;
+
+    /*          notBefore */
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &tmp, pool));
+    ptr += consumed; x509_len -= consumed;
+    apr_hash_set(x509_cert, "notBefore", APR_HASH_KEY_STRING,
+                 convert_cert_date(tmp, pool));
+
+    /*          notAfter */
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &tmp, pool));
+    ptr += consumed; x509_len -= consumed;
+    apr_hash_set(x509_cert, "notAfter", APR_HASH_KEY_STRING,
+                 convert_cert_date(tmp, pool));
+
+    /* 4.1.2.6  Subject */
+    serf__log(ST_DEBUG, __FILE__, "---- Subject ----.\n");
+    SERF_ERR(read_X509_DER_DistinguishedName(&subject, &subject_der,
+                                             ptr, &consumed, pool));
+    ptr += consumed; x509_len -= consumed;
+    apr_hash_set(x509_cert, "subject", APR_HASH_KEY_STRING, subject);
+    /* store the original der data buffer of the subject, for internal use
+       (comparison of certificates). */
+    apr_hash_set(x509_cert, "_subject_der", APR_HASH_KEY_STRING, subject_der);
+
+    /* 4.1.2.7  Subject Public Key Info */
+    serf__log(ST_DEBUG, __FILE__, "---- Subject Public Key Info ----.\n");
+    SERF_ERR(read_DER_TL(ptr, &tag, &value_len, &consumed));
+    ptr += consumed; x509_len -= consumed;
+    /*          AlgorithmIdentifier */
+    SERF_ERR(read_DER_TL(ptr, &tag, &value_len, &consumed));
+    ptr += consumed; x509_len -= consumed;
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &key, pool));
+    ptr += consumed; x509_len -= consumed;
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &value, pool));
+    ptr += consumed; x509_len -= consumed;
+
+    /* TODO: read subjectAltName's from extensions. */
+
+    /* Skip the remainder of TBSCertificate*/
+    ptr = data + signature_start;
+
+#if 0 /* Not needed, cleanup if this remains so. */
+    /* 4.1.1.2  signatureAlgorithm (required) */
+    SERF_ERR(read_DER_TL(ptr, &tag, &value_len, &consumed));
+    ptr += consumed; x509_len -= consumed;
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &key, pool));
+    ptr += consumed; x509_len -= consumed;
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &value, pool));
+    ptr += consumed; x509_len -= consumed;
+
+    /* 4.1.1.3  signatureValue (required) */
+    SERF_ERR(read_DER_TLvalue(ptr, &tag, &consumed, &value, pool));
+    ptr += consumed; x509_len -= consumed;
+
+    serf__log(ST_DEBUG, __FILE__, "Remaining to read: %d.\n", x509_len);
+#endif
+
+    *o = x509_cert;
+
+    apr_pool_cleanup_register(pool, issuer_der, dataref_cleanup,
+                              dataref_cleanup);
+    apr_pool_cleanup_register(pool, subject_der, dataref_cleanup,
+                              dataref_cleanup);
+
+    return APR_SUCCESS;
+
+cleanup:
+    CFRelease(dataref);
+
+    return status;
+}
+
+#endif /* SERF_HAVE_MACOSXSSL */
diff --git a/buckets/openssl_buckets.c b/buckets/openssl_buckets.c
new file mode 100644
index 0000000..6c7d7a0
--- /dev/null
+++ b/buckets/openssl_buckets.c
@@ -0,0 +1,2069 @@
+/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----
+ *
+ * For the OpenSSL thread-safety locking code:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Originally developed by Aaron Bannert and Justin Erenkrantz, eBuilt.
+ */
+
+#include <apr_pools.h>
+#include <apr_network_io.h>
+#include <apr_portable.h>
+#include <apr_strings.h>
+#include <apr_base64.h>
+#include <apr_version.h>
+#include <apr_atomic.h>
+
+#include "serf.h"
+#include "serf_private.h"
+#include "serf_bucket_util.h"
+#include "bucket_private.h"
+
+#ifdef SERF_HAVE_OPENSSL
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+
+#ifndef APR_VERSION_AT_LEAST /* Introduced in APR 1.3.0 */
+#define APR_VERSION_AT_LEAST(major,minor,patch)                           \
+    (((major) < APR_MAJOR_VERSION)                                        \
+      || ((major) == APR_MAJOR_VERSION && (minor) < APR_MINOR_VERSION)    \
+      || ((major) == APR_MAJOR_VERSION && (minor) == APR_MINOR_VERSION && \
+               (patch) <= APR_PATCH_VERSION))
+#endif /* APR_VERSION_AT_LEAST */
+
+#ifndef APR_ARRAY_PUSH
+#define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary)))
+#endif
+
+
+/*
+ * Here's an overview of the SSL bucket's relationship to OpenSSL and serf.
+ *
+ * HTTP request:  OPENSSLENCRYPT(REQUEST)
+ *   [context.c reads from OPENSSLENCRYPT and writes out to the socket]
+ * HTTP response: RESPONSE(OPENSSLDECRYPT(SOCKET))
+ *   [handler function reads from RESPONSE which in turn reads from
+ *    OPENSSLDECRYPT]
+ *
+ * HTTP request read call path:
+ *
+ * write_to_connection
+ *  |- serf_bucket_read on OPENSSLENCRYPT
+ *    |- serf_openssl_read
+ *      |- serf_databuf_read
+ *        |- common_databuf_prep
+ *          |- ssl_encrypt
+ *            |- 1. Try to read pending encrypted data; If available, return.
+ *            |- 2. Try to read from ctx->stream [REQUEST bucket]
+ *            |- 3. Call SSL_write with read data
+ *              |- ...
+ *                |- bio_bucket_read can be called
+ *                |- bio_bucket_write with encrypted data
+ *                  |- store in sink
+ *            |- 4. If successful, read pending encrypted data and return.
+ *            |- 5. If fails, place read data back in ctx->stream
+ *
+ * HTTP response read call path:
+ *
+ * read_from_connection
+ *  |- acceptor
+ *  |- handler
+ *    |- ...
+ *      |- serf_bucket_read on OPENSSLDECRYPT
+ *        |- serf_openssl_read
+ *          |- serf_databuf_read
+ *            |- ssl_decrypt
+ *              |- 1. SSL_read() for pending decrypted data; if any, return.
+ *              |- 2. Try to read from ctx->stream [SOCKET bucket]
+ *              |- 3. Append data to ssl_ctx->source
+ *              |- 4. Call SSL_read()
+ *                |- ...
+ *                  |- bio_bucket_write can be called
+ *                  |- bio_bucket_read
+ *                    |- read data from ssl_ctx->source
+ *              |- If data read, return it.
+ *              |- If an error, set the STATUS value and return.
+ *
+ */
+
+typedef struct bucket_list {
+    serf_bucket_t *bucket;
+    struct bucket_list *next;
+} bucket_list_t;
+
+typedef struct {
+    /* Helper to read data. Wraps stream. */
+    serf_databuf_t databuf;
+
+    /* Our source for more data. */
+    serf_bucket_t *stream;
+
+    /* The next set of buckets */
+    bucket_list_t *stream_next;
+
+    /* The status of the last thing we read. */
+    apr_status_t status;
+    apr_status_t exhausted;
+    int exhausted_reset;
+
+    /* Data we've read but not processed. */
+    serf_bucket_t *pending;
+} serf_ssl_stream_t;
+
+typedef struct openssl_context_t {
+    /* How many open buckets refer to this context. */
+    int refcount;
+
+    /* The pool that this context uses. */
+    apr_pool_t *pool;
+
+    /* The allocator associated with the above pool. */
+    serf_bucket_alloc_t *allocator;
+
+    /* Internal OpenSSL parameters */
+    SSL_CTX *ctx;
+    SSL *ssl;
+    BIO *bio;
+
+    serf_ssl_stream_t encrypt;
+    serf_ssl_stream_t decrypt;
+
+    /* Identity callbacks */
+    serf_ssl_need_client_cert_t cert_callback;    /* Deprecated */
+    serf_ssl_need_identity_t identity_callback;
+    void *cert_userdata;
+    apr_pool_t *cert_cache_pool;
+    const char *cert_file_success;
+
+    /* Client cert PW callbacks */
+    serf_ssl_need_cert_password_t cert_pw_callback;
+    void *cert_pw_userdata;
+    apr_pool_t *cert_pw_cache_pool;
+    const char *cert_pw_success;
+
+    /* Server cert callbacks */
+    serf_ssl_need_server_cert_t server_cert_callback;
+    serf_ssl_server_cert_chain_cb_t server_cert_chain_callback;
+    void *server_cert_userdata;
+
+    serf_ssl_new_session_t new_session_cb;
+    void *new_session_cb_baton;
+
+    const serf_ssl_identity_t *cached_identity;
+
+    apr_status_t pending_err;
+
+    /* Status of a fatal error, returned on subsequent encrypt or decrypt
+       requests. */
+    apr_status_t fatal_err;
+} openssl_context_t;
+
+typedef struct {
+    /* The bucket-independent ssl context that this bucket is associated with */
+    openssl_context_t *ssl_ctx;
+
+    /* Pointer to the 'right' databuf. */
+    serf_databuf_t *databuf;
+
+    /* Pointer to our stream, so we can find it later. */
+    serf_bucket_t **our_stream;
+} ssl_context_t;
+
+static void disable_compression(openssl_context_t *ssl_ctx);
+static apr_status_t serf__openssl_load_identity_from_file(void *impl_ctx,
+                        const serf_ssl_identity_t **identity,
+                        const char *file_path, apr_pool_t *pool);
+static apr_hash_t *convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool);
+
+#if SSL_VERBOSE
+/* Log all ssl alerts that we receive from the server. */
+static void
+apps_ssl_info_callback(const SSL *s, int where, int ret)
+{
+    const char *str;
+    int w;
+    w = where & ~SSL_ST_MASK;
+
+    if (w & SSL_ST_CONNECT)
+        str = "SSL_connect";
+    else if (w & SSL_ST_ACCEPT)
+        str = "SSL_accept";
+    else
+        str = "undefined";
+
+    if (where & SSL_CB_LOOP) {
+        serf__log(SSL_VERBOSE, __FILE__, "%s:%s\n", str,
+                  SSL_state_string_long(s));
+    }
+    else if (where & SSL_CB_ALERT) {
+        str = (where & SSL_CB_READ) ? "read" : "write";
+        serf__log(SSL_VERBOSE, __FILE__, "SSL3 alert %s:%s:%s\n",
+               str,
+               SSL_alert_type_string_long(ret),
+               SSL_alert_desc_string_long(ret));
+    }
+    else if (where & SSL_CB_EXIT) {
+        if (ret == 0)
+            serf__log(SSL_VERBOSE, __FILE__, "%s:failed in %s\n", str,
+                      SSL_state_string_long(s));
+        else if (ret < 0) {
+            serf__log(SSL_VERBOSE, __FILE__, "%s:error in %s\n", str,
+                      SSL_state_string_long(s));
+        }
+    }
+}
+#endif
+
+/* Returns the amount read. */
+static int bio_bucket_read(BIO *bio, char *in, int inlen)
+{
+    openssl_context_t *ctx = bio->ptr;
+    const char *data;
+    apr_status_t status;
+    apr_size_t len;
+
+    serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read called for %d bytes\n",
+              inlen);
+
+    if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN
+        && BIO_should_read(ctx->bio)) {
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "bio_bucket_read waiting: (%d %d %d)\n",
+           BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
+           BIO_get_retry_flags(ctx->bio));
+        /* Falling back... */
+        ctx->encrypt.exhausted_reset = 1;
+        BIO_clear_retry_flags(bio);
+    }
+
+    status = serf_bucket_read(ctx->decrypt.pending, inlen, &data, &len);
+
+    ctx->decrypt.status = status;
+
+    serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read received %d bytes (%d)\n",
+              len, status);
+
+    if (!SERF_BUCKET_READ_ERROR(status)) {
+        /* Oh suck. */
+        if (len) {
+            memcpy(in, data, len);
+            return len;
+        }
+        if (APR_STATUS_IS_EOF(status)) {
+            BIO_set_retry_read(bio);
+            return -1;
+        }
+    }
+
+    return -1;
+}
+
+/* Returns the amount written. */
+static int bio_bucket_write(BIO *bio, const char *in, int inl)
+{
+    openssl_context_t *ctx = bio->ptr;
+    serf_bucket_t *tmp;
+
+    serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_write called for %d bytes\n",
+              inl);
+
+    if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN
+        && !BIO_should_read(ctx->bio)) {
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "bio_bucket_write waiting: (%d %d %d)\n",
+           BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
+           BIO_get_retry_flags(ctx->bio));
+        /* Falling back... */
+        ctx->encrypt.exhausted_reset = 1;
+        BIO_clear_retry_flags(bio);
+    }
+
+    tmp = serf_bucket_simple_copy_create(in, inl,
+                                         ctx->encrypt.pending->allocator);
+
+    serf_bucket_aggregate_append(ctx->encrypt.pending, tmp);
+
+    return inl;
+}
+
+/* Returns the amount read. */
+static int bio_file_read(BIO *bio, char *in, int inlen)
+{
+    apr_file_t *file = bio->ptr;
+    apr_status_t status;
+    apr_size_t len;
+
+    BIO_clear_retry_flags(bio);
+
+    len = inlen;
+    status = apr_file_read(file, in, &len);
+
+    if (!SERF_BUCKET_READ_ERROR(status)) {
+        /* Oh suck. */
+        if (APR_STATUS_IS_EOF(status)) {
+            BIO_set_retry_read(bio);
+            return -1;
+        } else {
+            return len;
+        }
+    }
+
+    return -1;
+}
+
+/* Returns the amount written. */
+static int bio_file_write(BIO *bio, const char *in, int inl)
+{
+    apr_file_t *file = bio->ptr;
+    apr_size_t nbytes;
+
+    BIO_clear_retry_flags(bio);
+
+    nbytes = inl;
+    apr_file_write(file, in, &nbytes);
+
+    return nbytes;
+}
+
+static int bio_file_gets(BIO *bio, char *in, int inlen)
+{
+    return bio_file_read(bio, in, inlen);
+}
+
+static int bio_bucket_create(BIO *bio)
+{
+    bio->shutdown = 1;
+    bio->init = 1;
+    bio->num = -1;
+    bio->ptr = NULL;
+
+    return 1;
+}
+
+static int bio_bucket_destroy(BIO *bio)
+{
+    /* Did we already free this? */
+    if (bio == NULL) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static long bio_bucket_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+
+    switch (cmd) {
+    default:
+        /* abort(); */
+        break;
+    case BIO_CTRL_FLUSH:
+        /* At this point we can't force a flush. */
+        break;
+    case BIO_CTRL_PUSH:
+    case BIO_CTRL_POP:
+        ret = 0;
+        break;
+    }
+    return ret;
+}
+
+static BIO_METHOD bio_bucket_method = {
+    BIO_TYPE_MEM,
+    "Serf SSL encryption and decryption buckets",
+    bio_bucket_write,
+    bio_bucket_read,
+    NULL,                        /* Is this called? */
+    NULL,                        /* Is this called? */
+    bio_bucket_ctrl,
+    bio_bucket_create,
+    bio_bucket_destroy,
+#ifdef OPENSSL_VERSION_NUMBER
+    NULL /* sslc does not have the callback_ctrl field */
+#endif
+};
+
+static BIO_METHOD bio_file_method = {
+    BIO_TYPE_FILE,
+    "Wrapper around APR file structures",
+    bio_file_write,
+    bio_file_read,
+    NULL,                        /* Is this called? */
+    bio_file_gets,               /* Is this called? */
+    bio_bucket_ctrl,
+    bio_bucket_create,
+    bio_bucket_destroy,
+#ifdef OPENSSL_VERSION_NUMBER
+    NULL /* sslc does not have the callback_ctrl field */
+#endif
+};
+
+static int
+validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
+{
+    SSL *ssl;
+    openssl_context_t *ctx;
+    X509 *server_cert;
+    int err, depth;
+    int failures = 0;
+
+    ssl = X509_STORE_CTX_get_ex_data(store_ctx,
+                                     SSL_get_ex_data_X509_STORE_CTX_idx());
+    ctx = SSL_get_app_data(ssl);
+
+    server_cert = X509_STORE_CTX_get_current_cert(store_ctx);
+    depth = X509_STORE_CTX_get_error_depth(store_ctx);
+
+    /* If the certification was found invalid, get the error and convert it to
+       something our caller will understand. */
+    if (! cert_valid) {
+        err = X509_STORE_CTX_get_error(store_ctx);
+
+        switch(err) {
+            case X509_V_ERR_CERT_NOT_YET_VALID: 
+                    failures |= SERF_SSL_CERT_NOTYETVALID;
+                    break;
+            case X509_V_ERR_CERT_HAS_EXPIRED:
+                    failures |= SERF_SSL_CERT_EXPIRED;
+                    break;
+            case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+            case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+                    failures |= SERF_SSL_CERT_SELF_SIGNED;
+                    break;
+            case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+            case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+            case X509_V_ERR_CERT_UNTRUSTED:
+            case X509_V_ERR_INVALID_CA:
+                    failures |= SERF_SSL_CERT_UNKNOWNCA;
+                    break;
+            case X509_V_ERR_CERT_REVOKED:
+                    failures |= SERF_SSL_CERT_REVOKED;
+                    break;
+            default:
+                    failures |= SERF_SSL_CERT_UNKNOWN_FAILURE;
+                    break;
+        }
+    }
+
+    /* Check certificate expiry dates. */
+    if (X509_cmp_current_time(X509_get_notBefore(server_cert)) >= 0) {
+        failures |= SERF_SSL_CERT_NOTYETVALID;
+    }
+    else if (X509_cmp_current_time(X509_get_notAfter(server_cert)) <= 0) {
+        failures |= SERF_SSL_CERT_EXPIRED;
+    }
+
+    if (ctx->server_cert_callback &&
+        (depth == 0 || failures)) {
+        apr_status_t status;
+        serf_ssl_certificate_t *cert;
+
+        cert = serf__create_certificate(ctx->allocator,
+                                        &serf_ssl_bucket_type_openssl,
+                                        server_cert,
+                                        depth);
+
+        /* Callback for further verification. */
+        status = ctx->server_cert_callback(ctx->server_cert_userdata,
+                                           failures, cert);
+        if (status == APR_SUCCESS)
+            cert_valid = 1;
+        else {
+            /* Even if openssl found the certificate valid, the application
+             told us to reject it. */
+            cert_valid = 0;
+            /* Pass the error back to the caller through the context-run. */
+            ctx->pending_err = status;
+        }
+    }
+
+    if (ctx->server_cert_chain_callback
+        && (depth == 0 || failures)) {
+        apr_status_t status;
+        STACK_OF(X509) *chain;
+        const serf_ssl_certificate_t **certs;
+        int certs_len;
+        apr_pool_t *subpool;
+        serf_bucket_alloc_t *allocator;
+
+        apr_pool_create(&subpool, ctx->pool);
+        allocator = serf_bucket_allocator_create(subpool, NULL, NULL);
+
+        /* Borrow the chain to pass to the callback. */
+        chain = X509_STORE_CTX_get_chain(store_ctx);
+
+        /* If the chain can't be retrieved, just pass the current
+           certificate. */
+        /* ### can this actually happen with _get_chain() ?  */
+        if (!chain) {
+            serf_ssl_certificate_t *cert;
+
+            cert = serf__create_certificate(allocator,
+                                            &serf_ssl_bucket_type_openssl,
+                                            server_cert,
+                                            depth);
+
+            /* Room for the server_cert and a trailing NULL.  */
+            certs = apr_palloc(subpool, sizeof(*certs) * 2);
+            certs[0] = cert;
+
+            certs_len = 1;
+        } else {
+            int i;
+        
+            certs_len = sk_X509_num(chain);
+
+            /* Room for all the certs and a trailing NULL.  */
+            certs = apr_palloc(subpool, sizeof(*certs) * (certs_len + 1));
+            for (i = 0; i < certs_len; ++i) {
+                serf_ssl_certificate_t *cert;
+
+                cert = serf__create_certificate(allocator,
+                                                &serf_ssl_bucket_type_openssl,
+                                                sk_X509_value(chain, i),
+                                                i);
+
+                certs[i] = cert;
+            }
+        }
+        certs[certs_len] = NULL;
+
+        /* Callback for further verification. */
+        status = ctx->server_cert_chain_callback(ctx->server_cert_userdata,
+                                                 failures, depth,
+                                                 certs, certs_len);
+        if (status == APR_SUCCESS) {
+            cert_valid = 1;
+        } else {
+            /* Even if openssl found the certificate valid, the application
+             told us to reject it. */
+            cert_valid = 0;
+            /* Pass the error back to the caller through the context-run. */
+            ctx->pending_err = status;
+        }
+
+        apr_pool_destroy(subpool);
+    }
+
+    /* Return a specific error if the server certificate is not accepted by
+     OpenSSL and the application has not set callbacks to override this. */
+    if (!cert_valid &&
+        !ctx->server_cert_chain_callback &&
+        !ctx->server_cert_callback)
+    {
+        ctx->pending_err = SERF_ERROR_SSL_CERT_FAILED;
+    }
+
+    return cert_valid;
+}
+
+/* This function reads an encrypted stream and returns the decrypted stream. */
+static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
+                                char *buf, apr_size_t *len)
+{
+    openssl_context_t *ctx = baton;
+    apr_size_t priv_len;
+    apr_status_t status;
+    const char *data;
+    int ssl_len;
+
+    if (ctx->fatal_err)
+        return ctx->fatal_err;
+
+    serf__log(SSL_VERBOSE, __FILE__, "ssl_decrypt: begin %d\n", bufsize);
+
+    /* Is there some data waiting to be read? */
+    ssl_len = SSL_read(ctx->ssl, buf, bufsize);
+    if (ssl_len > 0) {
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n",
+                  ssl_len, bufsize, ctx->decrypt.status,
+                  BIO_get_retry_flags(ctx->bio));
+        *len = ssl_len;
+        return APR_SUCCESS;
+    }
+
+    status = serf_bucket_read(ctx->decrypt.stream, bufsize, &data, &priv_len);
+
+    if (!SERF_BUCKET_READ_ERROR(status) && priv_len) {
+        serf_bucket_t *tmp;
+
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "ssl_decrypt: read %d bytes (%d); status: %d\n",
+                  priv_len, bufsize, status);
+
+        tmp = serf_bucket_simple_copy_create(data, priv_len,
+                                             ctx->decrypt.pending->allocator);
+
+        serf_bucket_aggregate_append(ctx->decrypt.pending, tmp);
+
+        ssl_len = SSL_read(ctx->ssl, buf, bufsize);
+        if (ssl_len < 0) {
+            int ssl_err;
+
+            ssl_err = SSL_get_error(ctx->ssl, ssl_len);
+            switch (ssl_err) {
+            case SSL_ERROR_SYSCALL:
+                *len = 0;
+                status = ctx->decrypt.status;
+                break;
+            case SSL_ERROR_WANT_READ:
+                *len = 0;
+                status = APR_EAGAIN;
+                break;
+            case SSL_ERROR_SSL:
+                *len = 0;
+                if (ctx->pending_err) {
+                    status = ctx->pending_err;
+                    ctx->pending_err = 0;
+                } else {
+                    ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
+                }
+                break;
+            default:
+                *len = 0;
+                ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
+                break;
+            }
+        } else if (ssl_len == 0) {
+            /* The server shut down the connection. */
+            int ssl_err, shutdown;
+            *len = 0;
+
+            /* Check for SSL_RECEIVED_SHUTDOWN */
+            shutdown = SSL_get_shutdown(ctx->ssl);
+            /* Check for SSL_ERROR_ZERO_RETURN */
+            ssl_err = SSL_get_error(ctx->ssl, ssl_len);
+
+            if (shutdown == SSL_RECEIVED_SHUTDOWN &&
+                ssl_err == SSL_ERROR_ZERO_RETURN) {
+                /* The server closed the SSL session. While this doesn't
+                necessary mean the connection is closed, let's close
+                it here anyway.
+                We can optimize this later. */
+                serf__log(SSL_VERBOSE, __FILE__, 
+                          "ssl_decrypt: SSL read error: server"
+                          " shut down connection!\n");
+                status = APR_EOF;
+            } else {
+                /* A fatal error occurred. */
+                ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
+            }
+        } else {
+            *len = ssl_len;
+            serf__log(SSL_MSG_VERBOSE, __FILE__, 
+                      "---\n%.*s\n-(%d)-\n", *len, buf, *len);
+        }
+    }
+    else {
+        *len = 0;
+    }
+    serf__log(SSL_VERBOSE, __FILE__, 
+              "ssl_decrypt: %d %d %d\n", status, *len,
+              BIO_get_retry_flags(ctx->bio));
+
+    return status;
+}
+
+/* This function reads a decrypted stream and returns an encrypted stream. */
+static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
+                                char *buf, apr_size_t *len)
+{
+    const char *data;
+    apr_size_t interim_bufsize;
+    openssl_context_t *ctx = baton;
+    apr_status_t status;
+
+    if (ctx->fatal_err)
+        return ctx->fatal_err;
+
+    serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: begin %d\n", bufsize);
+
+    /* Try to read already encrypted but unread data first. */
+    status = serf_bucket_read(ctx->encrypt.pending, bufsize, &data, len);
+    if (SERF_BUCKET_READ_ERROR(status)) {
+        return status;
+    }
+
+    /* Aha, we read something.  Return that now. */
+    if (*len) {
+        memcpy(buf, data, *len);
+        if (APR_STATUS_IS_EOF(status)) {
+            status = APR_SUCCESS;
+        }
+
+        serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: %d %d %d (quick read)\n",
+                  status, *len, BIO_get_retry_flags(ctx->bio));
+
+        return status;
+    }
+
+    if (BIO_should_retry(ctx->bio) && BIO_should_write(ctx->bio)) {
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "ssl_encrypt: %d %d %d (should write exit)\n",
+                  status, *len, BIO_get_retry_flags(ctx->bio));
+
+        return APR_EAGAIN;
+    }
+
+    /* If we were previously blocked, unblock ourselves now. */
+    if (BIO_should_read(ctx->bio)) {
+        serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: reset %d %d (%d %d %d)\n",
+                  status, ctx->encrypt.status,
+                  BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
+                  BIO_get_retry_flags(ctx->bio));
+
+        ctx->encrypt.status = APR_SUCCESS;
+        ctx->encrypt.exhausted_reset = 0;
+    }
+
+    /* Oh well, read from our stream now. */
+    interim_bufsize = bufsize;
+    do {
+        apr_size_t interim_len;
+
+        if (!ctx->encrypt.status) {
+            struct iovec vecs[64];
+            int vecs_read;
+
+            status = serf_bucket_read_iovec(ctx->encrypt.stream,
+                                            interim_bufsize, 64, vecs,
+                                            &vecs_read);
+
+            if (!SERF_BUCKET_READ_ERROR(status) && vecs_read) {
+                char *vecs_data;
+                int i, cur, vecs_data_len;
+                int ssl_len;
+
+                /* Combine the buffers of the iovec into one buffer, as
+                   that is with SSL_write requires. */
+                vecs_data_len = 0;
+                for (i = 0; i < vecs_read; i++) {
+                    vecs_data_len += vecs[i].iov_len;
+                }
+
+                vecs_data = serf_bucket_mem_alloc(ctx->allocator,
+                                                  vecs_data_len);
+
+                cur = 0;
+                for (i = 0; i < vecs_read; i++) {
+                    memcpy(vecs_data + cur, vecs[i].iov_base, vecs[i].iov_len);
+                    cur += vecs[i].iov_len;
+                }
+
+                interim_bufsize -= vecs_data_len;
+                interim_len = vecs_data_len;
+
+                serf__log(SSL_VERBOSE, __FILE__,
+                          "ssl_encrypt: bucket read %d bytes; "\
+                          "status %d\n", interim_len, status);
+                serf__log(SSL_MSG_VERBOSE, __FILE__, "---\n%.*s\n-(%d)-\n",
+                          interim_len, vecs_data, interim_len);
+
+                /* Stash our status away. */
+                ctx->encrypt.status = status;
+
+                ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len);
+
+                serf__log(SSL_VERBOSE, __FILE__, 
+                          "ssl_encrypt: SSL write: %d\n", ssl_len);
+
+                /* If we failed to write... */
+                if (ssl_len < 0) {
+                    int ssl_err;
+
+                    /* Ah, bugger. We need to put that data back.
+                     Note: use the copy here, we do not own the original iovec
+                     data buffer so it will be freed on next read. */
+                    serf_bucket_t *vecs_copy =
+                    serf_bucket_simple_own_create(vecs_data,
+                                                  vecs_data_len,
+                                                  ctx->allocator);
+                    serf_bucket_aggregate_prepend(ctx->encrypt.stream,
+                                                  vecs_copy);
+
+                    ssl_err = SSL_get_error(ctx->ssl, ssl_len);
+
+                    serf__log(SSL_VERBOSE, __FILE__,
+                              "ssl_encrypt: SSL write error: %d\n", ssl_err);
+
+                    if (ssl_err == SSL_ERROR_SYSCALL) {
+                        status = ctx->encrypt.status;
+                        if (SERF_BUCKET_READ_ERROR(status)) {
+                            return status;
+                        }
+                    }
+                    else {
+                        /* Oh, no. */
+                        if (ssl_err == SSL_ERROR_WANT_READ) {
+                            status = SERF_ERROR_WAIT_CONN;
+                        }
+                        else {
+                            ctx->fatal_err = status =
+                            SERF_ERROR_SSL_COMM_FAILED;
+                        }
+                    }
+
+                    serf__log(SSL_VERBOSE, __FILE__,
+                              "ssl_encrypt: SSL write error: %d %d\n",
+                              status, *len);
+                } else {
+                    /* We're done with this data. */
+                    serf_bucket_mem_free(ctx->allocator, vecs_data);
+                }
+            }
+        }
+        else {
+            interim_len = 0;
+            *len = 0;
+            status = ctx->encrypt.status;
+        }
+
+    } while (!status && interim_bufsize);
+
+    /* Okay, we exhausted our underlying stream. */
+    if (!SERF_BUCKET_READ_ERROR(status)) {
+        apr_status_t agg_status;
+        struct iovec vecs[64];
+        int vecs_read, i;
+
+        /* We read something! */
+        agg_status = serf_bucket_read_iovec(ctx->encrypt.pending, bufsize,
+                                            64, vecs, &vecs_read);
+        *len = 0;
+        for (i = 0; i < vecs_read; i++) {
+            memcpy(buf + *len, vecs[i].iov_base, vecs[i].iov_len);
+            *len += vecs[i].iov_len;
+        }
+
+        serf__log(SSL_VERBOSE, __FILE__,
+                  "ssl_encrypt read agg: %d %d %d %d\n", status, agg_status,
+            ctx->encrypt.status, *len);
+
+        if (!agg_status) {
+            status = agg_status;
+        }
+    }
+
+    if (status == SERF_ERROR_WAIT_CONN
+        && BIO_should_retry(ctx->bio) && BIO_should_read(ctx->bio)) {
+        ctx->encrypt.exhausted = ctx->encrypt.status;
+        ctx->encrypt.status = SERF_ERROR_WAIT_CONN;
+    }
+
+    serf__log(SSL_VERBOSE, __FILE__,
+              "ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len,
+              BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
+              BIO_get_retry_flags(ctx->bio));
+
+    return status;
+}
+
+#if APR_HAS_THREADS
+static apr_pool_t *ssl_pool;
+static apr_thread_mutex_t **ssl_locks;
+
+typedef struct CRYPTO_dynlock_value {
+    apr_thread_mutex_t *lock;
+} CRYPTO_dynlock_value;
+
+static CRYPTO_dynlock_value *ssl_dyn_create(const char* file, int line)
+{
+    CRYPTO_dynlock_value *l;
+    apr_status_t rv;
+
+    l = apr_palloc(ssl_pool, sizeof(CRYPTO_dynlock_value));
+    rv = apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, ssl_pool);
+    if (rv != APR_SUCCESS) {
+        /* FIXME: return error here */
+    }
+    return l;
+}
+
+static void ssl_dyn_lock(int mode, CRYPTO_dynlock_value *l, const char *file,
+                         int line)
+{
+    if (mode & CRYPTO_LOCK) {
+        apr_thread_mutex_lock(l->lock);
+    }
+    else if (mode & CRYPTO_UNLOCK) {
+        apr_thread_mutex_unlock(l->lock);
+    }
+}
+
+static void ssl_dyn_destroy(CRYPTO_dynlock_value *l, const char *file,
+                            int line)
+{
+    apr_thread_mutex_destroy(l->lock);
+}
+
+static void ssl_lock(int mode, int n, const char *file, int line)
+{
+    if (mode & CRYPTO_LOCK) {
+        apr_thread_mutex_lock(ssl_locks[n]);
+    }
+    else if (mode & CRYPTO_UNLOCK) {
+        apr_thread_mutex_unlock(ssl_locks[n]);
+    }
+}
+
+static unsigned long ssl_id(void)
+{
+    /* FIXME: This is lame and not portable. -aaron */
+    return (unsigned long) apr_os_thread_current();
+}
+
+static apr_status_t cleanup_ssl(void *data)
+{
+    CRYPTO_set_locking_callback(NULL);
+    CRYPTO_set_id_callback(NULL);
+    CRYPTO_set_dynlock_create_callback(NULL);
+    CRYPTO_set_dynlock_lock_callback(NULL);
+    CRYPTO_set_dynlock_destroy_callback(NULL);
+
+    return APR_SUCCESS;
+}
+
+#endif
+
+static apr_uint32_t have_init_ssl = 0;
+
+static void init_ssl_libraries(void)
+{
+    apr_uint32_t val;
+#if APR_VERSION_AT_LEAST(1,0,0)
+    val = apr_atomic_xchg32(&have_init_ssl, 1);
+#else
+    val = apr_atomic_cas(&have_init_ssl, 1, 0);
+#endif
+
+    if (!val) {
+#if APR_HAS_THREADS
+        int i, numlocks;
+#endif
+
+#ifdef SSL_VERBOSE
+        /* Warn when compile-time and run-time version of OpenSSL differ in
+           major/minor version number. */
+        long libver = SSLeay();
+
+        if ((libver ^ OPENSSL_VERSION_NUMBER) & 0xFFF00000) {
+            serf__log(SSL_VERBOSE, __FILE__,
+                      "Warning: OpenSSL library version mismatch, compile-time "
+                      "was %lx, runtime is %lx.\n",
+                      OPENSSL_VERSION_NUMBER, libver);
+        }
+#endif
+
+        CRYPTO_malloc_init();
+        ERR_load_crypto_strings();
+        SSL_load_error_strings();
+        SSL_library_init();
+        OpenSSL_add_all_algorithms();
+
+#if APR_HAS_THREADS
+        numlocks = CRYPTO_num_locks();
+        apr_pool_create(&ssl_pool, NULL);
+        ssl_locks = apr_palloc(ssl_pool, sizeof(apr_thread_mutex_t*)*numlocks);
+        for (i = 0; i < numlocks; i++) {
+            apr_status_t rv;
+
+            /* Intraprocess locks don't /need/ a filename... */
+            rv = apr_thread_mutex_create(&ssl_locks[i],
+                                         APR_THREAD_MUTEX_DEFAULT, ssl_pool);
+            if (rv != APR_SUCCESS) {
+                /* FIXME: error out here */
+            }
+        }
+        CRYPTO_set_locking_callback(ssl_lock);
+        CRYPTO_set_id_callback(ssl_id);
+        CRYPTO_set_dynlock_create_callback(ssl_dyn_create);
+        CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock);
+        CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy);
+
+        apr_pool_cleanup_register(ssl_pool, NULL, cleanup_ssl, cleanup_ssl);
+#endif
+    }
+}
+
+static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
+{
+    openssl_context_t *ctx = SSL_get_app_data(ssl);
+    apr_hash_t **dnlist = NULL;
+    apr_size_t dnlen = 0;
+    apr_pool_t *tmppool;
+    apr_status_t status;
+
+    if (ctx->cached_identity) {
+        *cert = ctx->cached_identity->impl_cert;
+        *pkey = ctx->cached_identity->impl_pkey;
+        return 1;
+    }
+
+    if (!ctx->cert_callback && !ctx->identity_callback)
+        return 0;
+
+    apr_pool_create(&tmppool, ctx->pool);
+
+    /* Loop until a client identity is returned by the application, a .p12 file
+       is read and parsed successfully, or as long as the application keeps 
+       providing .p12 paths and passwords. */
+    while (1) {
+        const char *cert_path;
+        const serf_ssl_identity_t *identity;
+
+        if (ctx->cert_callback)
+        {
+            /* If we have a path to a .p12 file cached from a previous session,
+               we will reuse it here so we don't have to call the application.
+             */
+            if (ctx->cert_file_success) {
+                cert_path = ctx->cert_file_success;
+            } else {
+                status = ctx->cert_callback(ctx->cert_userdata, &cert_path);
+                if (status || !cert_path)
+                    break;
+            }
+            status = serf__openssl_load_identity_from_file(ctx,
+                                                           &identity,
+                                                           cert_path,
+                                                           ctx->pool);
+            /* No need to read & parse this file again. If it was parsed
+               successfully we will cache the identity object. If not we
+               don't want to use it anyway. */
+            ctx->cert_file_success = NULL;
+        }
+        else
+        {
+            if (!dnlist) {
+                STACK_OF(X509_NAME) *cas;
+                int i;
+
+                cas = SSL_get_client_CA_list(ctx->ssl);
+                dnlen = sk_X509_NAME_num(cas);
+
+                dnlist = apr_palloc(tmppool, dnlen * sizeof(apr_hash_t*));
+
+                for (i = 0; i < dnlen; i++) {
+                    apr_hash_t *issuer;
+
+                    X509_NAME *ca = (X509_NAME *)sk_X509_NAME_value(cas, i);
+                    issuer = convert_X509_NAME_to_table(ca, tmppool);
+
+                    dnlist[i] = issuer;
+                }
+            }
+            status = ctx->identity_callback(ctx->cert_userdata,
+                                            dnlist, dnlen, &identity,
+                                            ctx->pool);
+            if (status || !identity)
+                break;
+        }
+
+        if (status == APR_SUCCESS)
+        {
+            ctx->cached_identity = identity;
+
+            *cert = identity->impl_cert;
+            *pkey = identity->impl_pkey;
+
+            apr_pool_destroy(tmppool);
+
+            return 1;
+        }
+
+        /* A file was read but not parsed successfully, so ask the application
+           for another file. */
+    }
+
+    apr_pool_destroy(tmppool);
+
+    return 0;
+}
+
+
+static void serf__openssl_client_cert_provider_set(
+    void *impl_ctx,
+    serf_ssl_need_client_cert_t callback,
+    void *data,
+    void *cache_pool)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->cert_callback = callback;
+    ssl_ctx->cert_userdata = data;
+    ssl_ctx->cert_cache_pool = cache_pool;
+    if (ssl_ctx->cert_cache_pool) {
+        apr_pool_userdata_get((void**)&ssl_ctx->cert_file_success,
+                              "serf:ssl:cert", cache_pool);
+    }
+}
+
+
+static void serf__openssl_client_cert_password_set(
+    void *impl_ctx,
+    serf_ssl_need_cert_password_t callback,
+    void *data,
+    void *cache_pool)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->cert_pw_callback = callback;
+    ssl_ctx->cert_pw_userdata = data;
+    ssl_ctx->cert_pw_cache_pool = cache_pool;
+    if (ssl_ctx->cert_pw_cache_pool) {
+        apr_pool_userdata_get((void**)&ssl_ctx->cert_pw_success,
+                              "serf:ssl:certpw", cache_pool);
+    }
+}
+
+
+static void
+serf__openssl_identity_provider_set(void *impl_ctx,
+                                    serf_ssl_need_identity_t callback,
+                                    void *data,
+                                    void *cache_pool)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->identity_callback = callback;
+    ssl_ctx->cert_userdata = data;
+    ssl_ctx->cert_cache_pool = cache_pool;
+    if (ssl_ctx->cert_cache_pool) {
+        apr_pool_userdata_get((void**)&ssl_ctx->cert_file_success,
+                              "serf:ssl:cert", cache_pool);
+    }
+}
+
+static void serf__openssl_server_cert_callback_set(
+    void *impl_ctx,
+    serf_ssl_need_server_cert_t callback,
+    void *data)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->server_cert_callback = callback;
+    ssl_ctx->server_cert_userdata = data;
+}
+
+static void serf__openssl_server_cert_chain_callback_set(
+    void *impl_ctx,
+    serf_ssl_need_server_cert_t cert_callback,
+    serf_ssl_server_cert_chain_cb_t cert_chain_callback,
+    void *data)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->server_cert_callback = cert_callback;
+    ssl_ctx->server_cert_chain_callback = cert_chain_callback;
+    ssl_ctx->server_cert_userdata = data;
+}
+
+static void serf__openssl_new_session_callback_set(
+                void *impl_ctx,
+                serf_ssl_new_session_t new_session_cb,
+                void *baton)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    ssl_ctx->new_session_cb = new_session_cb;
+    ssl_ctx->new_session_cb_baton = baton;
+}
+
+static int new_session(SSL *ssl, SSL_SESSION *sess)
+{
+    openssl_context_t *ssl_ctx = SSL_get_app_data(ssl);
+
+    if (ssl_ctx->new_session_cb) {
+        serf_ssl_session_t session;
+        apr_pool_t *subpool;
+
+/*        session.type = &serf_ssl_bucket_type_openssl;*/
+        session.impl_session_obj = sess;
+        apr_pool_create(&subpool, ssl_ctx->pool);
+
+        ssl_ctx->new_session_cb(&session, ssl_ctx->new_session_cb_baton,
+                                subpool);
+
+        apr_pool_destroy(subpool);
+    }
+
+    return 0;
+}
+
+static apr_status_t
+serf__openssl_session_export(void **data_p,
+                             apr_size_t *len_p,
+                             const serf_ssl_session_t *session,
+                             apr_pool_t *pool)
+{
+    int sess_len;
+    void *sess_data;
+    unsigned char *unused;
+
+    sess_len = i2d_SSL_SESSION(session->impl_session_obj, NULL);
+    if (!sess_len) {
+        return APR_EGENERAL;
+    }
+
+    sess_data = apr_palloc(pool, sess_len);
+
+    unused = sess_data;
+    /* unused is incremented  */
+    sess_len = i2d_SSL_SESSION(session->impl_session_obj, &unused);
+    if (!sess_len) {
+        return APR_EGENERAL;
+    }
+
+    *data_p = sess_data;
+    *len_p = sess_len;
+    return APR_SUCCESS;
+}
+
+static apr_status_t cleanup_session(void *data)
+{
+    serf_ssl_session_t *session = data;
+
+    SSL_SESSION_free(session->impl_session_obj);
+    session->impl_session_obj = NULL;
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t
+serf__openssl_session_import(const serf_ssl_session_t **session_p,
+                             void *data,
+                             apr_size_t len,
+                             apr_pool_t *pool)
+{
+    SSL_SESSION *sess;
+    serf_ssl_session_t *session;
+    const unsigned char *unused;
+
+    unused = data;
+    sess = d2i_SSL_SESSION(NULL, &unused, len); /* unused is incremented  */
+
+    if (!sess) {
+        return APR_EGENERAL;
+    }
+
+    session = apr_pcalloc(pool, sizeof(serf_ssl_session_t));
+/*    session->type = &serf_ssl_bucket_type_openssl;*/
+    session->impl_session_obj = sess;
+    apr_pool_cleanup_register(pool, session, cleanup_session, cleanup_session);
+    
+    *session_p = session;
+    return APR_SUCCESS;
+}
+
+static openssl_context_t *ssl_init_context(void)
+{
+    openssl_context_t *ssl_ctx;
+    apr_pool_t *pool;
+    serf_bucket_alloc_t *allocator;
+
+    init_ssl_libraries();
+
+    apr_pool_create(&pool, NULL);
+    allocator = serf_bucket_allocator_create(pool, NULL, NULL);
+
+    ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
+
+    ssl_ctx->refcount = 0;
+    ssl_ctx->pool = pool;
+    ssl_ctx->allocator = allocator;
+
+    ssl_ctx->ctx = SSL_CTX_new(SSLv23_client_method());
+
+    SSL_CTX_set_client_cert_cb(ssl_ctx->ctx, ssl_need_client_cert);
+    ssl_ctx->cached_identity = 0;
+    ssl_ctx->pending_err = APR_SUCCESS;
+    ssl_ctx->fatal_err = APR_SUCCESS;
+
+    ssl_ctx->cert_callback = NULL;
+    ssl_ctx->identity_callback = NULL;
+    ssl_ctx->cert_pw_callback = NULL;
+    ssl_ctx->server_cert_callback = NULL;
+    ssl_ctx->server_cert_chain_callback = NULL;
+
+    SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER,
+                       validate_server_certificate);
+    SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL);
+
+    ssl_ctx->new_session_cb = NULL;
+
+    /* Enable SSL callback for new sessions and disable internal session
+     handling. */
+    SSL_CTX_set_session_cache_mode(
+        ssl_ctx->ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+    SSL_CTX_sess_set_new_cb(ssl_ctx->ctx, new_session);
+
+    /* Disable SSL compression by default. */
+    disable_compression(ssl_ctx);
+
+    ssl_ctx->ssl = SSL_new(ssl_ctx->ctx);
+    ssl_ctx->bio = BIO_new(&bio_bucket_method);
+    ssl_ctx->bio->ptr = ssl_ctx;
+
+    SSL_set_bio(ssl_ctx->ssl, ssl_ctx->bio, ssl_ctx->bio);
+
+    SSL_set_connect_state(ssl_ctx->ssl);
+
+    SSL_set_app_data(ssl_ctx->ssl, ssl_ctx);
+
+#if SSL_VERBOSE
+    SSL_CTX_set_info_callback(ssl_ctx->ctx, apps_ssl_info_callback);
+#endif
+
+    ssl_ctx->encrypt.stream = NULL;
+    ssl_ctx->encrypt.stream_next = NULL;
+    ssl_ctx->encrypt.pending = serf_bucket_aggregate_create(allocator);
+    ssl_ctx->encrypt.status = APR_SUCCESS;
+    serf_databuf_init(&ssl_ctx->encrypt.databuf);
+    ssl_ctx->encrypt.databuf.read = ssl_encrypt;
+    ssl_ctx->encrypt.databuf.read_baton = ssl_ctx;
+
+    ssl_ctx->decrypt.stream = NULL;
+    ssl_ctx->decrypt.pending = serf_bucket_aggregate_create(allocator);
+    ssl_ctx->decrypt.status = APR_SUCCESS;
+    serf_databuf_init(&ssl_ctx->decrypt.databuf);
+    ssl_ctx->decrypt.databuf.read = ssl_decrypt;
+    ssl_ctx->decrypt.databuf.read_baton = ssl_ctx;
+
+    return ssl_ctx;
+}
+
+apr_status_t serf__openssl_resume_session(void *impl_ctx,
+                                          const serf_ssl_session_t *session,
+                                          apr_pool_t *pool)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    SSL_set_session(ssl_ctx->ssl, session->impl_session_obj);
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_free_context(openssl_context_t *ssl_ctx)
+{
+    apr_pool_t *p;
+
+    /* If never had the pending buckets, don't try to free them. */
+    if (ssl_ctx->decrypt.pending != NULL) {
+        serf_bucket_destroy(ssl_ctx->decrypt.pending);
+    }
+    if (ssl_ctx->encrypt.pending != NULL) {
+        serf_bucket_destroy(ssl_ctx->encrypt.pending);
+    }
+
+    /* SSL_free implicitly frees the underlying BIO. */
+    SSL_free(ssl_ctx->ssl);
+    SSL_CTX_free(ssl_ctx->ctx);
+
+    p = ssl_ctx->pool;
+
+    serf_bucket_mem_free(ssl_ctx->allocator, ssl_ctx);
+    apr_pool_destroy(p);
+
+    return APR_SUCCESS;
+}
+
+static void bucket_create(
+    serf_bucket_t *bucket,
+    void *impl_ctx,
+    serf_bucket_alloc_t *allocator,
+    const serf_bucket_type_t *type)
+{
+    ssl_context_t *ctx;
+
+    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
+    if (!impl_ctx) {
+        ctx->ssl_ctx = ssl_init_context();
+    }
+    else {
+        ctx->ssl_ctx = impl_ctx;
+    }
+    ctx->ssl_ctx->refcount++;
+
+    bucket->data = ctx;
+    bucket->type = type;
+    bucket->allocator = allocator;
+}
+
+static apr_status_t serf__openssl_set_hostname(void *impl_ctx,
+                                               const char * hostname)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+#ifdef SSL_set_tlsext_host_name
+    if (SSL_set_tlsext_host_name(ssl_ctx->ssl, hostname) != 1) {
+        ERR_clear_error();
+    }
+#endif
+    return APR_SUCCESS;
+}
+
+static apr_status_t serf__openssl_use_default_certificates(void *impl_ctx)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+    X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx);
+
+    int result = X509_STORE_set_default_paths(store);
+
+    return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED;
+}
+
+static apr_status_t serf__openssl_load_CA_cert_from_file(
+    serf_ssl_certificate_t **cert,
+    const char *file_path,
+    apr_pool_t *pool)
+{
+    FILE *fp = fopen(file_path, "r");
+
+    if (fp) {
+        X509 *ssl_cert = PEM_read_X509(fp, NULL, NULL, NULL);
+        fclose(fp);
+
+        if (ssl_cert) {
+            serf_bucket_alloc_t *allocator =
+                serf_bucket_allocator_create(pool, NULL, NULL);
+
+            *cert = serf__create_certificate(allocator,
+                                             &serf_ssl_bucket_type_openssl,
+                                             ssl_cert,
+                                             0);
+            return APR_SUCCESS;
+        }
+    }
+
+    return SERF_ERROR_SSL_CERT_FAILED;
+}
+
+
+static apr_status_t
+callback_for_identity_pw(openssl_context_t *ctx,
+                         const char *cert_path,
+                         const char **password)
+{
+    apr_status_t status;
+
+    if (ctx->cert_pw_success) {
+        status = APR_SUCCESS;
+        *password = ctx->cert_pw_success;
+        ctx->cert_pw_success = NULL;
+    } else if (ctx->cert_pw_callback)
+    {
+        status = ctx->cert_pw_callback(ctx->cert_pw_userdata,
+                                       cert_path,
+                                       password);
+    } else
+    {
+        status = SERF_ERROR_SSL_CLIENT_CERT_PW_FAILED;
+    }
+
+    return status;
+}
+
+/* Opens a .p12 file and reads the client certificate and private key.
+   When parsing the file without password fails, this function will call the
+   application's identity password callback.
+
+   On successful parsing of the .p12 file, the file name & password will be
+   cached in the cache_pool provided in the calls:
+    client_cert_provider_set/client_cert_password_set (deprecated)
+    or identity_provider_set/client_cert_password_set.
+ */
+static apr_status_t
+serf__openssl_load_identity_from_file(void *impl_ctx,
+                                      const serf_ssl_identity_t **identity,
+                                      const char *file_path,
+                                      apr_pool_t *pool)
+{
+    openssl_context_t *ctx = impl_ctx;
+    apr_file_t *cert_file;
+    BIO *bio;
+    PKCS12 *p12;
+    const char *password = NULL;
+    int i, retry_once = 2;
+    apr_status_t status;
+
+    /* Load the x.509 cert file stored in PKCS12 */
+    status = apr_file_open(&cert_file, file_path, APR_READ, APR_OS_DEFAULT,
+                           ctx->pool);
+    if (status)
+        return status;
+
+    bio = BIO_new(&bio_file_method);
+    bio->ptr = cert_file;
+
+    p12 = d2i_PKCS12_bio(bio, NULL);
+    apr_file_close(cert_file);
+
+    /* Try parsing the file without password. If that doesn'twork, call the
+       application to get the password and try again. */
+    while (retry_once)
+    {
+        X509 *cert;
+        EVP_PKEY *pkey;
+
+        retry_once--;
+
+        i = PKCS12_parse(p12, password, &pkey, &cert, NULL);
+
+        if (i == 1)
+        {
+            *identity = serf__create_identity(&serf_ssl_bucket_type_openssl,
+                                              cert, pkey, pool);
+
+            /* If we haven't done so for this file already, store the file path
+               and associated password in the pool that will survive this
+               session for reuse. */
+            if (!ctx->cert_file_success && ctx->cert_cache_pool) {
+                const char *c;
+
+                c = apr_pstrdup(ctx->cert_cache_pool,
+                                file_path);
+
+                apr_pool_userdata_setn(c, "serf:ssl:cert",
+                                       apr_pool_cleanup_null,
+                                       ctx->cert_cache_pool);
+            }
+            if (!ctx->cert_file_success && ctx->cert_pw_cache_pool) {
+                const char *c;
+
+                c = apr_pstrdup(ctx->cert_pw_cache_pool,
+                                password);
+
+                apr_pool_userdata_setn(c, "serf:ssl:certpw",
+                                       apr_pool_cleanup_null,
+                                       ctx->cert_pw_cache_pool);
+            }
+
+            status = APR_SUCCESS;
+            goto cleanup;
+        } else {
+            int err = ERR_get_error();
+            ERR_clear_error();
+
+            /* If we haven't done so yet, call the application to get the
+               matching passphrase and retry parsing the file. */
+            if (retry_once &&
+                ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
+                ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE)
+            {
+                status = callback_for_identity_pw(ctx, file_path,
+                                                  &password);
+                if (status)
+                    goto cleanup;
+
+                /* continue with new password */
+            } else {
+                if (retry_once)
+                    serf__log(SSL_VERBOSE, __FILE__,
+                              "OpenSSL cert error: %d %d %d.\n",
+                              ERR_GET_LIB(err), ERR_GET_FUNC(err),
+                              ERR_GET_REASON(err));
+                status = SERF_ERROR_SSL_CERT_FAILED;
+                goto cleanup;
+            }
+        }
+    }
+
+cleanup:
+    BIO_free(bio);
+    PKCS12_free(p12);
+
+    return status;
+}
+
+
+static apr_status_t serf__openssl_trust_cert(
+    void *impl_ctx,
+    serf_ssl_certificate_t *cert)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+    X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx);
+
+    int result = X509_STORE_add_cert(store, cert->impl_cert);
+
+    return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED;
+}
+
+
+static void *
+serf__openssl_bucket_decrypt_create(serf_bucket_t *bucket,
+                                    serf_bucket_t *stream,
+                                    void *impl_ctx,
+                                    serf_bucket_alloc_t *allocator)
+{
+    ssl_context_t *ctx;
+
+    bucket_create(bucket, impl_ctx, allocator,
+                  &serf_bucket_type_openssl_decrypt);
+
+    ctx = bucket->data;
+
+    ctx->databuf = &ctx->ssl_ctx->decrypt.databuf;
+    if (ctx->ssl_ctx->decrypt.stream != NULL) {
+        return NULL;
+    }
+    ctx->ssl_ctx->decrypt.stream = stream;
+    ctx->our_stream = &ctx->ssl_ctx->decrypt.stream;
+
+    return ctx->ssl_ctx;
+}
+
+
+static void *serf__openssl_bucket_decrypt_context_get(
+     serf_bucket_t *bucket)
+{
+    ssl_context_t *ctx = bucket->data;
+    return ctx->ssl_ctx;
+}
+
+static void *
+serf__openssl_bucket_encrypt_create(serf_bucket_t *bucket,
+                                    serf_bucket_t *stream,
+                                    void *impl_ctx,
+                                    serf_bucket_alloc_t *allocator)
+{
+    ssl_context_t *ctx;
+
+    bucket_create(bucket, impl_ctx, allocator,
+                  &serf_bucket_type_openssl_encrypt);
+
+    ctx = bucket->data;
+
+    ctx->databuf = &ctx->ssl_ctx->encrypt.databuf;
+    ctx->our_stream = &ctx->ssl_ctx->encrypt.stream;
+    if (ctx->ssl_ctx->encrypt.stream == NULL) {
+        serf_bucket_t *tmp = serf_bucket_aggregate_create(stream->allocator);
+        serf_bucket_aggregate_append(tmp, stream);
+        ctx->ssl_ctx->encrypt.stream = tmp;
+    }
+    else {
+        bucket_list_t *new_list;
+
+        new_list = serf_bucket_mem_alloc(ctx->ssl_ctx->allocator,
+                                         sizeof(*new_list));
+        new_list->bucket = stream;
+        new_list->next = NULL;
+        if (ctx->ssl_ctx->encrypt.stream_next == NULL) {
+            ctx->ssl_ctx->encrypt.stream_next = new_list;
+        }
+        else {
+            bucket_list_t *scan = ctx->ssl_ctx->encrypt.stream_next;
+
+            while (scan->next != NULL)
+                scan = scan->next;
+            scan->next = new_list;
+        }
+    }
+
+    return ctx->ssl_ctx;
+}
+
+
+static void *serf__openssl_bucket_encrypt_context_get(
+     serf_bucket_t *bucket)
+{
+    ssl_context_t *ctx = bucket->data;
+    return ctx->ssl_ctx;
+}
+
+/* Functions to read a serf_ssl_certificate structure. */
+
+/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). */
+static apr_hash_t *
+convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool)
+{
+    char buf[1024];
+    int ret;
+
+    apr_hash_t *tgt = apr_hash_make(pool);
+
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_commonName,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_pkcs9_emailAddress,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_organizationalUnitName,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_organizationName,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_localityName,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_stateOrProvinceName,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+    ret = X509_NAME_get_text_by_NID(org,
+                                    NID_countryName,
+                                    buf, 1024);
+    if (ret != -1)
+        apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
+
+    return tgt;
+}
+
+
+static apr_hash_t *serf__openssl_cert_issuer(
+    const serf_ssl_certificate_t *cert,
+    apr_pool_t *pool)
+{
+    X509_NAME *issuer = X509_get_issuer_name(cert->impl_cert);
+
+    if (!issuer)
+        return NULL;
+
+    return convert_X509_NAME_to_table(issuer, pool);
+}
+
+
+static apr_hash_t *serf__openssl_cert_subject(
+    const serf_ssl_certificate_t *cert,
+    apr_pool_t *pool)
+{
+    X509_NAME *subject = X509_get_subject_name(cert->impl_cert);
+
+    if (!subject)
+        return NULL;
+
+    return convert_X509_NAME_to_table(subject, pool);
+}
+
+
+static apr_hash_t *serf__openssl_cert_certificate(
+    const serf_ssl_certificate_t *cert,
+    apr_pool_t *pool)
+{
+    apr_hash_t *tgt = apr_hash_make(pool);
+    unsigned int md_size;
+    unsigned char md[EVP_MAX_MD_SIZE];
+    BIO *bio;
+    STACK_OF(GENERAL_NAME) *names;
+    X509 *ssl_cert = cert->impl_cert;
+
+    /* sha1 fingerprint */
+    if (X509_digest(ssl_cert, EVP_sha1(), md, &md_size)) {
+        unsigned int i;
+        const char hex[] = "0123456789ABCDEF";
+        char fingerprint[EVP_MAX_MD_SIZE * 3];
+
+        for (i=0; i<md_size; i++) {
+            fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4];
+            fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)];
+            fingerprint[(3*i)+2] = ':';
+        }
+        if (md_size > 0)
+            fingerprint[(3*(md_size-1))+2] = '\0';
+        else
+            fingerprint[0] = '\0';
+
+        apr_hash_set(tgt, "sha1", APR_HASH_KEY_STRING,
+                     apr_pstrdup(pool, fingerprint));
+    }
+
+    /* set expiry dates */
+    bio = BIO_new(BIO_s_mem());
+    if (bio) {
+        ASN1_TIME *notBefore, *notAfter;
+        char buf[256];
+
+        memset (buf, 0, sizeof (buf));
+        notBefore = X509_get_notBefore(ssl_cert);
+        if (ASN1_TIME_print(bio, notBefore)) {
+            BIO_read(bio, buf, 255);
+            apr_hash_set(tgt, "notBefore", APR_HASH_KEY_STRING,
+                         apr_pstrdup(pool, buf));
+        }
+        memset (buf, 0, sizeof (buf));
+        notAfter = X509_get_notAfter(ssl_cert);
+        if (ASN1_TIME_print(bio, notAfter)) {
+            BIO_read(bio, buf, 255);
+            apr_hash_set(tgt, "notAfter", APR_HASH_KEY_STRING,
+                         apr_pstrdup(pool, buf));
+        }
+    }
+    BIO_free(bio);
+
+    /* Get subjectAltNames */
+    names = X509_get_ext_d2i(ssl_cert, NID_subject_alt_name, NULL, NULL);
+    if (names) {
+        int names_count = sk_GENERAL_NAME_num(names);
+        int name_idx;
+
+        apr_array_header_t *san_arr = apr_array_make(pool, names_count,
+                                                     sizeof(char*));
+        apr_hash_set(tgt, "subjectAltName", APR_HASH_KEY_STRING, san_arr);
+        for (name_idx = 0; name_idx < names_count; name_idx++) {
+            char *p = NULL;
+            GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, name_idx);
+
+            switch (nm->type) {
+            case GEN_DNS:
+                p = apr_pstrmemdup(pool, (const char *)nm->d.ia5->data,
+                                   nm->d.ia5->length);
+                break;
+            default:
+                /* Don't know what to do - skip. */
+                break;
+            }
+            if (p) {
+                APR_ARRAY_PUSH(san_arr, char*) = p;
+            }
+        }
+        sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+    }
+
+    return tgt;
+}
+
+
+static const char *serf__openssl_cert_export(
+    const serf_ssl_certificate_t *cert,
+    apr_pool_t *pool)
+{
+    char *binary_cert;
+    char *encoded_cert;
+    int len;
+    unsigned char *unused;
+
+    /* find the length of the DER encoding. */
+    len = i2d_X509(cert->impl_cert, NULL);
+    if (len < 0) {
+        return NULL;
+    }
+
+    binary_cert = apr_palloc(pool, len);
+    unused = (unsigned char *)binary_cert;
+    len = i2d_X509(cert->impl_cert, &unused);  /* unused is incremented  */
+    if (len < 0) {
+        return NULL;
+    }
+
+    encoded_cert = apr_palloc(pool, apr_base64_encode_len(len));
+    apr_base64_encode(encoded_cert, binary_cert, len);
+    
+    return encoded_cert;
+}
+
+/* Disables compression for all SSL sessions. */
+static void disable_compression(openssl_context_t *ssl_ctx)
+{
+#ifdef SSL_OP_NO_COMPRESSION
+    SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_NO_COMPRESSION);
+#endif
+}
+
+static apr_status_t
+serf__openssl_use_compression(void *impl_ctx, int enabled)
+{
+    openssl_context_t *ssl_ctx = impl_ctx;
+
+    if (enabled) {
+#ifdef SSL_OP_NO_COMPRESSION
+        SSL_clear_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION);
+        return APR_SUCCESS;
+#endif
+    } else {
+#ifdef SSL_OP_NO_COMPRESSION
+        SSL_set_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION);
+        return APR_SUCCESS;
+#endif
+    }
+
+    return APR_EGENERAL;
+}
+
+static void openssl_destroy_and_data(serf_bucket_t *bucket)
+{
+    ssl_context_t *ctx = bucket->data;
+
+    if (!--ctx->ssl_ctx->refcount) {
+        ssl_free_context(ctx->ssl_ctx);
+    }
+
+    serf_default_destroy_and_data(bucket);
+}
+
+static void serf_openssl_decrypt_destroy_and_data(serf_bucket_t *bucket)
+{
+    ssl_context_t *ctx = bucket->data;
+
+    serf_bucket_destroy(*ctx->our_stream);
+
+    openssl_destroy_and_data(bucket);
+}
+
+static void serf_openssl_encrypt_destroy_and_data(serf_bucket_t *bucket)
+{
+    ssl_context_t *ctx = bucket->data;
+    openssl_context_t *ssl_ctx = ctx->ssl_ctx;
+
+    if (ssl_ctx->encrypt.stream == *ctx->our_stream) {
+        serf_bucket_destroy(*ctx->our_stream);
+        serf_bucket_destroy(ssl_ctx->encrypt.pending);
+
+        /* Reset our encrypted status and databuf. */
+        ssl_ctx->encrypt.status = APR_SUCCESS;
+        ssl_ctx->encrypt.databuf.status = APR_SUCCESS;
+
+        /* Advance to the next stream - if we have one. */
+        if (ssl_ctx->encrypt.stream_next == NULL) {
+            ssl_ctx->encrypt.stream = NULL;
+            ssl_ctx->encrypt.pending = NULL;
+        }
+        else {
+            bucket_list_t *cur;
+
+            cur = ssl_ctx->encrypt.stream_next;
+            ssl_ctx->encrypt.stream = cur->bucket;
+            ssl_ctx->encrypt.pending =
+                serf_bucket_aggregate_create(cur->bucket->allocator);
+            ssl_ctx->encrypt.stream_next = cur->next;
+            serf_bucket_mem_free(ssl_ctx->allocator, cur);
+        }
+    }
+    else {
+        /* Ah, darn.  We haven't sent this one along yet. */
+        return;
+    }
+    openssl_destroy_and_data(bucket);
+}
+
+static apr_status_t serf_openssl_read(serf_bucket_t *bucket,
+                                  apr_size_t requested,
+                                  const char **data, apr_size_t *len)
+{
+    ssl_context_t *ctx = bucket->data;
+
+    return serf_databuf_read(ctx->databuf, requested, data, len);
+}
+
+static apr_status_t serf_openssl_readline(serf_bucket_t *bucket,
+                                      int acceptable, int *found,
+                                      const char **data,
+                                      apr_size_t *len)
+{
+    ssl_context_t *ctx = bucket->data;
+
+    return serf_databuf_readline(ctx->databuf, acceptable, found, data, len);
+}
+
+static apr_status_t serf_openssl_peek(serf_bucket_t *bucket,
+                                  const char **data,
+                                  apr_size_t *len)
+{
+    ssl_context_t *ctx = bucket->data;
+
+    return serf_databuf_peek(ctx->databuf, data, len);
+}
+
+
+const serf_bucket_type_t serf_bucket_type_openssl_encrypt = {
+    "OPENSSLENCRYPT",
+    serf_openssl_read,
+    serf_openssl_readline,
+    serf_default_read_iovec,
+    serf_default_read_for_sendfile,
+    serf_default_read_bucket,
+    serf_openssl_peek,
+    serf_openssl_encrypt_destroy_and_data,
+};
+
+const serf_bucket_type_t serf_bucket_type_openssl_decrypt = {
+    "OPENSSLDECRYPT",
+    serf_openssl_read,
+    serf_openssl_readline,
+    serf_default_read_iovec,
+    serf_default_read_for_sendfile,
+    serf_default_read_bucket,
+    serf_openssl_peek,
+    serf_openssl_decrypt_destroy_and_data,
+};
+
+const serf_ssl_bucket_type_t serf_ssl_bucket_type_openssl = {
+    serf__openssl_bucket_decrypt_create,
+    serf__openssl_bucket_decrypt_context_get,
+    serf__openssl_bucket_encrypt_create,
+    serf__openssl_bucket_encrypt_context_get,
+    serf__openssl_set_hostname,
+    serf__openssl_client_cert_provider_set,
+    serf__openssl_identity_provider_set,
+    serf__openssl_client_cert_password_set,
+    serf__openssl_server_cert_callback_set,
+    serf__openssl_server_cert_chain_callback_set,
+    serf__openssl_use_default_certificates,
+    serf__openssl_load_CA_cert_from_file,
+    serf__openssl_load_identity_from_file,
+    serf__openssl_trust_cert,
+    serf__openssl_cert_issuer,
+    serf__openssl_cert_subject,
+    serf__openssl_cert_certificate,
+    serf__openssl_cert_export,
+    serf__openssl_use_compression,
+    serf__openssl_session_export,
+    serf__openssl_session_import,
+    serf__openssl_new_session_callback_set,
+    serf__openssl_resume_session,
+};
+#else
+
+const serf_bucket_type_t serf_bucket_type_openssl_encrypt = {
+    "OPENSSLENCRYPT",
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+const serf_bucket_type_t serf_bucket_type_openssl_decrypt = {
+    "OPENSSLDECRYPT",
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+#endif /* SERF_HAVE_OPENSSL */
diff --git a/buckets/ssl_buckets.c b/buckets/ssl_buckets.c
index d3a5c48..a460bd7 100644
--- a/buckets/ssl_buckets.c
+++ b/buckets/ssl_buckets.c
@@ -1,4 +1,4 @@
-/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
+/* Copyright 2013 Justin Erenkrantz and Greg Stein
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,1458 +11,129 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
- * ----
- *
- * For the OpenSSL thread-safety locking code:
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Originally developed by Aaron Bannert and Justin Erenkrantz, eBuilt.
  */
 
-#include <apr_pools.h>
-#include <apr_network_io.h>
-#include <apr_portable.h>
-#include <apr_strings.h>
-#include <apr_base64.h>
-#include <apr_version.h>
-#include <apr_atomic.h>
-
 #include "serf.h"
 #include "serf_private.h"
 #include "serf_bucket_util.h"
+#include "bucket_private.h"
 
-#include <openssl/bio.h>
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/pkcs12.h>
-#include <openssl/x509v3.h>
+typedef struct serf_ssl_bucket_t {
+    serf_bucket_t bucket;
+    
+    /* context (including the type of this bucket) shared between encrypt and
+       decrypt ssl_bucket */
+    serf_ssl_context_t *ssl_ctx;
 
-#ifndef APR_ARRAY_PUSH
-#define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary)))
-#endif
+    /** the allocator used for this context (needed at destroy time) */
+    serf_bucket_alloc_t *allocator;
+} serf_ssl_bucket_t;
 
-
-/*
- * Here's an overview of the SSL bucket's relationship to OpenSSL and serf.
- *
- * HTTP request:  SSLENCRYPT(REQUEST)
- *   [context.c reads from SSLENCRYPT and writes out to the socket]
- * HTTP response: RESPONSE(SSLDECRYPT(SOCKET))
- *   [handler function reads from RESPONSE which in turn reads from SSLDECRYPT]
- *
- * HTTP request read call path:
- *
- * write_to_connection
- *  |- serf_bucket_read on SSLENCRYPT
- *    |- serf_ssl_read
- *      |- serf_databuf_read
- *        |- common_databuf_prep
- *          |- ssl_encrypt
- *            |- 1. Try to read pending encrypted data; If available, return.
- *            |- 2. Try to read from ctx->stream [REQUEST bucket]
- *            |- 3. Call SSL_write with read data
- *              |- ...
- *                |- bio_bucket_read can be called
- *                |- bio_bucket_write with encrypted data
- *                  |- store in sink
- *            |- 4. If successful, read pending encrypted data and return.
- *            |- 5. If fails, place read data back in ctx->stream
- *
- * HTTP response read call path:
- *
- * read_from_connection
- *  |- acceptor
- *  |- handler
- *    |- ...
- *      |- serf_bucket_read(SSLDECRYPT)
- *        |- serf_ssl_read
- *          |- serf_databuf_read
- *            |- ssl_decrypt
- *              |- 1. SSL_read() for pending decrypted data; if any, return.
- *              |- 2. Try to read from ctx->stream [SOCKET bucket]
- *              |- 3. Append data to ssl_ctx->source
- *              |- 4. Call SSL_read()
- *                |- ...
- *                  |- bio_bucket_write can be called
- *                  |- bio_bucket_read
- *                    |- read data from ssl_ctx->source
- *              |- If data read, return it.
- *              |- If an error, set the STATUS value and return.
- *
- */
-
-typedef struct bucket_list {
-    serf_bucket_t *bucket;
-    struct bucket_list *next;
-} bucket_list_t;
-
-typedef struct {
-    /* Helper to read data. Wraps stream. */
-    serf_databuf_t databuf;
-
-    /* Our source for more data. */
-    serf_bucket_t *stream;
-
-    /* The next set of buckets */
-    bucket_list_t *stream_next;
-
-    /* The status of the last thing we read. */
-    apr_status_t status;
-    apr_status_t exhausted;
-    int exhausted_reset;
-
-    /* Data we've read but not processed. */
-    serf_bucket_t *pending;
-} serf_ssl_stream_t;
-
-struct serf_ssl_context_t {
+struct serf_ssl_context_t
+{
     /* How many open buckets refer to this context. */
     int refcount;
 
-    /* The pool that this context uses. */
-    apr_pool_t *pool;
+    /* Which SSL implementation is used for this context. */
+    const serf_ssl_bucket_type_t *type;
 
-    /* The allocator associated with the above pool. */
-    serf_bucket_alloc_t *allocator;
-
-    /* Internal OpenSSL parameters */
-    SSL_CTX *ctx;
-    SSL *ssl;
-    BIO *bio;
-
-    serf_ssl_stream_t encrypt;
-    serf_ssl_stream_t decrypt;
-
-    /* Client cert callbacks */
-    serf_ssl_need_client_cert_t cert_callback;
-    void *cert_userdata;
-    apr_pool_t *cert_cache_pool;
-    const char *cert_file_success;
-
-    /* Client cert PW callbacks */
-    serf_ssl_need_cert_password_t cert_pw_callback;
-    void *cert_pw_userdata;
-    apr_pool_t *cert_pw_cache_pool;
-    const char *cert_pw_success;
-
-    /* Server cert callbacks */
-    serf_ssl_need_server_cert_t server_cert_callback;
-    serf_ssl_server_cert_chain_cb_t server_cert_chain_callback;
-    void *server_cert_userdata;
-
-    serf_ssl_new_session_t new_session_cb;
-    void *new_session_cb_baton;
-
-    const char *cert_path;
-
-    X509 *cached_cert;
-    EVP_PKEY *cached_cert_pw;
-
-    apr_status_t pending_err;
-
-    /* Status of a fatal error, returned on subsequent encrypt or decrypt
-       requests. */
-    apr_status_t fatal_err;
+    /** implementation specific context */
+    void *impl_ctx;
 };
 
-typedef struct {
-    /* The bucket-independent ssl context that this bucket is associated with */
-    serf_ssl_context_t *ssl_ctx;
-
-    /* Pointer to the 'right' databuf. */
-    serf_databuf_t *databuf;
-
-    /* Pointer to our stream, so we can find it later. */
-    serf_bucket_t **our_stream;
-} ssl_context_t;
-
-struct serf_ssl_certificate_t {
-    X509 *ssl_cert;
-    int depth;
-};
-
-struct serf_ssl_session_t {
-    SSL_SESSION *session_obj;
-};
-
-static void disable_compression(serf_ssl_context_t *ssl_ctx);
-
-#if SSL_VERBOSE
-/* Log all ssl alerts that we receive from the server. */
-static void
-apps_ssl_info_callback(const SSL *s, int where, int ret)
+void *serf__ssl_get_impl_context(serf_ssl_context_t *ssl_ctx)
 {
-    const char *str;
-    int w;
-    w = where & ~SSL_ST_MASK;
-
-    if (w & SSL_ST_CONNECT)
-        str = "SSL_connect";
-    else if (w & SSL_ST_ACCEPT)
-        str = "SSL_accept";
-    else
-        str = "undefined";
-
-    if (where & SSL_CB_LOOP) {
-        serf__log(SSL_VERBOSE, __FILE__, "%s:%s\n", str,
-                  SSL_state_string_long(s));
-    }
-    else if (where & SSL_CB_ALERT) {
-        str = (where & SSL_CB_READ) ? "read" : "write";
-        serf__log(SSL_VERBOSE, __FILE__, "SSL3 alert %s:%s:%s\n",
-               str,
-               SSL_alert_type_string_long(ret),
-               SSL_alert_desc_string_long(ret));
-    }
-    else if (where & SSL_CB_EXIT) {
-        if (ret == 0)
-            serf__log(SSL_VERBOSE, __FILE__, "%s:failed in %s\n", str,
-                      SSL_state_string_long(s));
-        else if (ret < 0) {
-            serf__log(SSL_VERBOSE, __FILE__, "%s:error in %s\n", str,
-                      SSL_state_string_long(s));
-        }
-    }
+    return ssl_ctx->impl_ctx;
 }
+
+static const serf_ssl_bucket_type_t *decide_ssl_bucket_type(void)
+{
+    apr_uint32_t bucket_impls = serf_config_get_bucket_impls();
+
+    /* Prefer SSL implementation integrated in host platform, depending
+       on what's builtin and what the application allows. */
+#ifdef SERF_HAVE_MACOSXSSL
+    if (bucket_impls & SERF_IMPL_SSL_MACOSXSSL)
+        return &serf_ssl_bucket_type_macosxssl;
+#endif
+#ifdef SERF_HAVE_OPENSSL
+    if (bucket_impls & SERF_IMPL_SSL_OPENSSL)
+        return &serf_ssl_bucket_type_openssl;
 #endif
 
-/* Returns the amount read. */
-static int bio_bucket_read(BIO *bio, char *in, int inlen)
-{
-    serf_ssl_context_t *ctx = bio->ptr;
-    const char *data;
-    apr_status_t status;
-    apr_size_t len;
-
-    serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read called for %d bytes\n",
-              inlen);
-
-    if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN
-        && BIO_should_read(ctx->bio)) {
-        serf__log(SSL_VERBOSE, __FILE__,
-                  "bio_bucket_read waiting: (%d %d %d)\n",
-           BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
-           BIO_get_retry_flags(ctx->bio));
-        /* Falling back... */
-        ctx->encrypt.exhausted_reset = 1;
-        BIO_clear_retry_flags(bio);
-    }
-
-    status = serf_bucket_read(ctx->decrypt.pending, inlen, &data, &len);
-
-    ctx->decrypt.status = status;
-
-    serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read received %d bytes (%d)\n",
-              len, status);
-
-    if (!SERF_BUCKET_READ_ERROR(status)) {
-        /* Oh suck. */
-        if (len) {
-            memcpy(in, data, len);
-            return len;
-        }
-        if (APR_STATUS_IS_EOF(status)) {
-            BIO_set_retry_read(bio);
-            return -1;
-        }
-    }
-
-    return -1;
+    return NULL;
 }
 
-/* Returns the amount written. */
-static int bio_bucket_write(BIO *bio, const char *in, int inl)
-{
-    serf_ssl_context_t *ctx = bio->ptr;
-    serf_bucket_t *tmp;
-
-    serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_write called for %d bytes\n",
-              inl);
-
-    if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN
-        && !BIO_should_read(ctx->bio)) {
-        serf__log(SSL_VERBOSE, __FILE__,
-                  "bio_bucket_write waiting: (%d %d %d)\n",
-           BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
-           BIO_get_retry_flags(ctx->bio));
-        /* Falling back... */
-        ctx->encrypt.exhausted_reset = 1;
-        BIO_clear_retry_flags(bio);
-    }
-
-    tmp = serf_bucket_simple_copy_create(in, inl,
-                                         ctx->encrypt.pending->allocator);
-
-    serf_bucket_aggregate_append(ctx->encrypt.pending, tmp);
-
-    return inl;
-}
-
-/* Returns the amount read. */
-static int bio_file_read(BIO *bio, char *in, int inlen)
-{
-    apr_file_t *file = bio->ptr;
-    apr_status_t status;
-    apr_size_t len;
-
-    BIO_clear_retry_flags(bio);
-
-    len = inlen;
-    status = apr_file_read(file, in, &len);
-
-    if (!SERF_BUCKET_READ_ERROR(status)) {
-        /* Oh suck. */
-        if (APR_STATUS_IS_EOF(status)) {
-            BIO_set_retry_read(bio);
-            return -1;
-        } else {
-            return len;
-        }
-    }
-
-    return -1;
-}
-
-/* Returns the amount written. */
-static int bio_file_write(BIO *bio, const char *in, int inl)
-{
-    apr_file_t *file = bio->ptr;
-    apr_size_t nbytes;
-
-    BIO_clear_retry_flags(bio);
-
-    nbytes = inl;
-    apr_file_write(file, in, &nbytes);
-
-    return nbytes;
-}
-
-static int bio_file_gets(BIO *bio, char *in, int inlen)
-{
-    return bio_file_read(bio, in, inlen);
-}
-
-static int bio_bucket_create(BIO *bio)
-{
-    bio->shutdown = 1;
-    bio->init = 1;
-    bio->num = -1;
-    bio->ptr = NULL;
-
-    return 1;
-}
-
-static int bio_bucket_destroy(BIO *bio)
-{
-    /* Did we already free this? */
-    if (bio == NULL) {
-        return 0;
-    }
-
-    return 1;
-}
-
-static long bio_bucket_ctrl(BIO *bio, int cmd, long num, void *ptr)
-{
-    long ret = 1;
-
-    switch (cmd) {
-    default:
-        /* abort(); */
-        break;
-    case BIO_CTRL_FLUSH:
-        /* At this point we can't force a flush. */
-        break;
-    case BIO_CTRL_PUSH:
-    case BIO_CTRL_POP:
-        ret = 0;
-        break;
-    }
-    return ret;
-}
-
-static BIO_METHOD bio_bucket_method = {
-    BIO_TYPE_MEM,
-    "Serf SSL encryption and decryption buckets",
-    bio_bucket_write,
-    bio_bucket_read,
-    NULL,                        /* Is this called? */
-    NULL,                        /* Is this called? */
-    bio_bucket_ctrl,
-    bio_bucket_create,
-    bio_bucket_destroy,
-#ifdef OPENSSL_VERSION_NUMBER
-    NULL /* sslc does not have the callback_ctrl field */
-#endif
-};
-
-static BIO_METHOD bio_file_method = {
-    BIO_TYPE_FILE,
-    "Wrapper around APR file structures",
-    bio_file_write,
-    bio_file_read,
-    NULL,                        /* Is this called? */
-    bio_file_gets,               /* Is this called? */
-    bio_bucket_ctrl,
-    bio_bucket_create,
-    bio_bucket_destroy,
-#ifdef OPENSSL_VERSION_NUMBER
-    NULL /* sslc does not have the callback_ctrl field */
-#endif
-};
-
-static int
-validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
-{
-    SSL *ssl;
-    serf_ssl_context_t *ctx;
-    X509 *server_cert;
-    int err, depth;
-    int failures = 0;
-
-    ssl = X509_STORE_CTX_get_ex_data(store_ctx,
-                                     SSL_get_ex_data_X509_STORE_CTX_idx());
-    ctx = SSL_get_app_data(ssl);
-
-    server_cert = X509_STORE_CTX_get_current_cert(store_ctx);
-    depth = X509_STORE_CTX_get_error_depth(store_ctx);
-
-    /* If the certification was found invalid, get the error and convert it to
-       something our caller will understand. */
-    if (! cert_valid) {
-        err = X509_STORE_CTX_get_error(store_ctx);
-
-        switch(err) {
-            case X509_V_ERR_CERT_NOT_YET_VALID: 
-                    failures |= SERF_SSL_CERT_NOTYETVALID;
-                    break;
-            case X509_V_ERR_CERT_HAS_EXPIRED:
-                    failures |= SERF_SSL_CERT_EXPIRED;
-                    break;
-            case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-            case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-                    failures |= SERF_SSL_CERT_SELF_SIGNED;
-                    break;
-            case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-            case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-            case X509_V_ERR_CERT_UNTRUSTED:
-            case X509_V_ERR_INVALID_CA:
-                    failures |= SERF_SSL_CERT_UNKNOWNCA;
-                    break;
-            case X509_V_ERR_CERT_REVOKED:
-                    failures |= SERF_SSL_CERT_REVOKED;
-                    break;
-            default:
-                    failures |= SERF_SSL_CERT_UNKNOWN_FAILURE;
-                    break;
-        }
-    }
-
-    /* Check certificate expiry dates. */
-    if (X509_cmp_current_time(X509_get_notBefore(server_cert)) >= 0) {
-        failures |= SERF_SSL_CERT_NOTYETVALID;
-    }
-    else if (X509_cmp_current_time(X509_get_notAfter(server_cert)) <= 0) {
-        failures |= SERF_SSL_CERT_EXPIRED;
-    }
-
-    if (ctx->server_cert_callback &&
-        (depth == 0 || failures)) {
-        apr_status_t status;
-        serf_ssl_certificate_t *cert;
-        apr_pool_t *subpool;
-
-        apr_pool_create(&subpool, ctx->pool);
-
-        cert = apr_palloc(subpool, sizeof(serf_ssl_certificate_t));
-        cert->ssl_cert = server_cert;
-        cert->depth = depth;
-
-        /* Callback for further verification. */
-        status = ctx->server_cert_callback(ctx->server_cert_userdata,
-                                           failures, cert);
-        if (status == APR_SUCCESS)
-            cert_valid = 1;
-        else {
-            /* Even if openssl found the certificate valid, the application
-               told us to reject it. */
-            cert_valid = 0;
-            /* Pass the error back to the caller through the context-run. */
-            ctx->pending_err = status;
-        }
-        apr_pool_destroy(subpool);
-    }
-
-    if (ctx->server_cert_chain_callback
-        && (depth == 0 || failures)) {
-        apr_status_t status;
-        STACK_OF(X509) *chain;
-        const serf_ssl_certificate_t **certs;
-        int certs_len;
-        apr_pool_t *subpool;
-
-        apr_pool_create(&subpool, ctx->pool);
-
-        /* Borrow the chain to pass to the callback. */
-        chain = X509_STORE_CTX_get_chain(store_ctx);
-
-        /* If the chain can't be retrieved, just pass the current
-           certificate. */
-        /* ### can this actually happen with _get_chain() ?  */
-        if (!chain) {
-            serf_ssl_certificate_t *cert = apr_palloc(subpool, sizeof(*cert));
-
-            cert->ssl_cert = server_cert;
-            cert->depth = depth;
-
-            /* Room for the server_cert and a trailing NULL.  */
-            certs = apr_palloc(subpool, sizeof(*certs) * 2);
-            certs[0] = cert;
-
-            certs_len = 1;
-        } else {
-            int i;
-
-            certs_len = sk_X509_num(chain);
-
-            /* Room for all the certs and a trailing NULL.  */
-            certs = apr_palloc(subpool, sizeof(*certs) * (certs_len + 1));
-            for (i = 0; i < certs_len; ++i) {
-                serf_ssl_certificate_t *cert;
-
-                cert = apr_palloc(subpool, sizeof(*cert));
-                cert->ssl_cert = sk_X509_value(chain, i);
-                cert->depth = i;
-
-                certs[i] = cert;
-            }
-        }
-        certs[certs_len] = NULL;
-
-        /* Callback for further verification. */
-        status = ctx->server_cert_chain_callback(ctx->server_cert_userdata,
-                                                 failures, depth,
-                                                 certs, certs_len);
-        if (status == APR_SUCCESS) {
-            cert_valid = 1;
-        } else {
-            /* Even if openssl found the certificate valid, the application
-               told us to reject it. */
-            cert_valid = 0;
-            /* Pass the error back to the caller through the context-run. */
-            ctx->pending_err = status;
-        }
-
-        apr_pool_destroy(subpool);
-    }
-
-    /* Return a specific error if the server certificate is not accepted by
-       OpenSSL and the application has not set callbacks to override this. */
-    if (!cert_valid &&
-        !ctx->server_cert_chain_callback &&
-        !ctx->server_cert_callback)
-    {
-        ctx->pending_err = SERF_ERROR_SSL_CERT_FAILED;
-    }
-        
-    return cert_valid;
-}
-
-/* This function reads an encrypted stream and returns the decrypted stream. */
-static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
-                                char *buf, apr_size_t *len)
-{
-    serf_ssl_context_t *ctx = baton;
-    apr_size_t priv_len;
-    apr_status_t status;
-    const char *data;
-    int ssl_len;
-
-    if (ctx->fatal_err)
-        return ctx->fatal_err;
-
-    serf__log(SSL_VERBOSE, __FILE__, "ssl_decrypt: begin %d\n", bufsize);
-
-    /* Is there some data waiting to be read? */
-    ssl_len = SSL_read(ctx->ssl, buf, bufsize);
-    if (ssl_len > 0) {
-        serf__log(SSL_VERBOSE, __FILE__,
-                  "ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n",
-                  ssl_len, bufsize, ctx->decrypt.status,
-                  BIO_get_retry_flags(ctx->bio));
-        *len = ssl_len;
-        return APR_SUCCESS;
-    }
-
-    status = serf_bucket_read(ctx->decrypt.stream, bufsize, &data, &priv_len);
-
-    if (!SERF_BUCKET_READ_ERROR(status) && priv_len) {
-        serf_bucket_t *tmp;
-
-        serf__log(SSL_VERBOSE, __FILE__,
-                  "ssl_decrypt: read %d bytes (%d); status: %d\n",
-                  priv_len, bufsize, status);
-
-        tmp = serf_bucket_simple_copy_create(data, priv_len,
-                                             ctx->decrypt.pending->allocator);
-
-        serf_bucket_aggregate_append(ctx->decrypt.pending, tmp);
-
-        ssl_len = SSL_read(ctx->ssl, buf, bufsize);
-        if (ssl_len < 0) {
-            int ssl_err;
-
-            ssl_err = SSL_get_error(ctx->ssl, ssl_len);
-            switch (ssl_err) {
-            case SSL_ERROR_SYSCALL:
-                *len = 0;
-                status = ctx->decrypt.status;
-                break;
-            case SSL_ERROR_WANT_READ:
-                *len = 0;
-                status = APR_EAGAIN;
-                break;
-            case SSL_ERROR_SSL:
-                *len = 0;
-                if (ctx->pending_err) {
-                    status = ctx->pending_err;
-                    ctx->pending_err = 0;
-                } else {
-                    ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
-                }
-                break;
-            default:
-                *len = 0;
-                ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
-                break;
-            }
-        } else if (ssl_len == 0) {
-            /* The server shut down the connection. */
-            int ssl_err, shutdown;
-            *len = 0;
-
-            /* Check for SSL_RECEIVED_SHUTDOWN */
-            shutdown = SSL_get_shutdown(ctx->ssl);
-            /* Check for SSL_ERROR_ZERO_RETURN */
-            ssl_err = SSL_get_error(ctx->ssl, ssl_len);
-
-            if (shutdown == SSL_RECEIVED_SHUTDOWN &&
-                ssl_err == SSL_ERROR_ZERO_RETURN) {
-                /* The server closed the SSL session. While this doesn't
-                necessary mean the connection is closed, let's close
-                it here anyway.
-                We can optimize this later. */
-                serf__log(SSL_VERBOSE, __FILE__, 
-                          "ssl_decrypt: SSL read error: server"
-                          " shut down connection!\n");
-                status = APR_EOF;
-            } else {
-                /* A fatal error occurred. */
-                ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
-            }
-        } else {
-            *len = ssl_len;
-            serf__log(SSL_MSG_VERBOSE, __FILE__, 
-                      "---\n%.*s\n-(%d)-\n", *len, buf, *len);
-        }
-    }
-    else {
-        *len = 0;
-    }
-    serf__log(SSL_VERBOSE, __FILE__, 
-              "ssl_decrypt: %d %d %d\n", status, *len,
-              BIO_get_retry_flags(ctx->bio));
-
-    return status;
-}
-
-/* This function reads a decrypted stream and returns an encrypted stream. */
-static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
-                                char *buf, apr_size_t *len)
-{
-    const char *data;
-    apr_size_t interim_bufsize;
-    serf_ssl_context_t *ctx = baton;
-    apr_status_t status;
-
-    if (ctx->fatal_err)
-        return ctx->fatal_err;
-
-    serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: begin %d\n", bufsize);
-
-    /* Try to read already encrypted but unread data first. */
-    status = serf_bucket_read(ctx->encrypt.pending, bufsize, &data, len);
-    if (SERF_BUCKET_READ_ERROR(status)) {
-        return status;
-    }
-
-    /* Aha, we read something.  Return that now. */
-    if (*len) {
-        memcpy(buf, data, *len);
-        if (APR_STATUS_IS_EOF(status)) {
-            status = APR_SUCCESS;
-        }
-
-        serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: %d %d %d (quick read)\n",
-                  status, *len, BIO_get_retry_flags(ctx->bio));
-
-        return status;
-    }
-
-    if (BIO_should_retry(ctx->bio) && BIO_should_write(ctx->bio)) {
-        serf__log(SSL_VERBOSE, __FILE__,
-                  "ssl_encrypt: %d %d %d (should write exit)\n",
-                  status, *len, BIO_get_retry_flags(ctx->bio));
-
-        return APR_EAGAIN;
-    }
-
-    /* If we were previously blocked, unblock ourselves now. */
-    if (BIO_should_read(ctx->bio)) {
-        serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: reset %d %d (%d %d %d)\n",
-                  status, ctx->encrypt.status,
-                  BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
-                  BIO_get_retry_flags(ctx->bio));
-
-        ctx->encrypt.status = APR_SUCCESS;
-        ctx->encrypt.exhausted_reset = 0;
-    }
-
-    /* Oh well, read from our stream now. */
-    interim_bufsize = bufsize;
-    do {
-        apr_size_t interim_len;
-
-        if (!ctx->encrypt.status) {
-            struct iovec vecs[64];
-            int vecs_read;
-
-            status = serf_bucket_read_iovec(ctx->encrypt.stream,
-                                            interim_bufsize, 64, vecs,
-                                            &vecs_read);
-
-            if (!SERF_BUCKET_READ_ERROR(status) && vecs_read) {
-                char *vecs_data;
-                int i, cur, vecs_data_len;
-                int ssl_len;
-
-                /* Combine the buffers of the iovec into one buffer, as
-                   that is with SSL_write requires. */
-                vecs_data_len = 0;
-                for (i = 0; i < vecs_read; i++) {
-                    vecs_data_len += vecs[i].iov_len;
-                }
-
-                vecs_data = serf_bucket_mem_alloc(ctx->allocator,
-                                                  vecs_data_len);
-
-                cur = 0;
-                for (i = 0; i < vecs_read; i++) {
-                    memcpy(vecs_data + cur, vecs[i].iov_base, vecs[i].iov_len);
-                    cur += vecs[i].iov_len;
-                }
-
-                interim_bufsize -= vecs_data_len;
-                interim_len = vecs_data_len;
-
-                serf__log(SSL_VERBOSE, __FILE__,
-                          "ssl_encrypt: bucket read %d bytes; "\
-                          "status %d\n", interim_len, status);
-                serf__log(SSL_MSG_VERBOSE, __FILE__, "---\n%.*s\n-(%d)-\n",
-                          interim_len, vecs_data, interim_len);
-
-                /* Stash our status away. */
-                ctx->encrypt.status = status;
-
-                ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len);
-
-                serf__log(SSL_VERBOSE, __FILE__, 
-                          "ssl_encrypt: SSL write: %d\n", ssl_len);
-
-                /* If we failed to write... */
-                if (ssl_len < 0) {
-                    int ssl_err;
-
-                    /* Ah, bugger. We need to put that data back.
-                       Note: use the copy here, we do not own the original iovec
-                       data buffer so it will be freed on next read. */
-                    serf_bucket_t *vecs_copy =
-                        serf_bucket_simple_own_create(vecs_data,
-                                                      vecs_data_len,
-                                                      ctx->allocator);
-                    serf_bucket_aggregate_prepend(ctx->encrypt.stream,
-                                                  vecs_copy);
-
-                    ssl_err = SSL_get_error(ctx->ssl, ssl_len);
-
-                    serf__log(SSL_VERBOSE, __FILE__, 
-                              "ssl_encrypt: SSL write error: %d\n", ssl_err);
-
-                    if (ssl_err == SSL_ERROR_SYSCALL) {
-                        status = ctx->encrypt.status;
-                        if (SERF_BUCKET_READ_ERROR(status)) {
-                            return status;
-                        }
-                    }
-                    else {
-                        /* Oh, no. */
-                        if (ssl_err == SSL_ERROR_WANT_READ) {
-                            status = SERF_ERROR_WAIT_CONN;
-                        }
-                        else {
-                            ctx->fatal_err = status =
-                                SERF_ERROR_SSL_COMM_FAILED;
-                        }
-                    }
-
-                    serf__log(SSL_VERBOSE, __FILE__, 
-                              "ssl_encrypt: SSL write error: %d %d\n",
-                              status, *len);
-                } else {
-                    /* We're done with this data. */
-                    serf_bucket_mem_free(ctx->allocator, vecs_data);
-                }
-            }
-        }
-        else {
-            interim_len = 0;
-            *len = 0;
-            status = ctx->encrypt.status;
-        }
-
-    } while (!status && interim_bufsize);
-
-    /* Okay, we exhausted our underlying stream. */
-    if (!SERF_BUCKET_READ_ERROR(status)) {
-        apr_status_t agg_status;
-        struct iovec vecs[64];
-        int vecs_read, i;
-
-        /* We read something! */
-        agg_status = serf_bucket_read_iovec(ctx->encrypt.pending, bufsize,
-                                            64, vecs, &vecs_read);
-        *len = 0;
-        for (i = 0; i < vecs_read; i++) {
-            memcpy(buf + *len, vecs[i].iov_base, vecs[i].iov_len);
-            *len += vecs[i].iov_len;
-        }
-
-        serf__log(SSL_VERBOSE, __FILE__,
-                  "ssl_encrypt read agg: %d %d %d %d\n", status, agg_status,
-            ctx->encrypt.status, *len);
-
-        if (!agg_status) {
-            status = agg_status;
-        }
-    }
-
-    if (status == SERF_ERROR_WAIT_CONN
-        && BIO_should_retry(ctx->bio) && BIO_should_read(ctx->bio)) {
-        ctx->encrypt.exhausted = ctx->encrypt.status;
-        ctx->encrypt.status = SERF_ERROR_WAIT_CONN;
-    }
-
-    serf__log(SSL_VERBOSE, __FILE__,
-              "ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len,
-              BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
-              BIO_get_retry_flags(ctx->bio));
-
-    return status;
-}
-
-#if APR_HAS_THREADS
-static apr_pool_t *ssl_pool;
-static apr_thread_mutex_t **ssl_locks;
-
-typedef struct CRYPTO_dynlock_value {
-    apr_thread_mutex_t *lock;
-} CRYPTO_dynlock_value;
-
-static CRYPTO_dynlock_value *ssl_dyn_create(const char* file, int line)
-{
-    CRYPTO_dynlock_value *l;
-    apr_status_t rv;
-
-    l = apr_palloc(ssl_pool, sizeof(CRYPTO_dynlock_value));
-    rv = apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, ssl_pool);
-    if (rv != APR_SUCCESS) {
-        /* FIXME: return error here */
-    }
-    return l;
-}
-
-static void ssl_dyn_lock(int mode, CRYPTO_dynlock_value *l, const char *file,
-                         int line)
-{
-    if (mode & CRYPTO_LOCK) {
-        apr_thread_mutex_lock(l->lock);
-    }
-    else if (mode & CRYPTO_UNLOCK) {
-        apr_thread_mutex_unlock(l->lock);
-    }
-}
-
-static void ssl_dyn_destroy(CRYPTO_dynlock_value *l, const char *file,
-                            int line)
-{
-    apr_thread_mutex_destroy(l->lock);
-}
-
-static void ssl_lock(int mode, int n, const char *file, int line)
-{
-    if (mode & CRYPTO_LOCK) {
-        apr_thread_mutex_lock(ssl_locks[n]);
-    }
-    else if (mode & CRYPTO_UNLOCK) {
-        apr_thread_mutex_unlock(ssl_locks[n]);
-    }
-}
-
-static unsigned long ssl_id(void)
-{
-    /* FIXME: This is lame and not portable. -aaron */
-    return (unsigned long) apr_os_thread_current();
-}
-
-static apr_status_t cleanup_ssl(void *data)
-{
-    CRYPTO_set_locking_callback(NULL);
-    CRYPTO_set_id_callback(NULL);
-    CRYPTO_set_dynlock_create_callback(NULL);
-    CRYPTO_set_dynlock_lock_callback(NULL);
-    CRYPTO_set_dynlock_destroy_callback(NULL);
-
-    return APR_SUCCESS;
-}
-
-#endif
-
-static apr_uint32_t have_init_ssl = 0;
-
-static void init_ssl_libraries(void)
-{
-    apr_uint32_t val;
-#if APR_VERSION_AT_LEAST(1,0,0)
-    val = apr_atomic_xchg32(&have_init_ssl, 1);
-#else
-    val = apr_atomic_cas(&have_init_ssl, 1, 0);
-#endif
-
-    if (!val) {
-#if APR_HAS_THREADS
-        int i, numlocks;
-#endif
-
-#ifdef SSL_VERBOSE
-        /* Warn when compile-time and run-time version of OpenSSL differ in
-           major/minor version number. */
-        long libver = SSLeay();
-
-        if ((libver ^ OPENSSL_VERSION_NUMBER) & 0xFFF00000) {
-            serf__log(SSL_VERBOSE, __FILE__,
-                      "Warning: OpenSSL library version mismatch, compile-time "
-                      "was %lx, runtime is %lx.\n",
-                      OPENSSL_VERSION_NUMBER, libver);
-        }
-#endif
-
-        CRYPTO_malloc_init();
-        ERR_load_crypto_strings();
-        SSL_load_error_strings();
-        SSL_library_init();
-        OpenSSL_add_all_algorithms();
-
-#if APR_HAS_THREADS
-        numlocks = CRYPTO_num_locks();
-        apr_pool_create(&ssl_pool, NULL);
-        ssl_locks = apr_palloc(ssl_pool, sizeof(apr_thread_mutex_t*)*numlocks);
-        for (i = 0; i < numlocks; i++) {
-            apr_status_t rv;
-
-            /* Intraprocess locks don't /need/ a filename... */
-            rv = apr_thread_mutex_create(&ssl_locks[i],
-                                         APR_THREAD_MUTEX_DEFAULT, ssl_pool);
-            if (rv != APR_SUCCESS) {
-                /* FIXME: error out here */
-            }
-        }
-        CRYPTO_set_locking_callback(ssl_lock);
-        CRYPTO_set_id_callback(ssl_id);
-        CRYPTO_set_dynlock_create_callback(ssl_dyn_create);
-        CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock);
-        CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy);
-
-        apr_pool_cleanup_register(ssl_pool, NULL, cleanup_ssl, cleanup_ssl);
-#endif
-    }
-}
-
-static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
-{
-    serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
-    apr_status_t status;
-
-    if (ctx->cached_cert) {
-        *cert = ctx->cached_cert;
-        *pkey = ctx->cached_cert_pw;
-        return 1;
-    }
-
-    while (ctx->cert_callback) {
-        const char *cert_path;
-        apr_file_t *cert_file;
-        BIO *bio;
-        PKCS12 *p12;
-        int i;
-        int retrying_success = 0;
-
-        if (ctx->cert_file_success) {
-            status = APR_SUCCESS;
-            cert_path = ctx->cert_file_success;
-            ctx->cert_file_success = NULL;
-            retrying_success = 1;
-        } else {
-            status = ctx->cert_callback(ctx->cert_userdata, &cert_path);
-        }
-
-        if (status || !cert_path) {
-            break;
-        }
-
-        /* Load the x.509 cert file stored in PKCS12 */
-        status = apr_file_open(&cert_file, cert_path, APR_READ, APR_OS_DEFAULT,
-                               ctx->pool);
-
-        if (status) {
-            continue;
-        }
-
-        bio = BIO_new(&bio_file_method);
-        bio->ptr = cert_file;
-
-        ctx->cert_path = cert_path;
-        p12 = d2i_PKCS12_bio(bio, NULL);
-        apr_file_close(cert_file);
-
-        i = PKCS12_parse(p12, NULL, pkey, cert, NULL);
-
-        if (i == 1) {
-            PKCS12_free(p12);
-            ctx->cached_cert = *cert;
-            ctx->cached_cert_pw = *pkey;
-            if (!retrying_success && ctx->cert_cache_pool) {
-                const char *c;
-
-                c = apr_pstrdup(ctx->cert_cache_pool, ctx->cert_path);
-
-                apr_pool_userdata_setn(c, "serf:ssl:cert",
-                                       apr_pool_cleanup_null,
-                                       ctx->cert_cache_pool);
-            }
-            return 1;
-        }
-        else {
-            int err = ERR_get_error();
-            ERR_clear_error();
-            if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
-                ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
-                if (ctx->cert_pw_callback) {
-                    const char *password;
-
-                    if (ctx->cert_pw_success) {
-                        status = APR_SUCCESS;
-                        password = ctx->cert_pw_success;
-                        ctx->cert_pw_success = NULL;
-                    } else {
-                        status = ctx->cert_pw_callback(ctx->cert_pw_userdata,
-                                                       ctx->cert_path,
-                                                       &password);
-                    }
-
-                    if (!status && password) {
-                        i = PKCS12_parse(p12, password, pkey, cert, NULL);
-                        if (i == 1) {
-                            PKCS12_free(p12);
-                            ctx->cached_cert = *cert;
-                            ctx->cached_cert_pw = *pkey;
-                            if (!retrying_success && ctx->cert_cache_pool) {
-                                const char *c;
-
-                                c = apr_pstrdup(ctx->cert_cache_pool,
-                                                ctx->cert_path);
-
-                                apr_pool_userdata_setn(c, "serf:ssl:cert",
-                                                       apr_pool_cleanup_null,
-                                                       ctx->cert_cache_pool);
-                            }
-                            if (!retrying_success && ctx->cert_pw_cache_pool) {
-                                const char *c;
-
-                                c = apr_pstrdup(ctx->cert_pw_cache_pool,
-                                                password);
-
-                                apr_pool_userdata_setn(c, "serf:ssl:certpw",
-                                                       apr_pool_cleanup_null,
-                                                       ctx->cert_pw_cache_pool);
-                            }
-                            return 1;
-                        }
-                    }
-                }
-                PKCS12_free(p12);
-                return 0;
-            }
-            else {
-                printf("OpenSSL cert error: %d %d %d\n", ERR_GET_LIB(err),
-                       ERR_GET_FUNC(err),
-                       ERR_GET_REASON(err));
-                PKCS12_free(p12);
-            }
-        }
-    }
-
-    return 0;
-}
-
-
 void serf_ssl_client_cert_provider_set(
-    serf_ssl_context_t *context,
+    serf_ssl_context_t *ssl_ctx,
     serf_ssl_need_client_cert_t callback,
     void *data,
     void *cache_pool)
 {
-    context->cert_callback = callback;
-    context->cert_userdata = data;
-    context->cert_cache_pool = cache_pool;
-    if (context->cert_cache_pool) {
-        apr_pool_userdata_get((void**)&context->cert_file_success,
-                              "serf:ssl:cert", cache_pool);
-    }
+    return ssl_ctx->type->client_cert_provider_set(ssl_ctx->impl_ctx, callback,
+                                                   data, cache_pool);
 }
 
-
 void serf_ssl_client_cert_password_set(
-    serf_ssl_context_t *context,
+    serf_ssl_context_t *ssl_ctx,
     serf_ssl_need_cert_password_t callback,
     void *data,
     void *cache_pool)
 {
-    context->cert_pw_callback = callback;
-    context->cert_pw_userdata = data;
-    context->cert_pw_cache_pool = cache_pool;
-    if (context->cert_pw_cache_pool) {
-        apr_pool_userdata_get((void**)&context->cert_pw_success,
-                              "serf:ssl:certpw", cache_pool);
-    }
+    return ssl_ctx->type->client_cert_password_set(ssl_ctx->impl_ctx, callback,
+                                                   data, cache_pool);
 }
 
+void serf_ssl_identity_provider_set(serf_ssl_context_t *ssl_ctx,
+                                    serf_ssl_need_identity_t callback,
+                                    void *data,
+                                    void *cache_pool)
+{
+    return ssl_ctx->type->identity_provider_set(ssl_ctx->impl_ctx, callback,
+                                                data, cache_pool);
+}
+
+void serf_ssl_identity_password_callback_set(
+    serf_ssl_context_t *ssl_ctx,
+    serf_ssl_need_cert_password_t callback,
+    void *data,
+    void *cache_pool)
+{
+    return ssl_ctx->type->client_cert_password_set(ssl_ctx->impl_ctx, callback,
+                                                   data, cache_pool);
+}
 
 void serf_ssl_server_cert_callback_set(
-    serf_ssl_context_t *context,
+    serf_ssl_context_t *ssl_ctx,
     serf_ssl_need_server_cert_t callback,
     void *data)
 {
-    context->server_cert_callback = callback;
-    context->server_cert_userdata = data;
+    return ssl_ctx->type->server_cert_callback_set(ssl_ctx->impl_ctx,
+                                                   callback, data);
 }
 
 void serf_ssl_server_cert_chain_callback_set(
-    serf_ssl_context_t *context,
+    serf_ssl_context_t *ssl_ctx,
     serf_ssl_need_server_cert_t cert_callback,
     serf_ssl_server_cert_chain_cb_t cert_chain_callback,
     void *data)
 {
-    context->server_cert_callback = cert_callback;
-    context->server_cert_chain_callback = cert_chain_callback;
-    context->server_cert_userdata = data;
+    return ssl_ctx->type->server_cert_chain_callback_set(ssl_ctx->impl_ctx,
+                                                         cert_callback,
+                                                         cert_chain_callback,
+                                                         data);
 }
 
-void serf_ssl_new_session_callback_set(
-    serf_ssl_context_t *context,
-    serf_ssl_new_session_t new_session_cb,
-    void *baton)
-{
-    context->new_session_cb = new_session_cb;
-    context->new_session_cb_baton = baton;
-}
-
-static int new_session(SSL *ssl, SSL_SESSION *sess)
-{
-    serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
-
-    if (ctx->new_session_cb) {
-        serf_ssl_session_t session;
-        apr_pool_t *subpool;
-
-        session.session_obj = sess;
-        apr_pool_create(&subpool, ctx->pool);
-
-        ctx->new_session_cb(&session, ctx->new_session_cb_baton, subpool);
-
-        apr_pool_destroy(subpool);
-    }
-
-    return 0;
-}
-
-apr_status_t serf_ssl_session_export(void **data_p,
-                                     apr_size_t *len_p,
-                                     const serf_ssl_session_t *session,
-                                     apr_pool_t *pool)
-{
-    int sess_len;
-    void *sess_data;
-    unsigned char *unused;
-
-    sess_len = i2d_SSL_SESSION(session->session_obj, NULL);
-    if (!sess_len) {
-        return APR_EGENERAL;
-    }
-
-    sess_data = apr_palloc(pool, sess_len);
-
-    unused = sess_data;
-    /* unused is incremented  */
-    sess_len = i2d_SSL_SESSION(session->session_obj, &unused); 
-    if (!sess_len) {
-        return APR_EGENERAL;
-    }
-
-    *data_p = sess_data;
-    *len_p = sess_len;
-    return APR_SUCCESS;
-}
-
-static apr_status_t cleanup_session(void *data)
-{
-    serf_ssl_session_t *session = data;
-
-    SSL_SESSION_free(session->session_obj);
-    session->session_obj = NULL;
-
-    return APR_SUCCESS;
-}
-
-apr_status_t serf_ssl_session_import(const serf_ssl_session_t **session_p,
-                                     void *data,
-                                     apr_size_t len,
-                                     apr_pool_t *pool)
-{
-    SSL_SESSION *sess;
-    serf_ssl_session_t *session;
-    const unsigned char *unused;
-
-    unused = data;
-    sess = d2i_SSL_SESSION(NULL, &unused, len); /* unused is incremented  */
-
-    if (!sess) {
-        return APR_EGENERAL;
-    }
-
-    session = apr_pcalloc(pool, sizeof(serf_ssl_session_t));
-    session->session_obj = sess;
-    apr_pool_cleanup_register(pool, session, cleanup_session, cleanup_session);
-
-    *session_p = session;
-    return APR_SUCCESS;
-}
-
-static serf_ssl_context_t *ssl_init_context(void)
-{
-    serf_ssl_context_t *ssl_ctx;
-    apr_pool_t *pool;
-    serf_bucket_alloc_t *allocator;
-
-    init_ssl_libraries();
-
-    apr_pool_create(&pool, NULL);
-    allocator = serf_bucket_allocator_create(pool, NULL, NULL);
-
-    ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
-
-    ssl_ctx->refcount = 0;
-    ssl_ctx->pool = pool;
-    ssl_ctx->allocator = allocator;
-
-    ssl_ctx->ctx = SSL_CTX_new(SSLv23_client_method());
-
-    SSL_CTX_set_client_cert_cb(ssl_ctx->ctx, ssl_need_client_cert);
-    ssl_ctx->cached_cert = 0;
-    ssl_ctx->cached_cert_pw = 0;
-    ssl_ctx->pending_err = APR_SUCCESS;
-    ssl_ctx->fatal_err = APR_SUCCESS;
-
-    ssl_ctx->cert_callback = NULL;
-    ssl_ctx->cert_pw_callback = NULL;
-    ssl_ctx->server_cert_callback = NULL;
-    ssl_ctx->server_cert_chain_callback = NULL;
-
-    SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER,
-                       validate_server_certificate);
-    SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL);
-
-    ssl_ctx->new_session_cb = NULL;
-
-    /* Enable SSL callback for new sessions and disable internal session
-       handling. */
-    SSL_CTX_set_session_cache_mode(
-        ssl_ctx->ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
-    SSL_CTX_sess_set_new_cb(ssl_ctx->ctx, new_session);
-
-    /* Disable SSL compression by default. */
-    disable_compression(ssl_ctx);
-
-    ssl_ctx->ssl = SSL_new(ssl_ctx->ctx);
-    ssl_ctx->bio = BIO_new(&bio_bucket_method);
-    ssl_ctx->bio->ptr = ssl_ctx;
-
-    SSL_set_bio(ssl_ctx->ssl, ssl_ctx->bio, ssl_ctx->bio);
-
-    SSL_set_connect_state(ssl_ctx->ssl);
-
-    SSL_set_app_data(ssl_ctx->ssl, ssl_ctx);
-
-#if SSL_VERBOSE
-    SSL_CTX_set_info_callback(ssl_ctx->ctx, apps_ssl_info_callback);
-#endif
-
-    ssl_ctx->encrypt.stream = NULL;
-    ssl_ctx->encrypt.stream_next = NULL;
-    ssl_ctx->encrypt.pending = serf_bucket_aggregate_create(allocator);
-    ssl_ctx->encrypt.status = APR_SUCCESS;
-    serf_databuf_init(&ssl_ctx->encrypt.databuf);
-    ssl_ctx->encrypt.databuf.read = ssl_encrypt;
-    ssl_ctx->encrypt.databuf.read_baton = ssl_ctx;
-
-    ssl_ctx->decrypt.stream = NULL;
-    ssl_ctx->decrypt.pending = serf_bucket_aggregate_create(allocator);
-    ssl_ctx->decrypt.status = APR_SUCCESS;
-    serf_databuf_init(&ssl_ctx->decrypt.databuf);
-    ssl_ctx->decrypt.databuf.read = ssl_decrypt;
-    ssl_ctx->decrypt.databuf.read_baton = ssl_ctx;
-
-    return ssl_ctx;
-}
-
-apr_status_t serf_ssl_resume_session(serf_ssl_context_t *ssl_ctx,
-                                     const serf_ssl_session_t *session,
-                                     apr_pool_t *pool)
-{
-    SSL_set_session(ssl_ctx->ssl, session->session_obj);
-    return APR_SUCCESS;
-}
-
-static apr_status_t ssl_free_context(
-    serf_ssl_context_t *ssl_ctx)
-{
-    apr_pool_t *p;
-
-    /* If never had the pending buckets, don't try to free them. */
-    if (ssl_ctx->decrypt.pending != NULL) {
-        serf_bucket_destroy(ssl_ctx->decrypt.pending);
-    }
-    if (ssl_ctx->encrypt.pending != NULL) {
-        serf_bucket_destroy(ssl_ctx->encrypt.pending);
-    }
-
-    /* SSL_free implicitly frees the underlying BIO. */
-    SSL_free(ssl_ctx->ssl);
-    SSL_CTX_free(ssl_ctx->ctx);
-
-    p = ssl_ctx->pool;
-
-    serf_bucket_mem_free(ssl_ctx->allocator, ssl_ctx);
-    apr_pool_destroy(p);
-
-    return APR_SUCCESS;
-}
-
-static serf_bucket_t * serf_bucket_ssl_create(
-    serf_ssl_context_t *ssl_ctx,
-    serf_bucket_alloc_t *allocator,
-    const serf_bucket_type_t *type)
-{
-    ssl_context_t *ctx;
-
-    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
-    if (!ssl_ctx) {
-        ctx->ssl_ctx = ssl_init_context();
-    }
-    else {
-        ctx->ssl_ctx = ssl_ctx;
-    }
-    ctx->ssl_ctx->refcount++;
-
-    return serf_bucket_create(type, allocator, ctx);
-}
-
-apr_status_t serf_ssl_set_hostname(serf_ssl_context_t *context,
+apr_status_t serf_ssl_set_hostname(serf_ssl_context_t *ssl_ctx,
                                    const char * hostname)
 {
-#ifdef SSL_set_tlsext_host_name
-    if (SSL_set_tlsext_host_name(context->ssl, hostname) != 1) {
-        ERR_clear_error();
-    }
-#endif
-    return APR_SUCCESS;
+    return ssl_ctx->type->set_hostname(ssl_ctx->impl_ctx, hostname);
 }
 
-apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx)
+apr_status_t
+serf_ssl_use_compression(serf_ssl_context_t *ssl_ctx, int enabled)
 {
-    X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx);
-
-    int result = X509_STORE_set_default_paths(store);
-
-    return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED;
-}
-
-apr_status_t serf_ssl_load_cert_file(
-    serf_ssl_certificate_t **cert,
-    const char *file_path,
-    apr_pool_t *pool)
-{
-    FILE *fp = fopen(file_path, "r");
-
-    if (fp) {
-        X509 *ssl_cert = PEM_read_X509(fp, NULL, NULL, NULL);
-        fclose(fp);
-
-        if (ssl_cert) {
-            *cert = apr_palloc(pool, sizeof(serf_ssl_certificate_t));
-            (*cert)->ssl_cert = ssl_cert;
-
-            return APR_SUCCESS;
-        }
-    }
-
-    return SERF_ERROR_SSL_CERT_FAILED;
-}
-
-
-apr_status_t serf_ssl_trust_cert(
-    serf_ssl_context_t *ssl_ctx,
-    serf_ssl_certificate_t *cert)
-{
-    X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx);
-
-    int result = X509_STORE_add_cert(store, cert->ssl_cert);
-
-    return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED;
+    return ssl_ctx->type->use_compression(ssl_ctx->impl_ctx, enabled);
 }
 
 
@@ -1471,30 +142,41 @@
     serf_ssl_context_t *ssl_ctx,
     serf_bucket_alloc_t *allocator)
 {
-    serf_bucket_t *bkt;
-    ssl_context_t *ctx;
+    const serf_ssl_bucket_type_t *type = decide_ssl_bucket_type();
+    serf_ssl_bucket_t *ssl_bkt;
 
-    bkt = serf_bucket_ssl_create(ssl_ctx, allocator,
-                                 &serf_bucket_type_ssl_decrypt);
-
-    ctx = bkt->data;
-
-    ctx->databuf = &ctx->ssl_ctx->decrypt.databuf;
-    if (ctx->ssl_ctx->decrypt.stream != NULL) {
+    /* If no SSL implementation is available or allowed, we can't create
+       a bucket. */
+    if (!type)
         return NULL;
-    }
-    ctx->ssl_ctx->decrypt.stream = stream;
-    ctx->our_stream = &ctx->ssl_ctx->decrypt.stream;
 
-    return bkt;
+    ssl_bkt = serf_bucket_mem_alloc(allocator, sizeof(*ssl_bkt));
+    ssl_bkt->allocator = allocator;
+
+    if (!ssl_ctx) {
+        ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
+        ssl_ctx->type = type;
+        ssl_ctx->refcount = 0;
+        ssl_ctx->impl_ctx = NULL;
+    }
+
+    ssl_ctx->impl_ctx = type->decrypt_create(&ssl_bkt->bucket,
+                                             stream,
+                                             ssl_ctx->impl_ctx,
+                                             allocator);
+    ssl_ctx->refcount++;
+    ssl_bkt->ssl_ctx = ssl_ctx;
+
+    return (serf_bucket_t*)ssl_bkt;
 }
 
 
 serf_ssl_context_t *serf_bucket_ssl_decrypt_context_get(
      serf_bucket_t *bucket)
 {
-    ssl_context_t *ctx = bucket->data;
-    return ctx->ssl_ctx;
+    serf_ssl_bucket_t *ssl_bucket = (serf_ssl_bucket_t *)bucket;
+
+    return ssl_bucket->ssl_ctx;
 }
 
 
@@ -1503,376 +185,207 @@
     serf_ssl_context_t *ssl_ctx,
     serf_bucket_alloc_t *allocator)
 {
-    serf_bucket_t *bkt;
-    ssl_context_t *ctx;
+    const serf_ssl_bucket_type_t *type = decide_ssl_bucket_type();
+    serf_ssl_bucket_t *ssl_bkt;
 
-    bkt = serf_bucket_ssl_create(ssl_ctx, allocator,
-                                 &serf_bucket_type_ssl_encrypt);
+    /* If no SSL implementation is available or allowed, we can't create
+     a bucket. */
+    if (!type)
+        return NULL;
 
-    ctx = bkt->data;
+    ssl_bkt = serf_bucket_mem_alloc(allocator, sizeof(*ssl_bkt));
+    ssl_bkt->allocator = allocator;
 
-    ctx->databuf = &ctx->ssl_ctx->encrypt.databuf;
-    ctx->our_stream = &ctx->ssl_ctx->encrypt.stream;
-    if (ctx->ssl_ctx->encrypt.stream == NULL) {
-        serf_bucket_t *tmp = serf_bucket_aggregate_create(stream->allocator);
-        serf_bucket_aggregate_append(tmp, stream);
-        ctx->ssl_ctx->encrypt.stream = tmp;
-    }
-    else {
-        bucket_list_t *new_list;
-
-        new_list = serf_bucket_mem_alloc(ctx->ssl_ctx->allocator,
-                                         sizeof(*new_list));
-        new_list->bucket = stream;
-        new_list->next = NULL;
-        if (ctx->ssl_ctx->encrypt.stream_next == NULL) {
-            ctx->ssl_ctx->encrypt.stream_next = new_list;
-        }
-        else {
-            bucket_list_t *scan = ctx->ssl_ctx->encrypt.stream_next;
-
-            while (scan->next != NULL)
-                scan = scan->next;
-            scan->next = new_list;
-        }
+    if (!ssl_ctx) {
+        ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
+        ssl_ctx->type = type;
+        ssl_ctx->refcount = 0;
+        ssl_ctx->impl_ctx = NULL;
     }
 
-    return bkt;
+    ssl_ctx->impl_ctx = type->encrypt_create(&ssl_bkt->bucket,
+                                             stream,
+                                             ssl_ctx->impl_ctx,
+                                             allocator);
+    ssl_ctx->refcount++;
+    ssl_bkt->ssl_ctx = ssl_ctx;
+
+    return (serf_bucket_t*)ssl_bkt;
 }
 
 
-serf_ssl_context_t *serf_bucket_ssl_encrypt_context_get(
-     serf_bucket_t *bucket)
+serf_ssl_context_t *
+serf_bucket_ssl_encrypt_context_get(serf_bucket_t *bucket)
 {
-    ssl_context_t *ctx = bucket->data;
-    return ctx->ssl_ctx;
+    serf_ssl_bucket_t *ssl_bucket = (serf_ssl_bucket_t *)bucket;
+
+    return ssl_bucket->ssl_ctx;
+}
+
+void
+serf_bucket_ssl_destroy_and_data(serf_bucket_t *bucket)
+{
+    serf_ssl_bucket_t *ssl_bucket = (serf_ssl_bucket_t *)bucket;
+    serf_ssl_context_t *ssl_ctx = ssl_bucket->ssl_ctx;
+
+    if (!--ssl_ctx->refcount) {
+        serf_bucket_mem_free(ssl_bucket->allocator, ssl_ctx);
+    }
+}
+
+apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx)
+{
+    return ssl_ctx->type->use_default_certificates(ssl_ctx->impl_ctx);
+}
+
+
+
+apr_status_t serf_ssl_trust_cert(serf_ssl_context_t *ssl_ctx,
+                                 serf_ssl_certificate_t *cert)
+{
+    return ssl_ctx->type->trust_cert(ssl_ctx->impl_ctx, cert);
+}
+
+/* Create a implementation-independent serf_ssl_certificate_t object */
+serf_ssl_certificate_t *
+serf__create_certificate(serf_bucket_alloc_t *allocator,
+                         const serf_ssl_bucket_type_t *type,
+                         void *impl_cert,
+                         int depth_of_error)
+{
+    serf_ssl_certificate_t *cert;
+
+    cert = serf_bucket_mem_alloc(allocator,
+                                 sizeof(serf_ssl_certificate_t));
+    cert->impl_cert = impl_cert;
+    cert->type = type;
+    cert->depth_of_error = depth_of_error;
+
+    return cert;
 }
 
 /* Functions to read a serf_ssl_certificate structure. */
-
-/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). */
-static apr_hash_t *
-convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool)
-{
-    char buf[1024];
-    int ret;
-
-    apr_hash_t *tgt = apr_hash_make(pool);
-
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_commonName,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_pkcs9_emailAddress,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_organizationalUnitName,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_organizationName,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_localityName,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_stateOrProvinceName,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-    ret = X509_NAME_get_text_by_NID(org,
-                                    NID_countryName,
-                                    buf, 1024);
-    if (ret != -1)
-        apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf));
-
-    return tgt;
-}
-
-
 int serf_ssl_cert_depth(const serf_ssl_certificate_t *cert)
 {
-    return cert->depth;
+    return cert->depth_of_error;
 }
 
-
 apr_hash_t *serf_ssl_cert_issuer(
     const serf_ssl_certificate_t *cert,
     apr_pool_t *pool)
 {
-    X509_NAME *issuer = X509_get_issuer_name(cert->ssl_cert);
-
-    if (!issuer)
-        return NULL;
-
-    return convert_X509_NAME_to_table(issuer, pool);
+    return cert->type->cert_issuer(cert, pool);
 }
 
-
 apr_hash_t *serf_ssl_cert_subject(
     const serf_ssl_certificate_t *cert,
     apr_pool_t *pool)
 {
-    X509_NAME *subject = X509_get_subject_name(cert->ssl_cert);
-
-    if (!subject)
-        return NULL;
-
-    return convert_X509_NAME_to_table(subject, pool);
+    return cert->type->cert_subject(cert, pool);
 }
 
-
 apr_hash_t *serf_ssl_cert_certificate(
     const serf_ssl_certificate_t *cert,
     apr_pool_t *pool)
 {
-    apr_hash_t *tgt = apr_hash_make(pool);
-    unsigned int md_size;
-    unsigned char md[EVP_MAX_MD_SIZE];
-    BIO *bio;
-    STACK_OF(GENERAL_NAME) *names;
-
-    /* sha1 fingerprint */
-    if (X509_digest(cert->ssl_cert, EVP_sha1(), md, &md_size)) {
-        unsigned int i;
-        const char hex[] = "0123456789ABCDEF";
-        char fingerprint[EVP_MAX_MD_SIZE * 3];
-
-        for (i=0; i<md_size; i++) {
-            fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4];
-            fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)];
-            fingerprint[(3*i)+2] = ':';
-        }
-        if (md_size > 0)
-            fingerprint[(3*(md_size-1))+2] = '\0';
-        else
-            fingerprint[0] = '\0';
-
-        apr_hash_set(tgt, "sha1", APR_HASH_KEY_STRING,
-                     apr_pstrdup(pool, fingerprint));
-    }
-
-    /* set expiry dates */
-    bio = BIO_new(BIO_s_mem());
-    if (bio) {
-        ASN1_TIME *notBefore, *notAfter;
-        char buf[256];
-
-        memset (buf, 0, sizeof (buf));
-        notBefore = X509_get_notBefore(cert->ssl_cert);
-        if (ASN1_TIME_print(bio, notBefore)) {
-            BIO_read(bio, buf, 255);
-            apr_hash_set(tgt, "notBefore", APR_HASH_KEY_STRING,
-                         apr_pstrdup(pool, buf));
-        }
-        memset (buf, 0, sizeof (buf));
-        notAfter = X509_get_notAfter(cert->ssl_cert);
-        if (ASN1_TIME_print(bio, notAfter)) {
-            BIO_read(bio, buf, 255);
-            apr_hash_set(tgt, "notAfter", APR_HASH_KEY_STRING,
-                         apr_pstrdup(pool, buf));
-        }
-    }
-    BIO_free(bio);
-
-    /* Get subjectAltNames */
-    names = X509_get_ext_d2i(cert->ssl_cert, NID_subject_alt_name, NULL, NULL);
-    if (names) {
-        int names_count = sk_GENERAL_NAME_num(names);
-        int name_idx;
-
-        apr_array_header_t *san_arr = apr_array_make(pool, names_count,
-                                                     sizeof(char*));
-        apr_hash_set(tgt, "subjectAltName", APR_HASH_KEY_STRING, san_arr);
-        for (name_idx = 0; name_idx < names_count; name_idx++) {
-            char *p = NULL;
-            GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, name_idx);
-
-            switch (nm->type) {
-            case GEN_DNS:
-                p = apr_pstrmemdup(pool, (const char *)nm->d.ia5->data,
-                                   nm->d.ia5->length);
-                break;
-            default:
-                /* Don't know what to do - skip. */
-                break;
-            }
-            if (p) {
-                APR_ARRAY_PUSH(san_arr, char*) = p;
-            }
-        }
-        sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
-    }
-
-    return tgt;
+    return cert->type->cert_certificate(cert, pool);
 }
 
-
 const char *serf_ssl_cert_export(
     const serf_ssl_certificate_t *cert,
     apr_pool_t *pool)
 {
-    char *binary_cert;
-    char *encoded_cert;
-    int len;
-    unsigned char *unused;
-
-    /* find the length of the DER encoding. */
-    len = i2d_X509(cert->ssl_cert, NULL);
-    if (len < 0) {
-        return NULL;
-    }
-
-    binary_cert = apr_palloc(pool, len);
-    unused = (unsigned char *)binary_cert;
-    len = i2d_X509(cert->ssl_cert, &unused);  /* unused is incremented  */
-    if (len < 0) {
-        return NULL;
-    }
-
-    encoded_cert = apr_palloc(pool, apr_base64_encode_len(len));
-    apr_base64_encode(encoded_cert, binary_cert, len);
-    
-    return encoded_cert;
+    return cert->type->cert_export(cert, pool);
 }
 
-/* Disables compression for all SSL sessions. */
-static void disable_compression(serf_ssl_context_t *ssl_ctx)
+/* Create a implementation-independent serf_ssl_identity_t object */
+serf_ssl_identity_t *
+serf__create_identity(const serf_ssl_bucket_type_t *type,
+                      void *impl_cert,
+                      void *impl_pkey,
+                      apr_pool_t *pool)
 {
-#ifdef SSL_OP_NO_COMPRESSION
-    SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_NO_COMPRESSION);
-#endif
+    serf_ssl_identity_t *identity;
+
+    identity = apr_palloc(pool, sizeof(serf_ssl_identity_t));
+    identity->impl_cert = impl_cert;
+    identity->impl_pkey = impl_pkey;
+    identity->type = type;
+
+    return identity;
 }
 
-apr_status_t serf_ssl_use_compression(serf_ssl_context_t *ssl_ctx, int enabled)
+apr_status_t serf_ssl_load_identity_from_file(serf_ssl_context_t *ssl_ctx,
+                 const serf_ssl_identity_t **identity,
+                 const char *file_path,
+                 apr_pool_t *pool)
 {
-    if (enabled) {
-#ifdef SSL_OP_NO_COMPRESSION
-        SSL_clear_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION);
-        return APR_SUCCESS;
-#endif
-    } else {
-#ifdef SSL_OP_NO_COMPRESSION
-        SSL_set_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION);
-        return APR_SUCCESS;
-#endif
-    }
-
-    return APR_EGENERAL;
+    return ssl_ctx->type->load_identity_from_file(ssl_ctx->impl_ctx, identity,
+                                                  file_path, pool);
 }
 
-static void serf_ssl_destroy_and_data(serf_bucket_t *bucket)
+apr_status_t serf_ssl_load_CA_cert_from_file(serf_ssl_context_t *ssl_ctx,
+                                             serf_ssl_certificate_t **cert,
+                                             const char *file_path,
+                                             apr_pool_t *pool)
 {
-    ssl_context_t *ctx = bucket->data;
-
-    if (!--ctx->ssl_ctx->refcount) {
-        ssl_free_context(ctx->ssl_ctx);
-    }
-
-    serf_default_destroy_and_data(bucket);
+    /* The ssl_ctx is not needed to load the certificate, only to determine
+       which SSL library we're using. */
+    return ssl_ctx->type->load_CA_cert_from_file(cert, file_path, pool);
 }
 
-static void serf_ssl_decrypt_destroy_and_data(serf_bucket_t *bucket)
+apr_status_t serf_ssl_load_cert_file(serf_ssl_certificate_t **cert,
+                                     const char *file_path,
+                                     apr_pool_t *pool)
 {
-    ssl_context_t *ctx = bucket->data;
+    /* ### This is a hack, depends on the SSL implementation type to be 
+       always the same during multiple ssl sessions. While that's currently
+       the case, it's not guaranteed to stay this way in future versions of
+       serf. */
+    const serf_ssl_bucket_type_t *type = decide_ssl_bucket_type();
 
-    serf_bucket_destroy(*ctx->our_stream);
-
-    serf_ssl_destroy_and_data(bucket);
+    return type->load_CA_cert_from_file(cert, file_path, pool);
 }
 
-static void serf_ssl_encrypt_destroy_and_data(serf_bucket_t *bucket)
+/* SSL Session Resumption API's */
+void serf_ssl_new_session_callback_set(serf_ssl_context_t *ssl_ctx,
+                                       serf_ssl_new_session_t new_session_cb,
+                                       void *baton)
 {
-    ssl_context_t *ctx = bucket->data;
-    serf_ssl_context_t *ssl_ctx = ctx->ssl_ctx;
-
-    if (ssl_ctx->encrypt.stream == *ctx->our_stream) {
-        serf_bucket_destroy(*ctx->our_stream);
-        serf_bucket_destroy(ssl_ctx->encrypt.pending);
-
-        /* Reset our encrypted status and databuf. */
-        ssl_ctx->encrypt.status = APR_SUCCESS;
-        ssl_ctx->encrypt.databuf.status = APR_SUCCESS;
-
-        /* Advance to the next stream - if we have one. */
-        if (ssl_ctx->encrypt.stream_next == NULL) {
-            ssl_ctx->encrypt.stream = NULL;
-            ssl_ctx->encrypt.pending = NULL;
-        }
-        else {
-            bucket_list_t *cur;
-
-            cur = ssl_ctx->encrypt.stream_next;
-            ssl_ctx->encrypt.stream = cur->bucket;
-            ssl_ctx->encrypt.pending =
-                serf_bucket_aggregate_create(cur->bucket->allocator);
-            ssl_ctx->encrypt.stream_next = cur->next;
-            serf_bucket_mem_free(ssl_ctx->allocator, cur);
-        }
-    }
-    else {
-        /* Ah, darn.  We haven't sent this one along yet. */
-        return;
-    }
-    serf_ssl_destroy_and_data(bucket);
+    ssl_ctx->type->new_session_callback_set(ssl_ctx->impl_ctx,
+                                            new_session_cb, baton);
 }
 
-static apr_status_t serf_ssl_read(serf_bucket_t *bucket,
-                                  apr_size_t requested,
-                                  const char **data, apr_size_t *len)
+apr_status_t serf_ssl_resume_session(serf_ssl_context_t *ssl_ctx,
+                                     const serf_ssl_session_t *ssl_session,
+                                     apr_pool_t *pool)
 {
-    ssl_context_t *ctx = bucket->data;
-
-    return serf_databuf_read(ctx->databuf, requested, data, len);
+    return ssl_ctx->type->resume_session(ssl_ctx->impl_ctx, ssl_session,
+                                         pool);
 }
 
-static apr_status_t serf_ssl_readline(serf_bucket_t *bucket,
-                                      int acceptable, int *found,
-                                      const char **data,
-                                      apr_size_t *len)
+apr_status_t serf_ssl_session_export(serf_ssl_context_t *ssl_ctx,
+                                     void **data,
+                                     apr_size_t *len,
+                                     const serf_ssl_session_t *ssl_session,
+                                     apr_pool_t *pool)
 {
-    ssl_context_t *ctx = bucket->data;
-
-    return serf_databuf_readline(ctx->databuf, acceptable, found, data, len);
+    return ssl_ctx->type->session_export(data, len, ssl_session, pool);
 }
 
-static apr_status_t serf_ssl_peek(serf_bucket_t *bucket,
-                                  const char **data,
-                                  apr_size_t *len)
+apr_status_t serf_ssl_session_import(serf_ssl_context_t *ssl_ctx,
+                                     const serf_ssl_session_t **ssl_session,
+                                     void *data,
+                                     apr_size_t len,
+                                     apr_pool_t *pool)
 {
-    ssl_context_t *ctx = bucket->data;
-
-    return serf_databuf_peek(ctx->databuf, data, len);
+    return ssl_ctx->type->session_import(ssl_session, data, len, pool);
 }
 
-
-const serf_bucket_type_t serf_bucket_type_ssl_encrypt = {
-    "SSLENCRYPT",
-    serf_ssl_read,
-    serf_ssl_readline,
-    serf_default_read_iovec,
-    serf_default_read_for_sendfile,
-    serf_default_read_bucket,
-    serf_ssl_peek,
-    serf_ssl_encrypt_destroy_and_data,
-};
-
+/* Define dummy ssl_decrypt and ssl_encrypt buckets. These are needed because
+   they are exported symbols, previously (not anymore) used in the
+   SERF_BUCKET_IS_SSL_DECRYPT and SERF_BUCKET_IS_SSL_ENCRYPT macro's. */
 const serf_bucket_type_t serf_bucket_type_ssl_decrypt = {
-    "SSLDECRYPT",
-    serf_ssl_read,
-    serf_ssl_readline,
-    serf_default_read_iovec,
-    serf_default_read_for_sendfile,
-    serf_default_read_bucket,
-    serf_ssl_peek,
-    serf_ssl_decrypt_destroy_and_data,
-};
+    "ABSTRACT DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+const serf_bucket_type_t serf_bucket_type_ssl_encrypt = {
+    "ABSTRACT ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL, NULL };
diff --git a/context.c b/context.c
index 2930471..203e373 100644
--- a/context.c
+++ b/context.c
@@ -374,6 +374,23 @@
         return "An error occurred during SSL communication";
     case SERF_ERROR_SSL_CERT_FAILED:
         return "An SSL certificate related error occurred ";
+    case SERF_ERROR_SSL_CLIENT_CERT_PW_FAILED:
+        return "The client certificate requires a password, but the necessary "
+               "application callback isn't configured.";
+    case SERF_ERROR_SSL_USER_DENIED_CERT:
+         return "The user denied the use of the server SSL certificate.";
+    case SERF_ERROR_SSL_FATAL_CERT_INVALID:
+        return "Invalid setting or result during server certificate chain "
+               "evaluation.";
+    case SERF_ERROR_SSL_FATAL_CERT_DENIED_IN_KEYCHAIN:
+         return "The user set the certificate as 'not trusted' in Keychain.";
+    case SERF_ERROR_SSL_FATAL_CERT_TRUST_FAILURE:
+         return "Error in the OS X trust framework during server certificate "
+                "chain evaluation.";
+    case SERF_ERROR_SSL_FATAL_CERT_FAILED:
+         return "Unspecified error during server certificate chain evaluation.";
+    case SERF_ERROR_SSL_NO_IDENTITIES_AVAILABLE:
+         return "Can't find matching identities in any of the keychains.";
     case SERF_ERROR_AUTHN_FAILED:
         return "An error occurred during authentication";
     case SERF_ERROR_AUTHN_NOT_SUPPORTED:
diff --git a/serf.h b/serf.h
index 3145e3c..1b7a0e1 100644
--- a/serf.h
+++ b/serf.h
@@ -92,12 +92,33 @@
 /* The server unexpectedly closed the connection prematurely. */
 #define SERF_ERROR_ABORTED_CONNECTION (SERF_ERROR_START + 8)
 
-/* SSL certificates related errors */
+/* General SSL certificate related errors */
 #define SERF_ERROR_SSL_CERT_FAILED (SERF_ERROR_START + 70)
 
-/* SSL communications related errors */
+/* General SSL communications related errors */
 #define SERF_ERROR_SSL_COMM_FAILED (SERF_ERROR_START + 71)
 
+/* Password for client certificate not provided or valid. */
+#define SERF_ERROR_SSL_CLIENT_CERT_PW_FAILED (SERF_ERROR_START + 72)
+
+/* User denied the use of the certificate. */
+#define SERF_ERROR_SSL_USER_DENIED_CERT (SERF_ERROR_START + 74)
+
+/* Fatal error during server certificate validation */
+#define SERF_ERROR_SSL_FATAL_CERT_INVALID (SERF_ERROR_START + 75)
+
+/* Certificate was marked as 'Never Trust' in Keychain. */
+#define SERF_ERROR_SSL_FATAL_CERT_DENIED_IN_KEYCHAIN (SERF_ERROR_START + 76)
+
+/* Fatal error during server certificate validation */
+#define SERF_ERROR_SSL_FATAL_CERT_TRUST_FAILURE (SERF_ERROR_START + 77)
+
+/* Fatal error during server certificate validation */
+#define SERF_ERROR_SSL_FATAL_CERT_FAILED (SERF_ERROR_START + 78)
+
+/* There are no identities available in the keychains to select from. */
+#define SERF_ERROR_SSL_NO_IDENTITIES_AVAILABLE (SERF_ERROR_START + 79)
+
 /* General authentication related errors */
 #define SERF_ERROR_AUTHN_FAILED (SERF_ERROR_START + 90)
 
diff --git a/serf_bucket_types.h b/serf_bucket_types.h
index 1d657d1..1edac7c 100644
--- a/serf_bucket_types.h
+++ b/serf_bucket_types.h
@@ -35,6 +35,32 @@
 extern "C" {
 #endif
 
+
+#define SERF_IMPL_SSL_MACOSXSSL   0x0001
+#define SERF_IMPL_SSL_OPENSSL     0x0002
+#define SERF_IMPL_SSL_ALL         0x0003
+
+#define SERF_IMPL_NONE            0x0000
+#define SERF_IMPL_ALL             0xFFFF
+
+/**
+ * From the compiled bucket implementations, activate only those specified
+ * in the SELECTED mask.
+ */
+apr_uint32_t serf_config_enable_bucket_impls(apr_uint32_t selected);
+
+/**
+ * From the currently active bucket implementations, disable those
+ * specified in the SELECTED mask.
+ */
+apr_uint32_t serf_config_disable_bucket_impls(apr_uint32_t selected);
+
+/**
+ * Get the current list of active bucket implementations. Returns a mask
+ * made of SERF_IMPL* bits.
+ */
+apr_uint32_t serf_config_get_bucket_impls(void);
+
 /* ==================================================================== */
 
 
@@ -488,24 +514,57 @@
 
 
 /* ==================================================================== */
-#define SERF_SSL_CERT_NOTYETVALID       1
-#define SERF_SSL_CERT_EXPIRED           2
-#define SERF_SSL_CERT_UNKNOWNCA         4
-#define SERF_SSL_CERT_SELF_SIGNED       8
-#define SERF_SSL_CERT_UNKNOWN_FAILURE  16
-#define SERF_SSL_CERT_REVOKED          32
+#define SERF_SSL_CERT_NOTYETVALID       0x1
+#define SERF_SSL_CERT_EXPIRED           0x2
+#define SERF_SSL_CERT_UNKNOWNCA         0x4
+#define SERF_SSL_CERT_SELF_SIGNED       0x8
+#define SERF_SSL_CERT_UNKNOWN_FAILURE  0x10
+#define SERF_SSL_CERT_REVOKED          0x20
+#define SERF_SSL_CERT_CONFIRM_NEEDED   0x40
+
+extern const serf_bucket_type_t serf_bucket_type_openssl_encrypt;
+extern const serf_bucket_type_t serf_bucket_type_macosxssl_encrypt;
 
 extern const serf_bucket_type_t serf_bucket_type_ssl_encrypt;
-#define SERF_BUCKET_IS_SSL_ENCRYPT(b) SERF_BUCKET_CHECK((b), ssl_encrypt)
+#define SERF_BUCKET_IS_SSL_ENCRYPT(b) \
+    (((b)->type == &serf_bucket_type_openssl_encrypt) || \
+     ((b)->type == &serf_bucket_type_macosxssl_encrypt))
 
 typedef struct serf_ssl_context_t serf_ssl_context_t;
 typedef struct serf_ssl_certificate_t serf_ssl_certificate_t;
+typedef struct serf_ssl_identity_t serf_ssl_identity_t;
 
+/* Deprecated, replaced by serf_ssl_need_identity_t. */
 typedef apr_status_t (*serf_ssl_need_client_cert_t)(
     void *data,
     const char **cert_path);
 
-typedef apr_status_t (*serf_ssl_need_cert_password_t)(
+/**
+ * Callback to let the application provide a client identity as requested by
+ * the server during the SSL/TLS handshake.
+ * The baton provided in serf_ssl_identity_provider_set is passed as @a data.
+ *
+ * The list of acceptable CA's as provided in @a dn_list should be used to
+ * filter allowed client identities. The dn_list array contains @a dn_len
+ * elements of type apr_hash_t *, each with following keys:
+ * - CN, O, OU, L, ST, C, E.
+ *
+ * The returned identity can be allocated in @a pool.
+ */
+typedef apr_status_t (*serf_ssl_need_identity_t)(
+    void *data,
+    apr_hash_t **dn_list,
+    apr_size_t dn_len,
+    const serf_ssl_identity_t **identity,
+    apr_pool_t *pool);
+
+/* TODO: this callback type doesn't make sense for serf_ssl_need_identity_t,
+   as that function doesn't return a file path, but an identity which doesn't
+   necessarily come from a file.
+   Need serf_ssl_need_identity_password_t. and another label to identify
+   for which identity we ask the password. Maybe just pass the identity itself.
+ */
+ typedef apr_status_t (*serf_ssl_need_cert_password_t)(
     void *data,
     const char *cert_path,
     const char **password);
@@ -522,20 +581,39 @@
     const serf_ssl_certificate_t * const * certs,
     apr_size_t certs_len);
 
+/* Deprecated, replaced by serf_ssl_identity_provider_set. */
 void serf_ssl_client_cert_provider_set(
     serf_ssl_context_t *context,
     serf_ssl_need_client_cert_t callback,
     void *data,
     void *cache_pool);
 
-void serf_ssl_client_cert_password_set(
-    serf_ssl_context_t *context,
-    serf_ssl_need_cert_password_t callback,
-    void *data,
-    void *cache_pool);
+/* Deprecated, replaced by serf_ssl_identity_password_callback_set. */
+void serf_ssl_client_cert_password_set(serf_ssl_context_t *context,
+                                       serf_ssl_need_cert_password_t callback,
+                                       void *data,
+                                       void *cache_pool);
 
 /**
- * Set a callback to override the default SSL server certificate validation 
+ * Set a callback to provide an identity - client certificate and private key -
+ * for authentication to the server.
+ */
+void serf_ssl_identity_provider_set(serf_ssl_context_t *context,
+                                    serf_ssl_need_identity_t callback,
+                                    void *data,
+                                    void *cache_pool);
+
+/**
+ * Set a callback to provide the password of an identity.
+ */
+void serf_ssl_identity_password_callback_set(
+                                    serf_ssl_context_t *context,
+                                    serf_ssl_need_cert_password_t callback,
+                                    void *data,
+                                    void *cache_pool);
+
+/**
+ * Set a callback to override the default SSL server certificate validation
  * algorithm.
  */
 void serf_ssl_server_cert_callback_set(
@@ -556,14 +634,16 @@
 typedef struct serf_ssl_session_t serf_ssl_session_t;
 
 /* Exports @a session to continous memory block. */
-apr_status_t serf_ssl_session_export(void **data,
+apr_status_t serf_ssl_session_export(serf_ssl_context_t *ssl_ctx,
+                                     void **data,
                                      apr_size_t *len,
                                      const serf_ssl_session_t *session,
                                      apr_pool_t *pool);
 
 /* Restores previously saved session from continuous memory block @a data with
  * @a len length. */
-apr_status_t serf_ssl_session_import(const serf_ssl_session_t **session,
+apr_status_t serf_ssl_session_import(serf_ssl_context_t *ssl_ctx,
+                                     const serf_ssl_session_t **session,
                                      void *data,
                                      apr_size_t len,
                                      apr_pool_t *pool);
@@ -640,7 +720,9 @@
 /**
  * Load a CA certificate file from a path @a file_path. If the file was loaded
  * and parsed correctly, a certificate @a cert will be created and returned.
- * This certificate object will be alloced in @a pool.
+ * This certificate object will be allocated in @a pool.
+ *
+ * @deprecated: use serf_ssl_load_CA_cert_from_file instead.
  */
 apr_status_t serf_ssl_load_cert_file(
     serf_ssl_certificate_t **cert,
@@ -648,6 +730,29 @@
     apr_pool_t *pool);
 
 /**
+ * Load a CA certificate file from a path @a file_path. If the file was loaded
+ * and parsed correctly, a certificate @a cert will be created and returned.
+ * This certificate object will be allocated in @a pool.
+ * 
+ * The certificate can be reused in ssl_context's.
+ */
+/* TODO: ssl_ctx is currently not needed as the choice of ssl buckets is
+   for the whole process, not per context. Probably can be removed. */
+apr_status_t serf_ssl_load_CA_cert_from_file(serf_ssl_context_t *ssl_ctx,
+                                             serf_ssl_certificate_t **cert,
+                                             const char *file_path,
+                                             apr_pool_t *pool);
+
+apr_status_t serf_ssl_load_identity_from_file(serf_ssl_context_t *ssl_ctx,
+                 const serf_ssl_identity_t **identity,
+                 const char *file_path,
+                 apr_pool_t *pool);
+
+/* TODO: add serf_ssl_get_certificate_from_identity, to give the application
+   a view on the client identity. This can be useful when matching an identity
+   with a list of acceptable CA's. */
+
+/**
  * Adds the certificate @a cert to the list of trusted certificates in 
  * @a ssl_ctx that will be used for verification. 
  * See also @a serf_ssl_load_cert_file.
@@ -665,6 +770,67 @@
     serf_ssl_context_t *ssl_ctx,
     int enabled);
 
+/* Show a SFCertificateTrustPanel. This is the Mac OS X default dialog to
+   ask the user to confirm or deny the use of the certificate. This panel
+   also gives the option to store the user's decision for this certificate
+   permanently in the Keychain (requires password).
+ 
+   This function will return APR_ENOTIMPL when SERF_HAVE_MACOSXSSL is not 
+   defined.
+ */
+apr_status_t
+serf_macosxssl_show_trust_certificate_panel(serf_ssl_context_t *ssl_ctx,
+                                            const char *message,
+                                            const char *ok_button_label,
+                                            const char *cancel_button_label);
+
+
+/* Note: both serf_macosxssl_show_select_identity_panel and
+   serf_macosxssl_find_preferred_identity_in_keychain support smart cards.
+ 
+   As soon as the card is inserted in the reader, an extra keychain will be 
+   created containing the certificate(s) and private key(s) stored on the smart 
+   card. From there it can be used just like any other identity: The client 
+   identity can be set as preferred identity for a host (or with wildcards) or 
+   will be shown in the identity selection dialog if no such preference was set.
+
+   Tested successfully with a Belgian Personal ID Card (BELPIC) and
+   Smartcard services v2.0b2-mtlion on Mac OS X 10.8.3
+ */
+
+/* Show a SFChooseIdentityPanel. This is the Mac OS X default dialog to
+   ask the user which client certificate to use for this server. The choice
+   of client certificate will not be saved.
+
+   TODO: should take list of acceptable CA's.
+
+   This function will return APR_ENOTIMPL when SERF_HAVE_MACOSXSSL is not
+   defined.
+ */
+apr_status_t
+serf_macosxssl_show_select_identity_panel(serf_ssl_context_t *ssl_ctx,
+                                          const serf_ssl_identity_t **identity,
+                                          const char *message,
+                                          const char *ok_button_label,
+                                          const char *cancel_button_label,
+                                          apr_pool_t *pool);
+
+/* Find a preferred identity for this hostname in the kechains.
+   (identity preference entry).
+ 
+   This function will return APR_ENOTIMPL when SERF_HAVE_MACOSXSSL is not
+   defined.
+ */
+apr_status_t
+serf_macosxssl_find_preferred_identity_in_keychain(
+        serf_ssl_context_t *ssl_ctx,
+        const serf_ssl_identity_t **identity,
+        apr_pool_t *pool);
+
+
+
+void serf_bucket_ssl_destroy_and_data(serf_bucket_t *bucket);
+
 serf_bucket_t *serf_bucket_ssl_encrypt_create(
     serf_bucket_t *stream,
     serf_ssl_context_t *ssl_context,
@@ -675,9 +841,13 @@
 
 /* ==================================================================== */
 
+extern const serf_bucket_type_t serf_bucket_type_openssl_decrypt;
+extern const serf_bucket_type_t serf_bucket_type_macosxssl_decrypt;
 
 extern const serf_bucket_type_t serf_bucket_type_ssl_decrypt;
-#define SERF_BUCKET_IS_SSL_DECRYPT(b) SERF_BUCKET_CHECK((b), ssl_decrypt)
+#define SERF_BUCKET_IS_SSL_DECRYPT(b) \
+    (((b)->type == &serf_bucket_type_openssl_decrypt) || \
+     ((b)->type == &serf_bucket_type_macosxssl_decrypt))
 
 serf_bucket_t *serf_bucket_ssl_decrypt_create(
     serf_bucket_t *stream,
diff --git a/test/certificates.pem b/test/certificates.pem
new file mode 100644
index 0000000..1f3858a
--- /dev/null
+++ b/test/certificates.pem
@@ -0,0 +1,289 @@
+Certificate:
+    Data:
+        Version: 1 (0x0)
+        Serial Number: 1048593 (0x100011)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite CA, CN=Serf CA/emailAddress=serfca@example.com
+        Validity
+            Not Before: Apr 18 19:50:11 2013 GMT
+            Not After : Apr 18 19:50:11 2014 GMT
+        Subject: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Server, CN=Serf Server/emailAddress=serfserver@example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ea:92:6f:ab:ed:ce:e1:c3:a8:2e:3e:03:9e:a0:
+                    d8:c0:e4:df:e3:58:1f:bf:eb:18:23:c8:ca:a5:9a:
+                    f0:f2:b5:97:ce:df:47:15:9a:0e:96:41:cc:b9:92:
+                    66:0c:f0:11:f7:f1:8f:3c:29:48:31:4a:1d:93:db:
+                    0a:f0:e9:98:5f:98:ed:07:1c:4e:2e:9f:e3:f3:3f:
+                    2c:2f:20:d8:f1:5e:8a:8f:3a:aa:3e:9c:f5:99:07:
+                    8c:a6:e9:9c:df:03:59:c1:ff:37:5a:bf:69:e8:41:
+                    bd:c0:28:66:c4:4b:0a:ab:dd:93:a1:8f:3e:5e:34:
+                    5c:e6:04:6c:1d:8b:a3:88:6b
+                Exponent: 65537 (0x10001)
+    Signature Algorithm: sha1WithRSAEncryption
+         19:2f:4f:a7:a4:ea:b4:31:94:0b:9c:e8:9e:47:59:bd:2c:87:
+         7d:70:c3:28:72:1d:79:36:69:9f:fa:d7:58:6c:d6:6b:68:c0:
+         be:99:ff:55:09:82:7e:c6:25:bb:35:6c:2c:7f:5c:89:f9:8e:
+         d8:cf:45:1e:93:a3:b5:6c:d7:93:9c:d6:53:5a:fb:3d:08:d6:
+         21:d0:dc:f2:66:ed:e2:56:2c:bd:63:e1:57:f3:04:1f:e3:96:
+         d8:16:5c:44:ac:3c:13:98:cb:21:02:bf:23:67:0c:c1:4a:df:
+         b0:c8:d9:16:cf:b4:a7:c0:3a:6d:cb:d8:1e:ce:fe:c9:1c:00:
+         68:e4
+-----BEGIN CERTIFICATE-----
+MIICvzCCAigCAxAAETANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMCQkUxEDAO
+BgNVBAgMB0FudHdlcnAxETAPBgNVBAcMCE1lY2hlbGVuMR8wHQYDVQQKDBZJbiBT
+ZXJmIHdlIHRydXN0LCBJbmMuMRYwFAYDVQQLDA1UZXN0IFN1aXRlIENBMRAwDgYD
+VQQDDAdTZXJmIENBMSEwHwYJKoZIhvcNAQkBFhJzZXJmY2FAZXhhbXBsZS5jb20w
+HhcNMTMwNDE4MTk1MDExWhcNMTQwNDE4MTk1MDExWjCBrDELMAkGA1UEBhMCQkUx
+EDAOBgNVBAgMB0FudHdlcnAxETAPBgNVBAcMCE1lY2hlbGVuMR8wHQYDVQQKDBZJ
+biBTZXJmIHdlIHRydXN0LCBJbmMuMRowGAYDVQQLDBFUZXN0IFN1aXRlIFNlcnZl
+cjEUMBIGA1UEAwwLU2VyZiBTZXJ2ZXIxJTAjBgkqhkiG9w0BCQEWFnNlcmZzZXJ2
+ZXJAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOqSb6vt
+zuHDqC4+A56g2MDk3+NYH7/rGCPIyqWa8PK1l87fRxWaDpZBzLmSZgzwEffxjzwp
+SDFKHZPbCvDpmF+Y7QccTi6f4/M/LC8g2PFeio86qj6c9ZkHjKbpnN8DWcH/N1q/
+aehBvcAoZsRLCqvdk6GPPl40XOYEbB2Lo4hrAgMBAAEwDQYJKoZIhvcNAQEFBQAD
+gYEAGS9Pp6TqtDGUC5zonkdZvSyHfXDDKHIdeTZpn/rXWGzWa2jAvpn/VQmCfsYl
+uzVsLH9cifmO2M9FHpOjtWzXk5zWU1r7PQjWIdDc8mbt4lYsvWPhV/MEH+OW2BZc
+RKw8E5jLIQK/I2cMwUrfsMjZFs+0p8A6bcvYHs7+yRwAaOQ=
+-----END CERTIFICATE-----
+================================================================
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 14060582211199810902 (0xc321390661bdbd56)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Root CA, CN=Serf Root CA/emailAddress=serfrootca@example.com
+        Validity
+            Not Before: Apr 13 11:19:14 2013 GMT
+            Not After : Apr 11 11:19:14 2023 GMT
+        Subject: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Root CA, CN=Serf Root CA/emailAddress=serfrootca@example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:e1:dd:69:ea:ac:fd:f2:73:81:ec:ed:b6:b1:0e:
+                    70:23:8c:01:6d:ab:f3:43:ab:0f:fc:8a:6a:23:eb:
+                    6c:48:37:c9:c0:8f:29:61:00:7e:89:1f:00:d1:68:
+                    dd:70:de:bd:34:32:0e:41:ac:f9:ea:c1:a6:0d:b5:
+                    65:be:5c:9e:f1:b4:27:54:c1:79:61:63:d4:2d:06:
+                    11:5f:cc:4c:d9:d3:ef:4e:da:9f:a4:26:16:cb:3f:
+                    86:f8:21:7d:c5:3a:32:34:c8:cb:85:ad:c4:3f:e4:
+                    b3:ad:8e:a7:67:9e:0c:3b:5a:58:29:5f:ce:96:3b:
+                    e3:f5:ca:42:eb:7b:44:d5:75
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B2:3E:19:35:C1:C4:4F:23:79:ED:BF:E8:DC:5C:31:03:F0:2F:15:77
+            X509v3 Authority Key Identifier: 
+                keyid:B2:3E:19:35:C1:C4:4F:23:79:ED:BF:E8:DC:5C:31:03:F0:2F:15:77
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         c3:ec:1f:3e:b1:87:d0:80:10:f9:bf:73:1b:38:d4:b1:b7:80:
+         4d:ea:20:c1:79:7d:f5:58:42:11:13:28:ab:b1:b4:0a:88:9c:
+         20:4d:9c:b5:5a:41:28:5e:f6:69:5e:55:bb:e2:1a:b9:c6:62:
+         38:86:32:7b:93:28:ca:9e:af:d1:06:f9:93:c2:5d:92:c0:25:
+         68:6a:e1:fe:85:2a:19:a7:6b:17:4d:23:9a:72:d6:d0:c1:80:
+         ff:74:10:8b:62:7a:11:c3:9a:87:2a:e4:7d:d1:8c:72:a6:bf:
+         c1:3b:d8:b8:33:c0:ff:b0:f7:d6:0e:a3:dd:36:fe:8a:41:a0:
+         98:cc
+-----BEGIN CERTIFICATE-----
+MIIDLDCCApWgAwIBAgIJAMMhOQZhvb1WMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD
+VQQGEwJCRTEQMA4GA1UECAwHQW50d2VycDERMA8GA1UEBwwITWVjaGVsZW4xHzAd
+BgNVBAoMFkluIFNlcmYgd2UgdHJ1c3QsIEluYy4xGzAZBgNVBAsMElRlc3QgU3Vp
+dGUgUm9vdCBDQTEVMBMGA1UEAwwMU2VyZiBSb290IENBMSUwIwYJKoZIhvcNAQkB
+FhZzZXJmcm9vdGNhQGV4YW1wbGUuY29tMB4XDTEzMDQxMzExMTkxNFoXDTIzMDQx
+MTExMTkxNFowga4xCzAJBgNVBAYTAkJFMRAwDgYDVQQIDAdBbnR3ZXJwMREwDwYD
+VQQHDAhNZWNoZWxlbjEfMB0GA1UECgwWSW4gU2VyZiB3ZSB0cnVzdCwgSW5jLjEb
+MBkGA1UECwwSVGVzdCBTdWl0ZSBSb290IENBMRUwEwYDVQQDDAxTZXJmIFJvb3Qg
+Q0ExJTAjBgkqhkiG9w0BCQEWFnNlcmZyb290Y2FAZXhhbXBsZS5jb20wgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAOHdaeqs/fJzgezttrEOcCOMAW2r80OrD/yK
+aiPrbEg3ycCPKWEAfokfANFo3XDevTQyDkGs+erBpg21Zb5cnvG0J1TBeWFj1C0G
+EV/MTNnT707an6QmFss/hvghfcU6MjTIy4WtxD/ks62Op2eeDDtaWClfzpY74/XK
+Qut7RNV1AgMBAAGjUDBOMB0GA1UdDgQWBBSyPhk1wcRPI3ntv+jcXDED8C8VdzAf
+BgNVHSMEGDAWgBSyPhk1wcRPI3ntv+jcXDED8C8VdzAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4GBAMPsHz6xh9CAEPm/cxs41LG3gE3qIMF5ffVYQhETKKux
+tAqInCBNnLVaQShe9mleVbviGrnGYjiGMnuTKMqer9EG+ZPCXZLAJWhq4f6FKhmn
+axdNI5py1tDBgP90EItiehHDmocq5H3RjHKmv8E72LgzwP+w99YOo902/opBoJjM
+-----END CERTIFICATE-----
+================================================================
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            58:0b:05:6c:53:24:db:b2:50:57:18:5f:f9:e5:a6:50
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BE, CN=Belgium Root CA
+        Validity
+            Not Before: Jan 26 23:00:00 2003 GMT
+            Not After : Jan 26 23:00:00 2014 GMT
+        Subject: C=BE, CN=Belgium Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c8:a1:71:e9:1c:46:42:79:78:71:6f:9d:ae:a9:
+                    a8:ab:28:b7:4d:c7:20:eb:30:91:5a:75:f5:e2:d2:
+                    cf:c8:4c:14:98:42:58:ad:c7:11:c5:40:40:6a:5a:
+                    f9:74:12:27:87:e9:9c:e5:71:4e:22:2c:d1:12:18:
+                    aa:30:5e:a2:21:b9:d9:bb:ff:f6:74:eb:31:01:e7:
+                    3b:7e:58:0f:91:16:4d:76:89:a8:01:4f:ad:22:66:
+                    70:fa:4b:1d:95:c1:30:58:ea:bc:d9:65:d8:9a:b4:
+                    88:eb:49:46:52:df:d2:53:15:76:cb:14:5d:19:49:
+                    b1:6f:6a:d3:d3:fd:bc:c2:2d:ec:45:3f:09:3f:58:
+                    be:fc:d4:ef:00:8c:81:35:72:bf:f7:18:ea:96:62:
+                    7d:2b:28:7f:15:6c:63:d2:ca:ca:7d:05:ac:c8:6d:
+                    07:6d:32:be:68:b8:05:40:ae:54:98:56:3e:66:f1:
+                    30:e8:ef:c4:ab:93:5e:07:de:32:8f:12:74:aa:5b:
+                    34:23:54:c0:ea:6c:ce:fe:36:92:a8:09:17:ea:a1:
+                    2d:cf:6c:e3:84:1d:de:87:2e:33:0b:3c:74:e2:21:
+                    50:38:95:2e:5c:e0:e5:c6:31:f9:db:40:fa:6a:a1:
+                    a4:8a:93:9b:a7:21:06:87:1d:27:d3:c4:a1:c9:4c:
+                    b0:6f
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Certificate Policies: 
+                Policy: 2.16.56.1.1.1
+                  CPS: http://repository.eid.belgium.be
+
+            X509v3 Subject Key Identifier: 
+                10:F0:0C:56:9B:61:EA:57:3A:B6:35:97:6D:9F:DD:B9:14:8E:DB:E6
+            Netscape Cert Type: 
+                SSL CA, S/MIME CA, Object Signing CA
+            X509v3 Authority Key Identifier: 
+                keyid:10:F0:0C:56:9B:61:EA:57:3A:B6:35:97:6D:9F:DD:B9:14:8E:DB:E6
+
+    Signature Algorithm: sha1WithRSAEncryption
+         c8:6d:22:51:8a:61:f8:0f:96:6e:d5:20:b2:81:f8:c6:dc:a3:
+         16:00:da:cd:6a:e7:6b:2a:fa:59:48:a7:4c:49:37:d7:73:a1:
+         6a:01:65:5e:32:bd:e7:97:d3:d0:2e:3c:73:d3:8c:7b:83:ef:
+         d6:42:c1:3f:a8:a9:5d:0f:37:ba:76:d2:40:bd:cc:2d:3f:d3:
+         44:41:49:9c:fd:5b:29:f4:02:23:22:5b:71:1b:bf:58:d9:28:
+         4e:2d:45:f4:da:e7:b5:63:45:44:11:0d:2a:7f:33:7f:36:49:
+         b4:ce:6e:a9:02:31:ae:5c:fd:c8:89:bf:42:7b:d7:f1:60:f2:
+         d7:87:f6:57:2e:7a:7e:6a:13:80:1d:dc:e3:d0:63:1e:3d:71:
+         31:b1:60:d4:9e:08:ca:ab:f0:94:c7:48:75:54:81:f3:1b:ad:
+         77:9c:e8:b2:8f:db:83:ac:8f:34:6b:e8:bf:c3:d9:f5:43:c3:
+         64:55:eb:1a:bd:36:86:36:ba:21:8c:97:1a:21:d4:ea:2d:3b:
+         ac:ba:ec:a7:1d:ab:be:b9:4a:9b:35:2f:1c:5c:1d:51:a7:1f:
+         54:ed:12:97:ff:f2:6e:87:7d:46:c9:74:d6:ef:eb:3d:7d:e6:
+         59:6e:06:94:04:e4:a2:55:87:38:28:6a:22:5e:e2:be:74:12:
+         b0:04:43:2a
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIQWAsFbFMk27JQVxhf+eWmUDANBgkqhkiG9w0BAQUFADAn
+MQswCQYDVQQGEwJCRTEYMBYGA1UEAxMPQmVsZ2l1bSBSb290IENBMB4XDTAzMDEy
+NjIzMDAwMFoXDTE0MDEyNjIzMDAwMFowJzELMAkGA1UEBhMCQkUxGDAWBgNVBAMT
+D0JlbGdpdW0gUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AMihcekcRkJ5eHFvna6pqKsot03HIOswkVp19eLSz8hMFJhCWK3HEcVAQGpa+XQS
+J4fpnOVxTiIs0RIYqjBeoiG52bv/9nTrMQHnO35YD5EWTXaJqAFPrSJmcPpLHZXB
+MFjqvNll2Jq0iOtJRlLf0lMVdssUXRlJsW9q09P9vMIt7EU/CT9YvvzU7wCMgTVy
+v/cY6pZifSsofxVsY9LKyn0FrMhtB20yvmi4BUCuVJhWPmbxMOjvxKuTXgfeMo8S
+dKpbNCNUwOpszv42kqgJF+qhLc9s44Qd3ocuMws8dOIhUDiVLlzg5cYx+dtA+mqh
+pIqTm6chBocdJ9PEoclMsG8CAwEAAaOBuzCBuDAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGBWA4AQEBMC4wLAYIKwYBBQUHAgEW
+IGh0dHA6Ly9yZXBvc2l0b3J5LmVpZC5iZWxnaXVtLmJlMB0GA1UdDgQWBBQQ8AxW
+m2HqVzq2NZdtn925FI7b5jARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAU
+EPAMVpth6lc6tjWXbZ/duRSO2+YwDQYJKoZIhvcNAQEFBQADggEBAMhtIlGKYfgP
+lm7VILKB+MbcoxYA2s1q52sq+llIp0xJN9dzoWoBZV4yveeX09AuPHPTjHuD79ZC
+wT+oqV0PN7p20kC9zC0/00RBSZz9Wyn0AiMiW3Ebv1jZKE4tRfTa57VjRUQRDSp/
+M382SbTObqkCMa5c/ciJv0J71/Fg8teH9lcuen5qE4Ad3OPQYx49cTGxYNSeCMqr
+8JTHSHVUgfMbrXec6LKP24OsjzRr6L/D2fVDw2RV6xq9NoY2uiGMlxoh1OotO6y6
+7Kcdq765Sps1LxxcHVGnH1TtEpf/8m6HfUbJdNbv6z195lluBpQE5KJVhzgoaiJe
+4r50ErAEQyo=
+-----END CERTIFICATE-----
+================================================================
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 3098404661496965511 (0x2affbe9fa2f0e987)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BE, CN=Belgium Root CA2
+        Validity
+            Not Before: Oct  4 10:00:00 2007 GMT
+            Not After : Dec 15 08:00:00 2021 GMT
+        Subject: C=BE, CN=Belgium Root CA2
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c6:73:42:1e:92:ff:75:0f:8b:bf:74:86:a7:3f:
+                    ed:b2:18:2d:2d:97:f9:a9:fb:98:4a:23:df:d5:8d:
+                    e0:8c:c6:32:1c:16:2d:07:42:d6:2d:b6:6e:2f:a7:
+                    f5:fc:c6:85:82:5d:95:de:b5:22:00:cc:a8:53:40:
+                    9f:af:0a:35:a2:7a:c7:e1:ca:f7:60:10:67:0e:a7:
+                    50:73:17:53:95:9f:22:9f:0c:5d:6f:b6:41:bc:8c:
+                    eb:da:1d:46:bd:a7:94:bf:f3:1a:cb:d4:fb:8d:0e:
+                    1e:33:c8:96:d7:ec:8c:53:de:93:1e:34:1e:8a:50:
+                    71:26:58:b4:5d:c2:88:89:da:60:d2:89:21:3f:de:
+                    d7:01:18:36:6e:e1:2c:70:03:04:65:ef:98:06:2b:
+                    5d:1e:62:dc:55:6b:fe:66:64:21:5f:f8:2f:e1:d7:
+                    9b:29:af:6f:cd:9f:aa:0c:46:d1:88:c5:9d:a2:95:
+                    9f:ac:3f:15:ae:f2:61:df:ef:c3:6b:9a:22:d8:2c:
+                    71:fd:58:1d:ec:00:a4:38:20:95:5a:c7:d5:37:63:
+                    ba:4f:9b:aa:fe:56:46:2e:63:d9:0b:23:d5:8a:fa:
+                    b5:23:f0:89:1b:a1:14:0d:26:da:41:f0:38:8f:30:
+                    ed:05:26:79:c1:4b:4e:a1:b8:7e:55:52:3a:69:46:
+                    84:75
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Certificate Policies: 
+                Policy: 2.16.56.9.1.1
+                  CPS: http://repository.eid.belgium.be
+
+            X509v3 Subject Key Identifier: 
+                85:8A:EB:F4:C5:BB:BE:0E:59:03:94:DE:D6:80:01:15:E3:10:9C:39
+            Netscape Cert Type: 
+                SSL CA, S/MIME CA, Object Signing CA
+            X509v3 Authority Key Identifier: 
+                keyid:85:8A:EB:F4:C5:BB:BE:0E:59:03:94:DE:D6:80:01:15:E3:10:9C:39
+
+    Signature Algorithm: sha1WithRSAEncryption
+         51:d8:85:dd:bb:57:6f:cc:a0:6c:b5:a3:20:9c:53:09:f3:4a:
+         01:0c:74:bf:2b:b3:9a:9a:ba:18:f2:0b:88:ac:1c:b3:33:af:
+         ce:e5:13:01:27:92:84:58:9a:10:b9:f7:cc:14:92:6b:74:16:
+         8a:96:e8:51:ef:bf:fa:4a:25:a7:89:b6:63:2b:5d:94:58:d1:
+         cf:11:72:b6:1e:b9:39:41:16:4d:29:bc:35:53:0b:da:de:8e:
+         0e:cd:a9:95:77:25:ca:94:5a:e9:b2:69:ae:d8:c0:13:be:98:
+         fc:96:9c:84:7f:55:13:e6:3c:87:e3:bc:20:a4:a4:36:68:6b:
+         4d:60:66:1c:f9:bf:ac:80:94:66:2e:b9:41:8a:d3:65:d3:84:
+         80:02:ef:50:1d:5e:46:dc:f7:c9:ba:b5:34:7c:2a:f3:c6:d8:
+         5f:5f:54:9d:db:4d:cd:11:e7:fd:14:02:83:66:5e:c8:a6:00:
+         12:a0:5f:be:ce:14:fe:bb:1f:a7:61:f7:ab:4a:f1:06:14:9f:
+         ca:49:42:c2:a9:bc:ed:85:b1:ab:81:41:e6:0d:c5:42:69:53:
+         87:39:9d:4c:1f:00:0e:3e:07:0d:75:57:44:a8:53:b4:36:76:
+         64:99:dc:6e:eb:3d:46:6e:14:5d:5e:47:53:8d:78:4d:e0:27:
+         bb:8e:85:76
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIIKv++n6Lw6YcwDQYJKoZIhvcNAQEFBQAwKDELMAkGA1UE
+BhMCQkUxGTAXBgNVBAMTEEJlbGdpdW0gUm9vdCBDQTIwHhcNMDcxMDA0MTAwMDAw
+WhcNMjExMjE1MDgwMDAwWjAoMQswCQYDVQQGEwJCRTEZMBcGA1UEAxMQQmVsZ2l1
+bSBSb290IENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZzQh6S
+/3UPi790hqc/7bIYLS2X+an7mEoj39WN4IzGMhwWLQdC1i22bi+n9fzGhYJdld61
+IgDMqFNAn68KNaJ6x+HK92AQZw6nUHMXU5WfIp8MXW+2QbyM69odRr2nlL/zGsvU
++40OHjPIltfsjFPekx40HopQcSZYtF3CiInaYNKJIT/e1wEYNm7hLHADBGXvmAYr
+XR5i3FVr/mZkIV/4L+HXmymvb82fqgxG0YjFnaKVn6w/Fa7yYd/vw2uaItgscf1Y
+HewApDgglVrH1Tdjuk+bqv5WRi5j2Qsj1Yr6tSPwiRuhFA0m2kHwOI8w7QUmecFL
+TqG4flVSOmlGhHUCAwEAAaOBuzCBuDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zBCBgNVHSAEOzA5MDcGBWA4CQEBMC4wLAYIKwYBBQUHAgEWIGh0dHA6
+Ly9yZXBvc2l0b3J5LmVpZC5iZWxnaXVtLmJlMB0GA1UdDgQWBBSFiuv0xbu+DlkD
+lN7WgAEV4xCcOTARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUhYrr9MW7
+vg5ZA5Te1oABFeMQnDkwDQYJKoZIhvcNAQEFBQADggEBAFHYhd27V2/MoGy1oyCc
+UwnzSgEMdL8rs5qauhjyC4isHLMzr87lEwEnkoRYmhC598wUkmt0FoqW6FHvv/pK
+JaeJtmMrXZRY0c8RcrYeuTlBFk0pvDVTC9rejg7NqZV3JcqUWumyaa7YwBO+mPyW
+nIR/VRPmPIfjvCCkpDZoa01gZhz5v6yAlGYuuUGK02XThIAC71AdXkbc98m6tTR8
+KvPG2F9fVJ3bTc0R5/0UAoNmXsimABKgX77OFP67H6dh96tK8QYUn8pJQsKpvO2F
+sauBQeYNxUJpU4c5nUwfAA4+Bw11V0SoU7Q2dmSZ3G7rPUZuFF1eR1ONeE3gJ7uO
+hXY=
+-----END CERTIFICATE-----
+================================================================
diff --git a/test/serf_get.c b/test/serf_get.c
index 1c9559a..708d061 100644
--- a/test/serf_get.c
+++ b/test/serf_get.c
@@ -26,7 +26,7 @@
 #include "serf.h"
 
 /* Add Connection: close header to each request. */
-/* #define CONNECTION_CLOSE_HDR */
+#define CONNECTION_CLOSE_HDR
 
 typedef struct {
     const char *hostinfo;
@@ -155,7 +155,8 @@
     app_baton_t *ctx = baton;
 
     if (ctx->session_filename) {
-        status = serf_ssl_session_export(&data, &len, session, pool);
+        status = serf_ssl_session_export(ctx->ssl_ctx,
+                                         &data, &len, session, pool);
 
         if (status) {
             return status;
@@ -176,7 +177,8 @@
     return APR_SUCCESS;
 }
 
-static apr_status_t read_ssl_session(const serf_ssl_session_t **session,
+static apr_status_t read_ssl_session(serf_ssl_context_t *ssl_ctx,
+                                     const serf_ssl_session_t **session,
                                      const char *filename,
                                      apr_pool_t *pool)
 {
@@ -194,7 +196,7 @@
 
     /* We should reach EOF. */
     if (status == APR_EOF) {
-        status = serf_ssl_session_import(session, buf, len, pool);
+        status = serf_ssl_session_import(ssl_ctx, session, buf, len, pool);
     } else {
         status = APR_EGENERAL;
     }
@@ -230,7 +232,8 @@
 
             serf_ssl_new_session_callback_set(ctx->ssl_ctx, new_ssl_session,
                                               ctx);
-            status = read_ssl_session(&session, ctx->session_filename, pool);
+            status = read_ssl_session(ctx->ssl_ctx, &session,
+                                      ctx->session_filename, pool);
             if (status == APR_SUCCESS) {
                 fprintf(stderr, "Using SSL session from '%s'\n",
                         ctx->session_filename);
diff --git a/test/server/test_sslserver.c b/test/server/test_sslserver.c
index 5b23801..9f19f96 100644
--- a/test/server/test_sslserver.c
+++ b/test/server/test_sslserver.c
@@ -190,7 +190,10 @@
         SSL_CTX_set_default_passwd_cb(ssl_ctx->ctx, pem_passwd_cb);
         SSL_CTX_use_PrivateKey_file(ssl_ctx->ctx, keyfile, SSL_FILETYPE_PEM);
 
-        /* Set server certificate, add ca certificates if provided. */
+        /* Set server certificate, add CA certificates if provided.
+           Also, use the same CA certificates as acceptable CA's for the client
+           certificate. This doesn't have to be the same list, but for now it's
+           sufficient. */
         certfile = certfiles[0];
         SSL_CTX_use_certificate_file(ssl_ctx->ctx, certfile, SSL_FILETYPE_PEM);
 
@@ -207,6 +210,9 @@
                 SSL_CTX_add_extra_chain_cert(ssl_ctx->ctx, ssl_cert);
 
                 X509_STORE_add_cert(store, ssl_cert);
+
+                /* acceptable CA for the client cert */
+                SSL_CTX_add_client_CA(ssl_ctx->ctx, ssl_cert);
             }
             certfile = certfiles[i++];
         }
diff --git a/test/test_context.c b/test/test_context.c
index 7c5469a..365f37a 100644
--- a/test/test_context.c
+++ b/test/test_context.c
@@ -1073,6 +1073,21 @@
 
     tb->result_flags |= TEST_RESULT_SERVERCERTCB_CALLED;
 
+#if 0
+    /* Example of how to ask the user to validate a server certificate
+       via a platform-specific dialog. */
+    if (failures) {
+        apr_status_t status;
+
+        status = serf_macosxssl_show_trust_certificate_panel(tb->ssl_context,
+                     "Server certificate requires validation",
+                     "Accept",
+                     "Cancel");
+        if (status && status != APR_ENOTIMPL)
+            return status;
+    }
+#endif
+
     /* We expect an error from the certificate validation function. */
     if (failures & expected_failures)
         return APR_SUCCESS;
@@ -1669,6 +1684,144 @@
     CuAssertTrue(tc, tb->result_flags & TEST_RESULT_CLIENT_CERTPWCB_CALLED);
 }
 
+static apr_status_t identity_cb(void *data,
+                                apr_hash_t **dnlist,
+                                apr_size_t dnlen,
+                                const serf_ssl_identity_t **identity,
+                                apr_pool_t *pool)
+{
+    test_baton_t *tb = data;
+    const char *cert_path = "test/server/serfclientcert.p12";
+    apr_hash_t *ca;
+    int i;
+    apr_status_t status;
+
+    tb->result_flags |= TEST_RESULT_CLIENT_CERTCB_CALLED;
+
+    if (!dnlist || dnlen != 2)
+        return SERF_ERROR_ISSUE_IN_TESTSUITE;
+
+    for (i = 0; i < 2; i++) {
+        ca = dnlist[i];
+        if (!ca) return SERF_ERROR_ISSUE_IN_TESTSUITE;
+
+        if (strcmp("Serf Root CA",
+                   apr_hash_get(ca, "CN", APR_HASH_KEY_STRING)) == 0)
+            continue;
+
+        if (strcmp("Serf CA",
+                   apr_hash_get(ca, "CN", APR_HASH_KEY_STRING)) != 0)
+            return SERF_ERROR_ISSUE_IN_TESTSUITE;
+    }
+
+#if 0
+    /* Example of how to use Keychain to fetch a client identity for a server.
+
+       How to test:
+       1. Import file test/server/serfclientcert.p12 in a keychain.
+       -> run the test test_ssl_identity now, you should get a dialog
+          where you can select "Serf Client".
+       2. If you have a smart card with a client identity, plug it in.
+       -> run the test again, the dialog should show both the "Serf Client"
+          identity as your personal (stored on the smart card) identity.
+       3. Add an identity preferences for 'https://localhost' to the
+           "Serf Client" certificate in the login keychain.
+       -> run the test test_ssl_identity now. It should continue without
+          showing a dialog.
+     
+       Note: in all these cases the test will fail, because the identity
+       password callback isn't called - Keychain handles that.
+     */
+
+    /* First check if the user has set a preferred identity for this server. */
+    status = serf_macosxssl_find_preferred_identity_in_keychain(tb->ssl_context,
+                                                                identity,
+                                                                pool);
+    if (status == APR_SUCCESS)
+        return APR_SUCCESS;
+
+    /* Choose an identity from the available identities in the keychains. */
+    status = serf_macosxssl_show_select_identity_panel(tb->ssl_context,
+                 identity,
+                 "Select client identity.", "Accept", "Cancel",
+                 pool);
+    if (status != APR_ENOTIMPL)
+        return status;
+#endif
+
+    status = serf_ssl_load_identity_from_file(tb->ssl_context,
+                                              identity,
+                                              cert_path, pool);
+
+    return status;
+}
+
+static apr_status_t
+identity_conn_setup(apr_socket_t *skt,
+                    serf_bucket_t **input_bkt,
+                    serf_bucket_t **output_bkt,
+                    void *setup_baton,
+                    apr_pool_t *pool)
+{
+    test_baton_t *tb = setup_baton;
+    apr_status_t status;
+
+    status = https_set_root_ca_conn_setup(skt, input_bkt, output_bkt,
+                                          setup_baton, pool);
+    if (status)
+        return status;
+
+    serf_ssl_identity_provider_set(tb->ssl_context,
+                                   identity_cb,
+                                   tb,
+                                   pool);
+
+    serf_ssl_identity_password_callback_set(tb->ssl_context,
+                                            client_cert_pw_cb,
+                                            tb,
+                                            pool);
+
+    return APR_SUCCESS;
+}
+
+static void test_ssl_identity(CuTest *tc)
+{
+    test_baton_t *tb;
+    handler_baton_t handler_ctx[1];
+    const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
+    test_server_message_t message_list[] = {
+        {CHUNKED_REQUEST(1, "1")},
+    };
+    test_server_action_t action_list[] = {
+        {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE},
+    };
+    apr_status_t status;
+
+    /* Set up a test context with a server */
+    apr_pool_t *test_pool = tc->testBaton;
+
+    /* The SSL server the complete certificate chain to validate the client
+     certificate. */
+    status = test_https_server_setup(&tb,
+                                     message_list, num_requests,
+                                     action_list, num_requests, 0,
+                                     identity_conn_setup,
+                                     "test/server/serfserverkey.pem",
+                                     all_server_certs,
+                                     "Serf Client",
+                                     NULL, /* No server cert callback */
+                                     test_pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+    create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
+
+    test_helper_run_requests_expect_ok(tc, tb, num_requests,
+                                       handler_ctx, test_pool);
+
+    CuAssertTrue(tc, tb->result_flags & TEST_RESULT_CLIENT_CERTCB_CALLED);
+    CuAssertTrue(tc, tb->result_flags & TEST_RESULT_CLIENT_CERTPWCB_CALLED);
+}
+
 /* Validate that the expired certificate is reported as failure in the
    callback. */
 static void test_ssl_expired_server_cert(CuTest *tc)
@@ -2188,6 +2341,7 @@
 CuSuite *test_context(void)
 {
     CuSuite *suite = CuSuiteNew();
+    CuSuite *openssl_suite, *macosxssl_suite;
 
     CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown);
 
@@ -2201,23 +2355,56 @@
     SUITE_ADD_TEST(suite, test_request_timeout);
     SUITE_ADD_TEST(suite, test_connection_large_response);
     SUITE_ADD_TEST(suite, test_connection_large_request);
-    SUITE_ADD_TEST(suite, test_ssl_handshake);
-    SUITE_ADD_TEST(suite, test_ssl_trust_rootca);
-    SUITE_ADD_TEST(suite, test_ssl_application_rejects_cert);
-    SUITE_ADD_TEST(suite, test_ssl_certificate_chain_with_anchor);
-    SUITE_ADD_TEST(suite, test_ssl_certificate_chain_all_from_server);
-    SUITE_ADD_TEST(suite, test_ssl_no_servercert_callback_allok);
-    SUITE_ADD_TEST(suite, test_ssl_no_servercert_callback_fail);
-    SUITE_ADD_TEST(suite, test_ssl_large_response);
-    SUITE_ADD_TEST(suite, test_ssl_large_request);
-    SUITE_ADD_TEST(suite, test_ssl_client_certificate);
-    SUITE_ADD_TEST(suite, test_ssl_expired_server_cert);
-    SUITE_ADD_TEST(suite, test_ssl_future_server_cert);
     SUITE_ADD_TEST(suite, test_setup_ssltunnel);
     SUITE_ADD_TEST(suite, test_ssltunnel_no_creds_cb);
     SUITE_ADD_TEST(suite, test_ssltunnel_basic_auth);
     SUITE_ADD_TEST(suite, test_ssltunnel_basic_auth_keepalive_off);
     SUITE_ADD_TEST(suite, test_ssltunnel_digest_auth);
 
+#ifdef SERF_HAVE_OPENSSL
+    openssl_suite = CuSuiteNew();
+
+    CuSuiteSetSetupTeardownCallbacks(openssl_suite, test_openssl_setup,
+                                     test_openssl_teardown);
+
+    SUITE_ADD_TEST(openssl_suite, test_ssl_handshake);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_trust_rootca);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_application_rejects_cert);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_certificate_chain_with_anchor);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_certificate_chain_all_from_server);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_no_servercert_callback_allok);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_no_servercert_callback_fail);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_large_response);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_large_request);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_client_certificate);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_identity);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_expired_server_cert);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_future_server_cert);
+
+    CuSuiteAddSuite(suite, openssl_suite);
+#endif
+#ifdef SERF_HAVE_MACOSXSSL
+    macosxssl_suite = CuSuiteNew();
+
+    CuSuiteSetSetupTeardownCallbacks(macosxssl_suite, test_macosxssl_setup,
+                                     test_macosxssl_teardown);
+
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_handshake);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_trust_rootca);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_application_rejects_cert);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_certificate_chain_with_anchor);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_certificate_chain_all_from_server);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_no_servercert_callback_allok);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_no_servercert_callback_fail);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_large_response);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_large_request);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_client_certificate);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_identity);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_expired_server_cert);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_future_server_cert);
+
+    CuSuiteAddSuite(suite, macosxssl_suite);
+#endif
+
     return suite;
 }
diff --git a/test/test_serf.h b/test/test_serf.h
index 29ad8a4..3e5b46f 100644
--- a/test/test_serf.h
+++ b/test/test_serf.h
@@ -186,6 +186,10 @@
 
 void *test_setup(void *baton);
 void *test_teardown(void *baton);
+void *test_openssl_setup(void *baton);
+void *test_openssl_teardown(void *baton);
+void *test_macosxssl_setup(void *baton);
+void *test_macosxssl_teardown(void *baton);
 
 typedef struct {
     serf_response_acceptor_t acceptor;
diff --git a/test/test_ssl.c b/test/test_ssl.c
index 54844bd..2775a2f 100644
--- a/test/test_ssl.c
+++ b/test/test_ssl.c
@@ -21,6 +21,9 @@
 #include "serf.h"
 #include "serf_bucket_types.h"
 
+/* Access to internal functions, for DER decoding. */
+#include "buckets/bucket_private.h"
+
 #include "test_serf.h"
 
 #if defined(WIN32) && defined(_DEBUG)
@@ -44,10 +47,13 @@
 
     bkt = serf_bucket_ssl_decrypt_create(stream, NULL,
                                          alloc);
+    CuAssertTrue(tc, SERF_BUCKET_IS_SSL_DECRYPT(bkt));
+
     ssl_context = serf_bucket_ssl_decrypt_context_get(bkt);
 
     bkt = serf_bucket_ssl_encrypt_create(stream, ssl_context,
                                          alloc);
+    CuAssertTrue(tc, SERF_BUCKET_IS_SSL_ENCRYPT(bkt));
 
     status = serf_ssl_use_default_certificates(ssl_context);
 
@@ -81,39 +87,95 @@
     CuAssertPtrNotNull(tc, cert);
 }
 
+typedef struct
+{
+    const char *cn;
+    const char *o;
+    const char *ou;
+    const char *l;
+    const char *st;
+    const char *c;
+    const char *e;
+} test_dn_t;
+
+typedef struct {
+    test_dn_t issuer;
+    test_dn_t subject;
+    const char *sha1;
+    const char *notBefore;
+    const char *notAfter;
+    /* serial number, subjectAltNames */
+} test_cert_t;
+
+static void
+validate_dn(CuTest *tc, const test_dn_t *expected, const apr_hash_t *actual)
+{
+    apr_hash_t *dn = (apr_hash_t *)actual;
+
+    CuAssertStrEquals(tc, expected->cn,
+                      apr_hash_get(dn, "CN", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->ou,
+                      apr_hash_get(dn, "OU", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->o,
+                      apr_hash_get(dn, "O", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->l,
+                      apr_hash_get(dn, "L", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->st,
+                      apr_hash_get(dn, "ST", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->c,
+                      apr_hash_get(dn, "C", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->e,
+                      apr_hash_get(dn, "E", APR_HASH_KEY_STRING));
+}
+
+static void
+validate_cert(CuTest *tc, const test_cert_t *expected, const apr_hash_t *actual)
+{
+    const apr_hash_t *subject, *issuer;
+    apr_hash_t *cert = (apr_hash_t *)actual;
+
+    subject = apr_hash_get(cert, "subject", APR_HASH_KEY_STRING);
+    CuAssertPtrNotNullMsg(tc, "Expected subject", subject);
+
+    validate_dn(tc, &expected->subject, subject);
+
+    issuer = apr_hash_get(cert, "issuer", APR_HASH_KEY_STRING);
+    CuAssertPtrNotNullMsg(tc, "Expected issuer", issuer);
+
+    validate_dn(tc, &expected->issuer, issuer);
+    /*
+     TODO: sha1sum of DER encoded cert for signature.
+     CuAssertStrEquals(tc, expected->sha1,
+     apr_hash_get(cert, "sha1", APR_HASH_KEY_STRING));
+     */
+    CuAssertStrEquals(tc, expected->notBefore,
+                      apr_hash_get(cert, "notBefore", APR_HASH_KEY_STRING));
+    CuAssertStrEquals(tc, expected->notAfter,
+                      apr_hash_get(cert, "notAfter", APR_HASH_KEY_STRING));
+    /* TODO: subjectAltNames */
+}
+
 /* Test that reading the subject from a custom CA certificate file works. */
 static void test_ssl_cert_subject(CuTest *tc)
 {
     apr_hash_t *subject;
     serf_ssl_certificate_t *cert = NULL;
     apr_status_t status;
+    const test_dn_t exp_subject = { "Serf", "In Serf we trust, Inc.",
+        "Test Suite", "Mechelen", "Antwerp", "BE", "serf@example.com" };
 
     apr_pool_t *test_pool = tc->testBaton;
 
     status = serf_ssl_load_cert_file(&cert, get_ca_file(test_pool,
                                                         "test/serftestca.pem"),
                                      test_pool);
-
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertPtrNotNull(tc, cert);
 
     subject = serf_ssl_cert_subject(cert, test_pool);
     CuAssertPtrNotNull(tc, subject);
 
-    CuAssertStrEquals(tc, "Serf",
-                      apr_hash_get(subject, "CN", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "Test Suite",
-                      apr_hash_get(subject, "OU", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "In Serf we trust, Inc.", 
-                      apr_hash_get(subject, "O", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "Mechelen", 
-                      apr_hash_get(subject, "L", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "Antwerp", 
-                      apr_hash_get(subject, "ST", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "BE", 
-                      apr_hash_get(subject, "C", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "serf@example.com", 
-                      apr_hash_get(subject, "E", APR_HASH_KEY_STRING));
+    validate_dn(tc, &exp_subject, subject);
 }
 
 /* Test that reading the issuer from a custom CA certificate file works. */
@@ -122,6 +184,8 @@
     apr_hash_t *issuer;
     serf_ssl_certificate_t *cert = NULL;
     apr_status_t status;
+    const test_dn_t exp_issuer = { "Serf", "In Serf we trust, Inc.",
+        "Test Suite", "Mechelen", "Antwerp", "BE", "serf@example.com" };
 
     apr_pool_t *test_pool = tc->testBaton;
 
@@ -135,21 +199,7 @@
     issuer = serf_ssl_cert_issuer(cert, test_pool);
     CuAssertPtrNotNull(tc, issuer);
 
-    /* TODO: create a new test certificate with different issuer and subject. */
-    CuAssertStrEquals(tc, "Serf",
-                      apr_hash_get(issuer, "CN", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "Test Suite",
-                      apr_hash_get(issuer, "OU", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "In Serf we trust, Inc.",
-                      apr_hash_get(issuer, "O", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "Mechelen",
-                      apr_hash_get(issuer, "L", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "Antwerp",
-                      apr_hash_get(issuer, "ST", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "BE",
-                      apr_hash_get(issuer, "C", APR_HASH_KEY_STRING));
-    CuAssertStrEquals(tc, "serf@example.com",
-                      apr_hash_get(issuer, "E", APR_HASH_KEY_STRING));
+    validate_dn(tc, &exp_issuer, issuer);
 }
 
 /* Test that reading the notBefore,notAfter,sha1 fingerprint and subjectAltNames
@@ -184,7 +234,33 @@
     CuAssertTrue(tc, san_arr == NULL);
 }
 
+static void test_ssl_load_CA_cert_from_file(CuTest *tc)
+{
+    serf_ssl_certificate_t *cert = NULL;
+    serf_bucket_t *bkt, *stream;
+    serf_ssl_context_t *ssl_context;
+    apr_status_t status;
+
+    apr_pool_t *test_pool = tc->testBaton;
+    serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+                                                              NULL);
+
+    stream = SERF_BUCKET_SIMPLE_STRING("", alloc);
+    bkt = serf_bucket_ssl_decrypt_create(stream, NULL, alloc);
+    ssl_context = serf_bucket_ssl_decrypt_context_get(bkt);
+
+    status = serf_ssl_load_CA_cert_from_file(ssl_context,
+                                             &cert,
+                                             get_ca_file(test_pool, "test/serftestca.pem"),
+                                             test_pool);
+
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertPtrNotNull(tc, cert);
+}
+
 static const char *extract_cert_from_pem(const char *pemdata,
+                                         apr_size_t *pemlen,
+                                         int copy_delimiters,
                                          apr_pool_t *pool)
 {
     enum { INIT, CERT_BEGIN, CERT_FOUND } state;
@@ -205,6 +281,7 @@
         const char *data;
         apr_size_t len;
         int found;
+        int delimiter = FALSE;
 
         status = serf_bucket_readline(pembkt, SERF_NEWLINE_ANY, &found,
                                       &data, &len);
@@ -212,31 +289,46 @@
             return NULL;
 
         if (state == INIT) {
-            if (strncmp(begincert, data, strlen(begincert)) == 0)
-                state = CERT_BEGIN;
+            if (strncmp(begincert, data, strlen(begincert)) != 0)
+                continue;
+
+            state = CERT_BEGIN;
+            delimiter = TRUE;
+            if (!copy_delimiters)
+                continue;
         } else if (state == CERT_BEGIN) {
             if (strncmp(endcert, data, strlen(endcert)) == 0)
+            {
                 state = CERT_FOUND;
-            else {
-                certdata = apr_pstrcat(pool, certdata, data, NULL);
-                certlen += len;
-                switch (found) {
-                    case SERF_NEWLINE_CR:
-                    case SERF_NEWLINE_LF:
-                        certdata[certlen-1] = '\0';
-                        certlen --;
-                        break;
-                    case SERF_NEWLINE_CRLF:
-                        certdata[certlen-2] = '\0';
-                        certlen-=2;
-                        break;
-                }
+                delimiter = TRUE;
+                if (!copy_delimiters)
+                    break;
+                certdata = apr_pstrcat(pool, certdata, CRLF, NULL);
+                certlen += 2;
             }
         }
+        /* Copy the line to the output buffer. Remove linefeeds from the
+         base64 DER encoded certificate. */
+        certdata = apr_pstrcat(pool, certdata, data, NULL);
+        certlen += len;
+        switch (found && !delimiter)
+        {
+            case SERF_NEWLINE_CR:
+            case SERF_NEWLINE_LF:
+                certdata[certlen-1] = '\0';
+                certlen--;
+                break;
+            case SERF_NEWLINE_CRLF:
+                certdata[certlen-2] = '\0';
+                certlen-=2;
+                break;
+        }
     }
 
-    if (state == CERT_FOUND)
+    if (state == CERT_FOUND) {
+        *pemlen = certlen;
         return certdata;
+    }
     else
         return NULL;
 }
@@ -275,22 +367,201 @@
     base64derbuf = serf_ssl_cert_export(cert, test_pool);
 
     CuAssertStrEquals(tc,
-                      extract_cert_from_pem(pembuf, test_pool),
+                      extract_cert_from_pem(pembuf, &pemlen, 0, test_pool),
                       base64derbuf);
 }
 
+/****************************************************************************/
+/* Test the DER decoding with certificates collected in file
+   test/certificates.pem.
+ */
+
+static apr_status_t read_certificate(serf_bucket_t *bkt,
+                                     char *buf,
+                                     apr_size_t buf_len,
+                                     apr_size_t *read_len)
+{
+    apr_size_t total_read;
+    apr_status_t status;
+    const char *delimiter = "=========================";
+    int acceptable = SERF_NEWLINE_CRLF | SERF_NEWLINE_LF | SERF_NEWLINE_CR;
+
+    total_read = 0;
+    do
+    {
+        const char *data;
+        apr_size_t len;
+        int found;
+
+        status = serf_bucket_readline(bkt, acceptable, &found,
+                                      &data, &len);
+        if (!SERF_BUCKET_READ_ERROR(status))
+        {
+            if (len >= sizeof(delimiter) &&
+                strncmp(delimiter, data, sizeof(delimiter)) == 0)
+            {
+                /* end of certificate found, return current data. */
+                status = APR_SUCCESS;
+                break;
+            }
+            if (total_read + len > buf_len)
+            {
+                /* Buffer is not large enough to read all data */
+                status = SERF_ERROR_ISSUE_IN_TESTSUITE;
+            }
+            memcpy(buf + total_read, data, len);
+            total_read += len;
+        }
+    } while(status == APR_SUCCESS);
+
+    *read_len = total_read;
+    return status;
+}
+
+const test_cert_t test_certs[] =
+{
+    { { "Serf CA", "In Serf we trust, Inc.", "Test Suite CA",
+        "Mechelen", "Antwerp", "BE", "serfca@example.com"},
+      { "Serf Server", "In Serf we trust, Inc.", "Test Suite Server",
+        "Mechelen", "Antwerp", "BE", "serfserver@example.com"},
+      "", "Apr 18 19:50:11 2013 GMT", "Apr 18 19:50:11 2014 GMT",
+    },
+    { { "Serf Root CA", "In Serf we trust, Inc.", "Test Suite Root CA",
+        "Mechelen", "Antwerp", "BE", "serfrootca@example.com"},
+      { "Serf Root CA", "In Serf we trust, Inc.", "Test Suite Root CA",
+        "Mechelen", "Antwerp", "BE", "serfrootca@example.com"},
+      "", "Apr 13 11:19:14 2013 GMT", "Apr 11 11:19:14 2023 GMT",
+    },
+    { { "Belgium Root CA", NULL, NULL, NULL, NULL, "BE", NULL},
+      { "Belgium Root CA", NULL, NULL, NULL, NULL, "BE", NULL},
+        "", "Jan 26 23:00:00 2003 GMT", "Jan 26 23:00:00 2014 GMT",
+    },
+    { { "Belgium Root CA2", NULL, NULL, NULL, NULL, "BE", NULL},
+      { "Belgium Root CA2", NULL, NULL, NULL, NULL, "BE", NULL},
+      "", "Oct 04 10:00:00 2007 GMT", "Dec 15 08:00:00 2021 GMT",
+    },
+};
+
+static void test_macosxssl_DER_decoding(CuTest *tc)
+{
+#ifdef SERF_HAVE_MACOSXSSL
+    serf_bucket_t *filebkt;
+    apr_file_t *fp;
+    char buf[16384];
+    apr_size_t len;
+    apr_status_t status;
+    int current_cert;
+
+    apr_pool_t *test_pool = tc->testBaton;
+    serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool,
+                                                              NULL, NULL);
+
+    status = apr_file_open(&fp, "test/certificates.pem",
+                           APR_FOPEN_READ | APR_FOPEN_BINARY,
+                           APR_FPROT_OS_DEFAULT, test_pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+    filebkt = serf_bucket_file_create(fp, alloc);
+
+    current_cert = 0;
+    do
+    {
+        status = read_certificate(filebkt, buf, sizeof(buf), &len);
+        CuAssertIntEquals(tc, SERF_BUCKET_READ_ERROR(status), 0);
+
+        /* A certificate was read from test/certificates.pem */
+        if (status == APR_SUCCESS)
+        {
+            apr_size_t pemlen;
+            const char *pemdata = extract_cert_from_pem(buf, &pemlen, 1, test_pool);
+            serf_ssl_certificate_t *cert;
+            apr_hash_t *actual;
+            
+            test_cert_t thiscert = test_certs[current_cert];
+
+/*            printf("Certificate found: %.*s\n\n", pemlen, pemdata);**/
+            status = load_CA_cert_from_buffer(&cert, pemdata, pemlen, test_pool);
+            CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+            status = serf__macosxssl_read_X509_DER_certificate(&actual,
+                                                               cert->impl_cert,
+                                                               test_pool);
+            CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+            validate_cert(tc, &thiscert, actual);
+
+            current_cert++;
+        }
+    } while (status == APR_SUCCESS &&
+             current_cert < sizeof(test_certs) / sizeof(test_certs[0]));
+
+#endif
+}
+
+/* Disable all available SSL implementations, test for no crash. */
+static void test_ssl_no_implementations(CuTest *tc)
+{
+    serf_bucket_t *bkt, *stream;
+
+    apr_pool_t *test_pool = tc->testBaton;
+    serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+                                                              NULL);
+    stream = SERF_BUCKET_SIMPLE_STRING("", alloc);
+
+    serf_config_disable_bucket_impls(SERF_IMPL_SSL_ALL);
+
+    bkt = serf_bucket_ssl_decrypt_create(stream, NULL, alloc);
+    CuAssertPtrEquals(tc, NULL, bkt);
+
+    bkt = serf_bucket_ssl_encrypt_create(stream, NULL, alloc);
+    CuAssertPtrEquals(tc, NULL, bkt);
+
+    serf_config_enable_bucket_impls(SERF_IMPL_SSL_ALL);
+}
+
 CuSuite *test_ssl(void)
 {
     CuSuite *suite = CuSuiteNew();
+    CuSuite *openssl_suite, *macosxssl_suite;
 
     CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown);
 
-    SUITE_ADD_TEST(suite, test_ssl_init);
-    SUITE_ADD_TEST(suite, test_ssl_load_cert_file);
-    SUITE_ADD_TEST(suite, test_ssl_cert_subject);
-    SUITE_ADD_TEST(suite, test_ssl_cert_issuer);
-    SUITE_ADD_TEST(suite, test_ssl_cert_certificate);
-    SUITE_ADD_TEST(suite, test_ssl_cert_export);
+#ifdef SERF_HAVE_OPENSSL
+    openssl_suite = CuSuiteNew();
+
+    CuSuiteSetSetupTeardownCallbacks(openssl_suite, test_openssl_setup,
+                                     test_openssl_teardown);
+
+    SUITE_ADD_TEST(openssl_suite, test_ssl_init);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_load_cert_file);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_cert_subject);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_cert_issuer);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_cert_certificate);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_load_CA_cert_from_file);
+    SUITE_ADD_TEST(openssl_suite, test_ssl_cert_export);
+    SUITE_ADD_TEST(openssl_suite, test_macosxssl_DER_decoding);
+
+    CuSuiteAddSuite(suite, openssl_suite);
+#endif
+#ifdef SERF_HAVE_MACOSXSSL
+    macosxssl_suite = CuSuiteNew();
+
+    CuSuiteSetSetupTeardownCallbacks(macosxssl_suite, test_macosxssl_setup,
+                                     test_macosxssl_teardown);
+
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_init);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_load_cert_file);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_cert_subject);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_cert_issuer);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_cert_certificate);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_load_CA_cert_from_file);
+    SUITE_ADD_TEST(macosxssl_suite, test_ssl_cert_export);
+    SUITE_ADD_TEST(macosxssl_suite, test_macosxssl_DER_decoding);
+
+    CuSuiteAddSuite(suite, macosxssl_suite);
+#endif
+
+    SUITE_ADD_TEST(suite, test_ssl_no_implementations);
 
     return suite;
 }
diff --git a/test/test_util.c b/test/test_util.c
index c9d6aab..ff37fe2 100644
--- a/test/test_util.c
+++ b/test/test_util.c
@@ -378,6 +378,33 @@
     return NULL;
 }
 
+void *test_openssl_setup(void *dummy)
+{
+    serf_config_disable_bucket_impls(SERF_IMPL_SSL_ALL);
+    serf_config_enable_bucket_impls(SERF_IMPL_SSL_OPENSSL);
+
+    return test_setup(dummy);
+}
+
+void *test_openssl_teardown(void *baton)
+{
+    serf_config_enable_bucket_impls(SERF_IMPL_SSL_ALL);
+    return test_teardown(baton);
+}
+
+void *test_macosxssl_setup(void *dummy)
+{
+    serf_config_disable_bucket_impls(SERF_IMPL_SSL_ALL);
+    serf_config_enable_bucket_impls(SERF_IMPL_SSL_MACOSXSSL);
+    return test_setup(dummy);
+}
+
+void *test_macosxssl_teardown(void *baton)
+{
+    serf_config_enable_bucket_impls(SERF_IMPL_SSL_ALL);
+    return test_teardown(baton);
+}
+
 /* Helper function, runs the client and server context loops and validates
  that no errors were encountered, and all messages were sent and received. */
 apr_status_t