diff --git a/configure b/configure
index 23ebfa8..6bdc96a 100755
--- a/configure
+++ b/configure
@@ -708,6 +708,7 @@ UUID_EXTRA_OBJS
 with_uuid
 with_systemd
 with_selinux
+with_securetransport
 with_openssl
 with_ldap
 with_krb_srvnam
@@ -854,6 +855,7 @@ with_bsd_auth
 with_ldap
 with_bonjour
 with_openssl
+with_securetransport
 with_selinux
 with_systemd
 with_readline
@@ -1554,6 +1556,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-securetransport  build with Apple Secure Transport support
   --with-selinux          build with SELinux support
   --with-systemd          build with systemd support
   --without-readline      do not use GNU Readline nor BSD Libedit for editing
@@ -7968,6 +7971,41 @@ $as_echo "$with_openssl" >&6; }
 
 
 #
+# Apple Secure Transport
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with Apple Secure Transport support" >&5
+$as_echo_n "checking whether to build with Apple Secure Transport support... " >&6; }
+
+
+
+# Check whether --with-securetransport was given.
+if test "${with_securetransport+set}" = set; then :
+  withval=$with_securetransport;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_SECURETRANSPORT 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-securetransport option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_securetransport=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_securetransport" >&5
+$as_echo "$with_securetransport" >&6; }
+
+
+#
 # SELinux
 #
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with SELinux support" >&5
@@ -12998,6 +13036,25 @@ fi
 
 fi
 
+if test "$with_securetransport" = yes ; then
+  ac_fn_c_check_header_mongrel "$LINENO" "Security/Security.h" "ac_cv_header_Security_Security_h" "$ac_includes_default"
+if test "x$ac_cv_header_Security_Security_h" = xyes; then :
+
+else
+  as_fn_error $? "header file <Security/Security.h> is required for Apple Secure Transport" "$LINENO" 5
+fi
+
+
+  ac_fn_c_check_header_mongrel "$LINENO" "CoreFoundation/CoreFoundation.h" "ac_cv_header_CoreFoundation_CoreFoundation_h" "$ac_includes_default"
+if test "x$ac_cv_header_CoreFoundation_CoreFoundation_h" = xyes; then :
+
+else
+  as_fn_error $? "header file <CoreFoundation/CoreFoundation.h> is required for Apple Secure Transport" "$LINENO" 5
+fi
+
+
+fi
+
 if test "$with_pam" = yes ; then
   for ac_header in security/pam_appl.h
 do :
diff --git a/configure.in b/configure.in
index 530f275..58c9883 100644
--- a/configure.in
+++ b/configure.in
@@ -841,6 +841,15 @@ AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
 #
+# Apple Secure Transport
+#
+AC_MSG_CHECKING([whether to build with Apple Secure Transport support])
+PGAC_ARG_BOOL(with, securetransport, no, [build with Apple Secure Transport support],
+			  [AC_DEFINE([USE_SECURETRANSPORT], 1, [Define to build with Apple Secure Transport support.  (--with-securetransport)])])
+AC_MSG_RESULT([$with_securetransport])
+AC_SUBST(with_securetransport)
+
+#
 # SELinux
 #
 AC_MSG_CHECKING([whether to build with SELinux support])
@@ -1367,6 +1376,11 @@ if test "$with_openssl" = yes ; then
   AC_CHECK_HEADER(openssl/err.h, [], [AC_MSG_ERROR([header file <openssl/err.h> is required for OpenSSL])])
 fi
 
+if test "$with_securetransport" = yes ; then
+  AC_CHECK_HEADER(Security/Security.h, [], [AC_MSG_ERROR([header file <Security/Security.h> is required for Apple Secure Transport])])
+  AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [], [AC_MSG_ERROR([header file <CoreFoundation/CoreFoundation.h> is required for Apple Secure Transport])])
+fi
+
 if test "$with_pam" = yes ; then
   AC_CHECK_HEADERS(security/pam_appl.h, [],
                    [AC_CHECK_HEADERS(pam/pam_appl.h, [],
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 9cf0c35..48ee631 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -185,6 +185,7 @@ with_perl	= @with_perl@
 with_python	= @with_python@
 with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
+with_securetransport = @with_securetransport@
 with_selinux	= @with_selinux@
 with_systemd	= @with_systemd@
 with_gssapi	= @with_gssapi@
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 3a58bf6..a93b9a7 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -51,6 +51,10 @@ ifeq ($(with_systemd),yes)
 LIBS += -lsystemd
 endif
 
+ifeq ($(with_securetransport),yes)
+LIBS += -framework CoreFoundation -framework Security
+endif
+
 ##########################################################################
 
 all: submake-libpgport submake-catalog-headers submake-utils-headers postgres $(POSTGRES_IMP)
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 3dbec23..3f8a8b9 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,9 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif
 
+ifeq ($(with_securetransport),yes)
+OBJS += be-secure-securetransport.o
+override CFLAGS += -framework Security -framework CoreFoundation
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/be-secure-securetransport.c b/src/backend/libpq/be-secure-securetransport.c
new file mode 100644
index 0000000..6c3f36c
--- /dev/null
+++ b/src/backend/libpq/be-secure-securetransport.c
@@ -0,0 +1,1447 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-securetransport.c
+ *	  Apple Secure Transport support
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * TODO:
+ *		- It would be good to be able to set "not applicable" on some options
+ *		  like compression which isn't supported in Secure Transport (and most
+ *		  likely any other SSL libraries supported in the future).
+ *		- Support memory allocation in Secure Transport via a custom Core
+ *		  Foundation allocator which is backed by a MemoryContext? Not sure it
+ *		  would be possible but would be interested to investigate.
+ *
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-securetransport.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include "common/base64.h"
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "storage/fd.h"
+#include "storage/latch.h"
+#include "tcop/tcopprot.h"
+#include "utils/backend_random.h"
+#include "utils/memutils.h"
+
+/*
+ * TODO: This dance is required due to collisions in the CoreFoundation
+ * headers. How to handle it properly?
+ */
+#define pg_ACL_DELETE ACL_DELETE
+#define pg_ACL_EXECUTE ACL_EXECUTE
+#undef ACL_EXECUTE
+#undef ACL_DELETE
+#define Size pg_Size
+#define uint64 pg_uint64
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+#include "common/securetransport.h"
+#undef uint64
+#undef Size
+#undef ACL_DELETE
+#undef ACL_EXECUTE
+#define pg_uint64 uint64
+#define pg_Size Size
+#define ACL_DELETE pg_ACL_DELETE
+#define ACL_EXECUTE pg_ACL_EXECUTE
+
+#ifndef errSecUnknownFormat
+#define errSecUnknownFormat -25257
+#endif
+
+#define KC_PREFIX		"keychain:"
+#define KC_PREFIX_LEN	(strlen("keychain:"))
+
+/* ------------------------------------------------------------ */
+/*				Struct definitions and Static variables			*/
+/* ------------------------------------------------------------ */
+
+/*
+ * For Secure Transport API functions we rely on SecCopyErrorMessageString()
+ * which will provide a human readable error message for the individual error
+ * statuses. For our static functions, we mimic the behaviour by passing
+ * errSecInternalError and setting the error message in internal_err. Since we
+ * may have encountered an error due to memory pressure, we don't want to rely
+ * on dynamically allocating memory for this error message.
+ */
+#define ERR_MSG_LEN 128
+static char internal_err[ERR_MSG_LEN];
+
+static bool ssl_is_server_start;
+
+/* ------------------------------------------------------------ */
+/*							Prototypes							*/
+/* ------------------------------------------------------------ */
+
+/*
+ * SecIdentityCreate is not an exported Secure Transport API. There is however
+ * no exported Secure Transport function that can create an identity from a
+ * SecCertificateRef and a SecKeyRef without having either of them in a
+ * Keychain. This function is commonly used in open source projects (such as
+ * ffmpeg and mono f.e), but finding an alternative is a TODO.
+ */
+extern SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
+										SecCertificateRef certificate,
+										SecKeyRef privateKey);
+
+static bool load_key(char *name, CFArrayRef *out);
+static OSStatus load_keychain(char *name, CFArrayRef *keychains);
+static OSStatus load_certificate_file(char *name, CFArrayRef *cert_array);
+static OSStatus load_identity_keychain(const char *common_name,
+									   SecIdentityRef *identity,
+									   CFArrayRef keychains);
+
+static int load_dh_file(char *filename, char **buf);
+static void load_dh_params(char *dh, int len, bool is_pem, SSLContextRef ssl);
+static char * pg_SSLerrmessage(OSStatus status);
+static OSStatus pg_SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len);
+static OSStatus pg_SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len);
+static void KeychainEnsureValid(CFArrayRef *keychains);
+
+/* ------------------------------------------------------------ */
+/*							Backend API							*/
+/* ------------------------------------------------------------ */
+
+/*
+ * be_tls_init
+ *		Initialize global Secure Transport structures (if any)
+ *
+ * This is where we'd like to load and parse certificates and private keys
+ * for the connection, but since Secure Transport will spawn threads deep
+ * inside the API we must postpone this until inside a backend. This means
+ * that we won't fail on an incorrect certificate chain until a connection
+ * is attempted, unlike with OpenSSL where we fail immediately on server
+ * startup.
+ *
+ * Another reason to defer this until when in the backend is that Keychains
+ * are SQLite3 backed, and sqlite does not allow access across a fork. See
+ * https://sqlite.org/faq.html#q6 for more information.
+ */
+int
+be_tls_init(bool isServerStart)
+{
+	memset(internal_err, '\0', sizeof(internal_err));
+
+	ssl_is_server_start = isServerStart;
+
+	return 0;
+}
+
+/*
+ * be_tls_destroy
+ *		Tear down global Secure Transport structures and return resources.
+ */
+void
+be_tls_destroy(void)
+{
+	ssl_loaded_verify_locations = false;
+}
+
+/*
+ * be_tls_open_server
+ *		Attempt to negotiate a secure connection
+ *
+ * Unlike the OpenSSL backend, this function is responsible for the deferred
+ * loading of keys and certificates for use in the connection. See the comment
+ * on be_tls_init for further reasoning around this.
+ */
+int
+be_tls_open_server(Port *port)
+{
+	OSStatus			status;
+	SecTrustRef			trust;
+	SecTrustResultType	trust_eval;
+	SecIdentityRef		identity;
+	CFArrayRef			root_certificates;
+	CFArrayRef			certificates;
+	CFArrayRef			keys;
+	CFArrayRef			keychains = NULL;
+	CFMutableArrayRef	chain;
+	char			   *dh_buf;
+	int					dh_len;
+
+	Assert(!port->ssl);
+
+	if (ssl_keychain_file[0])
+	{
+		status = load_keychain(ssl_keychain_file, &keychains);
+		if (status != noErr)
+			ereport(COMMERROR,
+					(errmsg("could not load keychain(s): \"%s\"",
+							pg_SSLerrmessage(status))));
+	}
+
+	/*
+	 * If the ssl_cert_file is prefixed with a keychain reference, we will try
+	 * to load a complete identity from the Keychain.
+	 */
+	if (pg_strncasecmp(ssl_cert_file, KC_PREFIX, KC_PREFIX_LEN) == 0)
+		status = load_identity_keychain(ssl_cert_file + KC_PREFIX_LEN, &identity, keychains);
+	else
+	{
+		status = load_certificate_file(ssl_cert_file, &certificates);
+		if (status != noErr)
+			ereport(COMMERROR,
+					(errmsg("could not load server certificate \"%s\": \"%s\"",
+							ssl_cert_file, pg_SSLerrmessage(status))));
+
+		if (!load_key(ssl_key_file, &keys))
+			return -1;
+
+		/*
+		 * We now have a certificate and either a private key, or a search path
+		 * which should contain it.
+		 */
+		identity = SecIdentityCreate(NULL,
+									 (SecCertificateRef) CFArrayGetValueAtIndex(certificates, 0),
+									 (SecKeyRef) CFArrayGetValueAtIndex(keys, 0));
+	}
+
+	if (identity == NULL)
+		ereport(COMMERROR,
+				(errmsg("could not create identity: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	/*
+	 * SSLSetCertificate() sets the certificate(s) to use for the connection.
+	 * The first element in the passed array is required to be the identity
+	 * with elements 1..n being certificates.
+	 */
+	chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+	CFRetain(identity);
+	CFArrayInsertValueAtIndex(chain, 0, identity);
+	CFArrayAppendArray(chain, certificates,
+					   CFRangeMake(0, CFArrayGetCount(certificates)));
+
+	/*
+	 * Load the Certificate Authority if configured
+	 */
+	if (ssl_ca_file[0])
+	{
+		status = load_certificate_file(ssl_ca_file, &root_certificates);
+		if (status == noErr)
+		{
+			CFArrayAppendArray(chain, root_certificates,
+							   CFRangeMake(0, CFArrayGetCount(root_certificates)));
+
+			ssl_loaded_verify_locations = true;
+		}
+		else
+		{
+			ereport(LOG,
+					(errmsg("could not load root certificate \"%s\": \"%s\"",
+					 ssl_ca_file, pg_SSLerrmessage(status))));
+
+			ssl_loaded_verify_locations = false;
+		}
+	}
+	else
+		ssl_loaded_verify_locations = false;
+
+	/*
+	 * Certificate Revocation List are not supported in the Secure Transport
+	 * API
+	 */
+	if (ssl_crl_file[0])
+		ereport(FATAL,
+				(errmsg("CRL files not supported with Secure Transport")));
+
+	port->ssl = (void *) SSLCreateContext(NULL, kSSLServerSide, kSSLStreamType);
+	if (!port->ssl)
+		ereport(COMMERROR,
+				(errmsg("could not create SSL context")));
+
+	port->ssl_in_use = true;
+	port->ssl_buffered = 0;
+
+	/*
+	 * We use kTryAuthenticate here since we don't know which sslmode the
+	 * client is using. If we were to use kAlwaysAuthenticate then sslmode
+	 * require won't work as intended.
+	 */
+	if (ssl_loaded_verify_locations)
+		SSLSetClientSideAuthenticate((SSLContextRef) port->ssl, kTryAuthenticate);
+
+	/*
+	 * In case the user hasn't configured a DH parameters file, we load a pre-
+	 * computed DH parameter to avoid having Secure Transport computing one for
+	 * us (which is done by default unless one is set).
+	 */
+	dh_buf = NULL;
+	if (ssl_dh_params_file[0])
+		dh_len = load_dh_file(ssl_dh_params_file, &dh_buf);
+
+	if (!dh_buf || dh_len == 0)
+	{
+		dh_buf = pstrdup(FILE_DH2048);
+		dh_len = sizeof(FILE_DH2048);
+	}
+
+	load_dh_params(dh_buf, dh_len, true, (SSLContextRef) port->ssl);
+
+	/*
+	 * Set Tlsv1.2 as the minimum protocol version we allow for the connection
+	 */
+	status = SSLSetProtocolVersionMin((SSLContextRef) port->ssl,
+									  kTLSProtocol12);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set protocol for connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetCertificate((SSLContextRef) port->ssl,
+							   (CFArrayRef) chain);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set certificate for connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetIOFuncs((SSLContextRef) port->ssl,
+						   pg_SSLSocketRead,
+						   pg_SSLSocketWrite);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set SSL IO functions: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetSessionOption((SSLContextRef) port->ssl,
+								 kSSLSessionOptionBreakOnClientAuth, true);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set SSL certificate validation: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetConnection((SSLContextRef) port->ssl, port);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not establish SSL connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	/*
+	 * We can't initiate the SSL structures in _init as per how OpenSSL does
+	 * it, so we can't use serverstart for the same error handling. We still
+	 * use it for a few things so maintain it for a single instantiation.
+	 * TODO: it would be neat if this hack isn't required, more thinking is
+	 * required.
+	 */
+	ssl_is_server_start = false;
+
+	/*
+	 * Perform handshake
+	 */
+	for (;;)
+	{
+		status = SSLHandshake((SSLContextRef) port->ssl);
+		if (status == noErr)
+			break;
+
+		if (status == errSSLWouldBlock)
+			continue;
+
+		if (status == errSSLClosedAbort || status == errSSLClosedGraceful)
+			return -1;
+
+		if (status == errSSLPeerAuthCompleted)
+		{
+			status = SSLCopyPeerTrust((SSLContextRef) port->ssl, &trust);
+			if (status != noErr || trust == NULL)
+			{
+				ereport(WARNING,
+					(errmsg("SSLCopyPeerTrust returned: \"%s\"",
+					 pg_SSLerrmessage(status))));
+				port->peer_cert_valid = false;
+				return 0;
+			}
+
+			if (ssl_loaded_verify_locations)
+			{
+				status = SecTrustSetAnchorCertificates(trust, root_certificates);
+				if (status != noErr)
+				{
+					ereport(WARNING,
+							(errmsg("SecTrustSetAnchorCertificates returned: \"%s\"",
+							 pg_SSLerrmessage(status))));
+					return -1;
+				}
+
+				status = SecTrustSetAnchorCertificatesOnly(trust, false);
+				if (status != noErr)
+				{
+					ereport(WARNING,
+							(errmsg("SecTrustSetAnchorCertificatesOnly returned: \"%s\"",
+							 pg_SSLerrmessage(status))));
+					return -1;
+				}
+			}
+
+			trust_eval = 0;
+			status = SecTrustEvaluate(trust, &trust_eval);
+			if (status != noErr)
+			{
+				ereport(WARNING,
+						(errmsg("SecTrustEvaluate failed, returned: \"%s\"",
+						 pg_SSLerrmessage(status))));
+				return -1;
+			}
+
+			switch (trust_eval)
+			{
+				/*
+				 * If 'Unspecified' then an anchor certificate was reached
+				 * without encountering any explicit user trust. If 'Proceed'
+				 * then the user has chosen to explicitly trust a certificate
+				 * in the chain by clicking "Trust" in the Keychain app.
+				 */
+				case kSecTrustResultUnspecified:
+				case kSecTrustResultProceed:
+					port->peer_cert_valid = true;
+					break;
+
+				/*
+				 * 'RecoverableTrustFailure' indicates that the certificate was
+				 * rejected but might be trusted with minor changes to the eval
+				 * context (ignoring expired certificate etc). In the frontend
+				 * we can in some circumstances allow this, but in the backend
+				 * this always means that the client certificate is considered
+				 * untrusted.
+				 */
+				case kSecTrustResultRecoverableTrustFailure:
+					port->peer_cert_valid = false;
+					break;
+
+				/*
+				 * Treat all other cases as rejection without further
+				 * questioning.
+				 */
+				default:
+					port->peer_cert_valid = false;
+					break;
+			}
+
+			if (port->peer_cert_valid)
+			{
+				SecCertificateRef	usercert;
+				CFStringRef			usercert_cn;
+				const char		   *peer_cn;
+
+				usercert = SecTrustGetCertificateAtIndex(trust, 0L);
+				SecCertificateCopyCommonName(usercert, &usercert_cn);
+
+				/* Guard against empty/missing CNs */
+				peer_cn = CFStringGetCStringPtr(usercert_cn, kCFStringEncodingUTF8);
+				if (!peer_cn)
+					port->peer_cn = pstrdup("");
+				else
+					port->peer_cn = pstrdup(peer_cn);
+
+				CFRelease(usercert_cn);
+			}
+
+			CFRelease(trust);
+		}
+	}
+
+	if (status != noErr)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * load_key
+ *		Extracts a key from a PEM file on the filesystem
+ */
+static bool
+load_key(char *name, CFArrayRef *out)
+{
+	OSStatus			status;
+	struct stat			stat_buf;
+	int					ret;
+	UInt8			   *buf;
+	FILE			   *fd;
+	CFDataRef			data;
+	SecExternalFormat	format;
+	SecExternalItemType	type;
+	CFStringRef			path;
+	SecItemImportExportKeyParameters params;
+
+	if (!check_ssl_key_file_permissions(name, ssl_is_server_start))
+		return false;
+
+	/*
+	 * check_ssl_key_file_permissions() has already checked the file for
+	 * existence and correct permissions, but we still need to stat it to
+	 * get the filesize.
+	 */
+	if (stat(name, &stat_buf) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": unable to open",
+						name)));
+
+	if ((fd = AllocateFile(name, "r")) == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": unable to open",
+						name)));
+
+	buf = palloc(stat_buf.st_size);
+
+	ret = fread(buf, 1, stat_buf.st_size, fd);
+	FreeFile(fd);
+
+	if (ret != stat_buf.st_size)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": unable to read",
+						name)));
+
+	type = kSecItemTypePrivateKey;
+	format = kSecFormatPEMSequence;
+	path = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
+	data = CFDataCreate(NULL, buf, stat_buf.st_size);
+
+	memset(&params, 0, sizeof(SecItemImportExportKeyParameters));
+	params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+	/* Set OS default access control on the imported key */
+	params.flags = kSecKeyNoAccessControl;
+
+	status = SecItemImport(data, path, &format, &type, 0, &params, NULL, out);
+
+	/*
+	 * There is no way to set a callback for acquiring the passphrase like how
+	 * OpenSSL does it, so we need to re-run the import if it failed with a
+	 * passphrase missing status. If no ssl_passphrase_command has been set we
+	 * currently don't retry, which is something that will need to be revisited.
+	 * TODO: figure out what would be the least confusing to the user here;
+	 * perhaps supplying our own fallback passphrase_command? (which should be
+	 * a TLS backend common function since it wouldn't be Secure Transport
+	 * specific?)
+	 */
+	if (status == errSecPassphraseRequired)
+	{
+		if ((ssl_is_server_start && ssl_passphrase_command[0]) ||
+			(!ssl_is_server_start && ssl_passphrase_command[0] && ssl_passphrase_command_supports_reload))
+		{
+			const char	   *prompt = "Enter PEM pass phrase: ";
+			char			buf[256];
+			CFStringRef		passphrase;
+
+			run_ssl_passphrase_command(prompt, ssl_is_server_start, buf, sizeof(buf));
+
+			passphrase = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
+			params.passphrase = passphrase;
+			memset(&buf, '\0', sizeof(buf));
+
+			status = SecItemImport(data, path, &format, &type, 0, &params, NULL, out);
+
+			CFRelease(passphrase);
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("private key file \"%s\" cannot be loaded because it requires a passphrase",
+							name)));
+	}
+
+	CFRelease(path);
+	CFRelease(data);
+
+	if (status != noErr)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": \"%s\"",
+						name, pg_SSLerrmessage(status))));
+
+	return true;
+}
+
+/*
+ * load_keychain
+ *		Open the specified, and default, Keychain
+ *
+ * Operations on keychains other than the default take the keychain references
+ * in an array. We need to copy a reference to the default keychain into the
+ * array to include it in the search.
+ *
+ * For server applications, we don't want modal dialog boxes opened for
+ * Keychain interaction. Calling SecKeychainSetUserInteractionAllowed(FALSE)
+ * will turn off all GUI interaction with the user, which may seem like what we
+ * want server side. This however has the side effect to turn off all GUI
+ * elements for all applications until some application calls
+ * SecKeychainSetUserInteractionAllowed(TRUE) or reboots the box. We might thus
+ * remove wanted GUI interaction from another app, or another app might
+ * introduce it for us.
+ */
+static OSStatus
+load_keychain(char *name, CFArrayRef *keychains)
+{
+	OSStatus			status;
+	struct stat			stat_buf;
+	SecKeychainRef		kc[2];
+	int					array_len = 1;
+
+	/*
+	 * If we are passing in a non-empty CFArrayRef, and fail to load the user
+	 * Keychain then we would risk injecting Keychains since we will trust this
+	 * array from hereon. Unconditionally error out hard immediately to avoid.
+	 */
+	if (*keychains != NULL)
+		ereport(FATAL,
+				(errmsg("Requesting to load Keychains into already allocated memory")));
+
+	if (stat(name, &stat_buf) != 0)
+		return errSecInvalidValue;
+
+	status = SecKeychainOpen(name, &kc[0]);
+	if (status == noErr)
+	{
+		SecKeychainUnlock(kc[0], 0, "", TRUE);
+
+		/*
+		 * If we are allowed to use the default Keychain, add it to the array
+		 * to include it in Keychain searches. If we are only using the default
+		 * Keychain and no user defined Keychain we don't create an array at
+		 * all since the recommended procedure is to pass NULL instead of an
+		 * array containing only a reference to the default Keychain.
+		 */
+		if (ssl_keychain_use_default)
+			SecKeychainCopyDefault(&kc[array_len++]);
+
+		*keychains = CFArrayCreate(NULL, (const void **) kc, array_len,
+								   &kCFTypeArrayCallBacks);
+
+		if (!*keychains)
+		{
+			snprintf(internal_err, ERR_MSG_LEN, "unable to allocate memory");
+			return errSecInternalError;
+		}
+	}
+
+	return status;
+}
+
+/*
+ * import_identity_keychain
+ *		Import the identity for the specified certificate from a Keychain
+ *
+ * Queries the specified Keychain, or the default unless not defined, for a
+ * identity with a certificate matching the passed certificate reference.
+ * Keychains are searched by creating a dictionary of key/value pairs with the
+ * search criteria and then asking for a copy of the matching entry/entries to
+ * the search criteria.
+ */
+static OSStatus
+load_identity_keychain(const char *common_name, SecIdentityRef *identity,
+							CFArrayRef keychains)
+{
+	OSStatus				status = errSecItemNotFound;
+	CFMutableDictionaryRef	query;
+	CFStringRef				cert;
+	CFArrayRef				temp;
+
+	/* Ensure we have a correctly set Keychains array */
+	KeychainEnsureValid(&keychains);
+
+	/*
+	 * Make sure the user didn't just specify keychain: as the sslcert config.
+	 * The passed certificate will have the keychain prefix stripped so in that
+	 * case the string is expected to be empty here.
+	 */
+	if (strlen(common_name) == 0)
+		return errSecInvalidValue;
+
+	query = CFDictionaryCreateMutable(NULL, 0,
+									  &kCFTypeDictionaryKeyCallBacks,
+									  &kCFTypeDictionaryValueCallBacks);
+
+	cert = CFStringCreateWithCString(NULL, common_name, kCFStringEncodingUTF8);
+
+	/*
+	 * If we didn't get a Keychain passed, skip adding it to the dictionary
+	 * thus prompting a search in the users default Keychain.
+	 */
+	if (keychains)
+		CFDictionaryAddValue(query, kSecMatchSearchList, keychains);
+
+	CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
+	CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+	CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
+	CFDictionaryAddValue(query, kSecMatchPolicy, SecPolicyCreateSSL(true, NULL));
+	CFDictionaryAddValue(query, kSecAttrLabel, cert);
+
+	/*
+	 * Normally we could have used kSecMatchLimitOne in the above dictionary
+	 * but since there are versions of macOS where the certificate matching on
+	 * the label doesn't work, we need to request all and find the one we want.
+	 * Copy all the results to a temp array and scan it for the certificate we
+	 * are interested in.
+	 */
+	status = SecItemCopyMatching(query, (CFTypeRef *) &temp);
+	if (status == noErr)
+	{
+		OSStatus		search_stat;
+		SecIdentityRef	dummy;
+		int				i;
+
+		for (i = 0; i < CFArrayGetCount(temp); i++)
+		{
+			SecCertificateRef	search_cert;
+			CFStringRef			cn;
+
+			dummy = (SecIdentityRef) CFArrayGetValueAtIndex(temp, i);
+			search_stat = SecIdentityCopyCertificate(dummy, &search_cert);
+
+			if (search_stat == noErr && search_cert != NULL)
+			{
+				SecCertificateCopyCommonName(search_cert, &cn);
+				if (CFStringCompare(cn, cert, 0) == kCFCompareEqualTo)
+				{
+					CFRelease(cn);
+					CFRelease(search_cert);
+					*identity = (SecIdentityRef) CFRetain(dummy);
+					break;
+				}
+
+				CFRelease(cn);
+				CFRelease(search_cert);
+			}
+		}
+
+		CFRelease(temp);
+	}
+
+	CFRelease(query);
+	CFRelease(cert);
+
+	return status;
+}
+
+/*
+ * load_certificate_file
+ *		Extracts a certificate from a PEM file on the filesystem
+ */
+static OSStatus
+load_certificate_file(char *name, CFArrayRef *cert_array)
+{
+	struct stat			stat_buf;
+	int					ret;
+	UInt8			   *buf;
+	FILE			   *fd;
+	CFDataRef			data;
+	SecExternalFormat	format;
+	SecExternalItemType	type;
+	CFStringRef			path;
+	OSStatus			status;
+
+	ret = stat(name, &stat_buf);
+	if (ret != 0 && errno == ENOENT)
+	{
+		snprintf(internal_err, ERR_MSG_LEN, "unable to find file");
+		return errSecInternalError;
+	}
+	else if (ret == 0 && S_ISREG(stat_buf.st_mode))
+	{
+		if ((fd = AllocateFile(name, "r")) == NULL)
+		{
+			snprintf(internal_err, ERR_MSG_LEN, "unable to open file for reading");
+			return errSecInternalError;
+		}
+
+		buf = palloc(stat_buf.st_size);
+		ret = fread(buf, 1, stat_buf.st_size, fd);
+		FreeFile(fd);
+
+		if (ret != stat_buf.st_size)
+		{
+			snprintf(internal_err, ERR_MSG_LEN, "unable to read file");
+			return errSecInternalError;
+		}
+
+		type = kSecItemTypeCertificate;
+		format = kSecFormatPEMSequence;
+		path = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
+		data = CFDataCreate(NULL, buf, stat_buf.st_size);
+
+		status = SecItemImport(data, path, &format, &type, 0, NULL, NULL,
+							   cert_array);
+		pfree(buf);
+
+		return status;
+	}
+
+	snprintf(internal_err, ERR_MSG_LEN, "unable to open file for reading");
+	return errSecInternalError;
+}
+
+/*
+ * load_dh_file
+ *		Slurp the contents of the specified file into buf
+ *
+ * Open the supplied filename and load its contents. This function is only
+ * reading the data without assessing its structure, actually parsing it is
+ * performed by load_dh_params(). The reason for splitting up the process is
+ * that we also support loading hardcoded DH params.
+ */
+static int
+load_dh_file(char *filename, char **buf)
+{
+	FILE		   *dh;
+	struct stat		stat_buf;
+	int				ret;
+
+	/*
+	 * Open the DH file and slurp the contents. If the file doesn't exist it's
+	 * not an error, if it can't be opened it is however an error.
+	 */
+	ret = stat(filename, &stat_buf);
+	if (ret != 0 && errno == ENOENT)
+		return 0;
+	else if (ret == 0 && S_ISREG(stat_buf.st_mode))
+	{
+		if ((dh = AllocateFile(filename, "r")) == NULL)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not open DH parameters file \"%s\": %m",
+							filename)));
+
+		*buf = palloc(stat_buf.st_size);
+		ret = fread(*buf, 1, stat_buf.st_size, dh);
+		FreeFile(dh);
+
+		if (ret != stat_buf.st_size)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read DH parameters file \"%s\": %m",
+							filename)));
+	}
+	else
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("DH parameters file \"%s\" is not a regular file",
+						filename)));
+
+	return ret;
+}
+
+/*
+ * load_dh_params
+ *		Load the specified DH params for the connection
+ *
+ * Secure Transport requires the DH params to be in DER format, but to be
+ * compatible with the OpenSSL code we also support PEM and convert to DER
+ * before loading.  Conversion does rudimentary PEM parsing, if we miss the
+ * data being correct, the Secure Transport API will give an error anyways so
+ * we're just checking basic integrity.
+ *
+ * This function may scribble on the dh parameter so if that's required so stay
+ * intact in the caller, a copy should be sent.
+ */
+#define DH_HEADER "-----BEGIN DH PARAMETERS-----"
+#define DH_FOOTER "-----END DH PARAMETERS-----"
+static void
+load_dh_params(char *dh, int len, bool is_pem, SSLContextRef ssl)
+{
+	OSStatus	status;
+	char	   *der;
+	int			der_len;
+
+	Assert(dh);
+
+	/* Convert PEM to DER */
+	if (is_pem)
+	{
+		char   *head;
+		char   *tail;
+		int		pem_len = 0;
+
+		if (strstr(dh, DH_HEADER) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("Invalid PEM format for DH parameter, header missing")));
+
+		dh += strlen(DH_HEADER);
+		tail = strstr(dh, DH_FOOTER);
+		if (!tail)
+			ereport(ERROR,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("Invalid PEM format for DH parameter, footer missing")));
+		*tail = '\0';
+
+		/* In order to PEM convert it we need to remove all newlines */
+		head = dh;
+		tail = dh;
+		while (*head != '\0')
+		{
+			if (*head != '\n')
+			{
+				*tail++ = *head++;
+				pem_len++;
+			}
+			else
+				head++;
+		}
+		*tail = '\0';
+
+		der = palloc(pg_b64_dec_len(strlen(dh)) + 1);
+		der_len = pg_b64_decode(dh, strlen(dh), der);
+		der[der_len] = '\0';
+	}
+	else
+	{
+		der = dh;
+		der_len = len;
+	}
+
+	status = SSLSetDiffieHellmanParams(ssl, der, der_len);
+	if (status != noErr)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("unable to load DH parameters: %s",
+						pg_SSLerrmessage(status))));
+}
+
+/*
+ * be_tls_close
+ *		Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	OSStatus		ssl_status;
+
+	if (!port->ssl)
+		return;
+
+	ssl_status = SSLClose((SSLContextRef) port->ssl);
+	if (ssl_status != noErr)
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("error in closing SSL connection: %s",
+						pg_SSLerrmessage(ssl_status))));
+
+	CFRelease((SSLContextRef) port->ssl);
+
+	port->ssl = NULL;
+	port->ssl_in_use = false;
+}
+
+/*
+ * be_tls_get_version
+ *		Retrieve the protocol version of the current connection
+ */
+const char *
+be_tls_get_version(Port *port)
+{
+	OSStatus		status;
+	SSLProtocol		protocol;
+
+	if (!(SSLContextRef) port->ssl)
+		elog(ERROR, "No SSL connection");
+
+	status = SSLGetNegotiatedProtocolVersion((SSLContextRef) port->ssl, &protocol);
+	if (status != noErr)
+		ereport(ERROR,
+				(errmsg("could not detect TLS version for connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	if (protocol == kTLSProtocol11)
+		return "TLSv1.1";
+	else if (protocol == kTLSProtocol12)
+		return "TLSv1.2";
+
+	return "unknown";
+}
+
+/*
+ * be_tls_read
+ *		Read data from a secure connection.
+ */
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
+{
+	size_t			n = 0;
+	ssize_t			ret;
+	OSStatus		read_status;
+	SSLContextRef	ssl = (SSLContextRef) port->ssl;
+
+	errno = 0;
+
+	if (len <= 0)
+		return 0;
+
+	read_status = SSLRead(ssl, ptr, len, &n);
+	switch (read_status)
+	{
+		case noErr:
+			ret = n;
+			break;
+
+		/* Function is blocked, waiting for I/O */
+		case errSSLWouldBlock:
+			if (port->ssl_buffered)
+				*waitfor = WL_SOCKET_WRITEABLE;
+			else
+				*waitfor = WL_SOCKET_READABLE;
+
+			errno = EWOULDBLOCK;
+			if (n == 0)
+				ret = -1;
+			else
+				ret = n;
+
+			break;
+
+		case errSSLClosedGraceful:
+			ret = 0;
+			break;
+
+		/*
+		 * If the connection was closed for an unforeseen reason, return error
+		 * and set errno such that the caller can raise an appropriate ereport
+		 */
+		case errSSLClosedNoNotify:
+		case errSSLClosedAbort:
+			ret = -1;
+			errno = ECONNRESET;
+			break;
+
+		default:
+			ret = -1;
+			ereport(COMMERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("SSL error: %s",
+							pg_SSLerrmessage(read_status))));
+			break;
+	}
+
+	return ret;
+}
+
+/*
+ * be_tls_write
+ *		Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
+{
+	size_t		n = 0;
+	OSStatus	write_status;
+
+	errno = 0;
+
+	if (len == 0)
+		return 0;
+
+	/*
+	 * SSLWrite returns the number of bytes written in the 'n' argument. This
+	 * however can be data either actually written to the socket, or buffered
+	 * in the context. In the latter case SSLWrite will return errSSLWouldBlock
+	 * and we need to call it with no new data (NULL) to drain the buffer on to
+	 * the socket. We track the buffer in ssl_buffered and clear that when all
+	 * data has been drained.
+	 */
+	if (port->ssl_buffered > 0)
+	{
+		write_status = SSLWrite((SSLContextRef) port->ssl, NULL, 0, &n);
+
+		if (write_status == noErr)
+		{
+			n = port->ssl_buffered;
+			port->ssl_buffered = 0;
+		}
+		else if (write_status == errSSLWouldBlock || write_status == -1)
+		{
+			n = -1;
+			errno = EINTR;
+		}
+		else
+		{
+			n = -1;
+			errno = ECONNRESET;
+		}
+	}
+	else
+	{
+		write_status = SSLWrite((SSLContextRef) port->ssl, ptr, len, &n);
+
+		switch (write_status)
+		{
+			case noErr:
+				break;
+
+			/*
+			 * The data was buffered in the context rather than written to the
+			 * socket. Track this and repeatedly call SSLWrite to drain the
+			 * buffer. See comment above.
+			 */
+			case errSSLWouldBlock:
+				port->ssl_buffered = len;
+				n = 0;
+#ifdef EAGAIN
+				errno = EAGAIN;
+#else
+				errno = EINTR;
+#endif
+				break;
+
+			/* Clean disconnections */
+			case errSSLClosedNoNotify:
+				/* fall through */
+			case errSSLClosedGraceful:
+				errno = ECONNRESET;
+				n = -1;
+				break;
+
+			default:
+				errno = ECONNRESET;
+				n = -1;
+				break;
+		}
+	}
+
+	return n;
+}
+
+/*
+ * be_tls_get_cipher_bits
+ *		Returns the number of bits in the encryption for the current cipher
+ *
+ * Note: In case of errors, this returns 0 to match the OpenSSL implementation.
+ * A NULL encryption will however also return 0 making it complicated to
+ * differentiate between the two.
+ */
+int
+be_tls_get_cipher_bits(Port *port)
+{
+	OSStatus		status;
+	SSLCipherSuite	cipher;
+
+	if (!(SSLContextRef) port->ssl)
+		return 0;
+
+	status = SSLGetNegotiatedCipher((SSLContextRef) port->ssl, &cipher);
+	if (status != noErr)
+		return 0;
+
+	return pg_SSLcipherbits(cipher);
+}
+
+void
+be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+{
+	OSStatus			status;
+	SecTrustRef			trust;
+	SecCertificateRef	cert;
+	CFStringRef			dn_str;
+
+	if (!ptr || len == 0)
+		return;
+
+	ptr[0] = '\0';
+
+	if (!(SSLContextRef) port->ssl)
+		return;
+
+	status = SSLCopyPeerTrust((SSLContextRef) port->ssl, &trust);
+	if (status == noErr && trust != NULL)
+	{
+		/*
+		 * TODO: copy the certificate parts with SecCertificateCopyValues and
+		 * parse the OIDs to build up the DN
+		 */
+		cert = SecTrustGetCertificateAtIndex(trust, 0);
+		dn_str = SecCertificateCopyLongDescription(NULL, cert, NULL);
+		if (dn_str)
+		{
+			strlcpy(ptr, CFStringGetCStringPtr(dn_str, kCFStringEncodingASCII), len);
+			CFRelease(dn_str);
+		}
+
+		CFRelease(trust);
+	}
+}
+
+/*
+ * be_tls_get_cipher
+ *		Return the negotiated ciphersuite for the current connection.
+ */
+const char *
+be_tls_get_cipher(Port *port)
+{
+	OSStatus		status;
+	SSLCipherSuite	cipher;
+	const char	   *cipher_name;
+
+	if (!(SSLContextRef) port->ssl)
+		elog(ERROR, "No SSL connection");
+
+	status = SSLGetNegotiatedCipher((SSLContextRef) port->ssl, &cipher);
+	if (status != noErr)
+		ereport(ERROR,
+				(errmsg("could not detect cipher for connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	cipher_name = pg_SSLciphername(cipher);
+	if (cipher_name == NULL)
+		elog(ERROR, "Unknown cipher detected");
+
+	return cipher_name;
+}
+
+/*
+ * be_tls_get_compression
+ *		Retrieve and return whether compression is used for the	current
+ *		connection.
+ *
+ * Since Secure Transport doesn't support compression at all, always return
+ * false here. Ideally we should be able to tell the caller that the option
+ * isn't applicable rather than return false, but the current SSL support
+ * doesn't allow for that.
+ */
+bool
+be_tls_get_compression(Port *port)
+{
+	return false;
+}
+
+/* ------------------------------------------------------------ */
+/*				Internal functions - Translation				*/
+/* ------------------------------------------------------------ */
+
+/*
+ * pg_SSLerrmessage
+ *		Create and return a human readable error message given
+ *		the specified status code
+ *
+ * While only interesting to use for error cases, the function will return a
+ * translation for non-error statuses as well like noErr and errSecSuccess.
+ */
+static char *
+pg_SSLerrmessage(OSStatus status)
+{
+	CFStringRef		err_msg;
+	char		   *err_buf;
+
+	/*
+	 * While errSecUnknownFormat has been defined as -25257 at least since 10.8
+	 * Lion, there still is no translation for it in 10.11 El Capitan, so we
+	 * maintain our own.
+	 */
+	if (status == errSecUnknownFormat)
+		return pstrdup(_("The item you are trying to import has an unknown format."));
+
+	/*
+	 * The Secure Transport supplied error string for invalid passphrase only
+	 * reads "invalid attribute" without any reference to a passphrase, which
+	 * can be confusing. Override with our own.
+	 */
+	if (status == errSecInvalidAttributePassphrase)
+		return pstrdup(_("Incorrect passphrase."));
+
+	if (status == errSSLRecordOverflow)
+		return pstrdup(_("SSL error"));
+
+	/*
+	 * If the error is internal, and we have an error message in the internal
+	 * buffer, then return that error and clear the internal buffer.
+	 */
+	if (status == errSecInternalError && internal_err[0])
+	{
+		err_buf = pstrdup(internal_err);
+		memset(internal_err, '\0', ERR_MSG_LEN);
+	}
+	else
+	{
+		err_msg = SecCopyErrorMessageString(status, NULL);
+
+		if (err_msg)
+		{
+			err_buf = pstrdup(CFStringGetCStringPtr(err_msg,
+													kCFStringEncodingUTF8));
+			CFRelease(err_msg);
+		}
+		else
+			err_buf = pstrdup(_("unknown SSL error"));
+	}
+
+	return err_buf;
+}
+
+/* ------------------------------------------------------------ */
+/*				Internal functions - Socket IO					*/
+/* ------------------------------------------------------------ */
+
+/*
+ * pg_SSLSocketRead
+ *
+ * Callback for reading data from the connection. When entering the function,
+ * len is set to the number of bytes requested. Upon leaving, len should be
+ * overwritten with the actual number of bytes read.
+ */
+static OSStatus
+pg_SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len)
+{
+	OSStatus	status;
+	int			res;
+
+	res = secure_raw_read((Port *) conn, data, *len);
+
+	if (res < 0)
+	{
+		switch (errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+			case ENOENT:
+				status =  errSSLClosedGraceful;
+				break;
+
+			default:
+				status = errSSLClosedAbort;
+				break;
+		}
+
+		*len = 0;
+	}
+	else
+	{
+		status = noErr;
+		*len = res;
+	}
+
+	return status;
+}
+
+static OSStatus
+pg_SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len)
+{
+	OSStatus	status;
+	int			res;
+	Port	   *port = (Port *) conn;
+
+	res = secure_raw_write(port, data, *len);
+
+	if (res < 0)
+	{
+		switch (errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+
+			default:
+				status = errSSLClosedAbort;
+				break;
+		}
+
+		*len = res;
+	}
+	else
+	{
+		status = noErr;
+		*len = res;
+	}
+
+	return status;
+}
+
+/* ------------------------------------------------------------ */
+/*				Internal functions - Misc						*/
+/* ------------------------------------------------------------ */
+
+/*
+ * KeychainEnsureValid
+ *		Ensure the validity of using the passed Keychain array
+ *
+ * Any consumers of the Keychain array should always call this to ensure that
+ * it is set up in a manner that reflect the configuration. If it not, then
+ * this function will fatally error out as all codepaths should assume that
+ * setup has been done correctly and that there is no recovery in case it
+ * hasn't.
+ */
+static void
+KeychainEnsureValid(CFArrayRef *keychains)
+{
+	int		keychain_count;
+
+	/*
+	 * If the keychain array is unallocated, we must be allowed to use the
+	 * default user Keychain, as that will be the effect of passing NULL to the
+	 * Keychain search API. If not, error out.
+	 */
+	if (*keychains == NULL)
+	{
+		if (!ssl_keychain_use_default)
+			goto error;
+
+		return;
+	}
+
+	keychain_count = CFArrayGetCount(*keychains);
+
+	/*
+	 * If we have one Keychain loaded then we must have a Keychain file
+	 * configured, and not be allowed to use the default Keychain. If we have
+	 * two then we must have a Keychain file configured *and* be allowed to use
+	 * the default user Keychain. If we have any other number of Keychains in
+	 * the array then we definitely have an incorrect situation.
+	 */
+	if (keychain_count == 1)
+	{
+		if (ssl_keychain_file[0] == '\0')
+			goto error;
+
+		return;
+	}
+	else if (keychain_count == 2)
+	{
+		if (ssl_keychain_file[0] == '\0' || !ssl_keychain_use_default)
+			goto error;
+
+		return;
+	}
+	else
+		/* Fallthrough to erroring out */
+
+error:
+	ereport(FATAL,
+			(errmsg("Incorrect loading of Keychains detected")));
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index d349d7c..21d5eb4 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -46,6 +46,10 @@ char	   *ssl_crl_file;
 char	   *ssl_dh_params_file;
 char	   *ssl_passphrase_command;
 bool		ssl_passphrase_command_supports_reload;
+#ifdef USE_SECURETRANSPORT
+char       *ssl_keychain_file;
+bool		ssl_keychain_use_default = false;
+#endif
 
 #ifdef USE_SSL
 bool		ssl_loaded_verify_locations = false;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e9f542c..c4a3925 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -511,6 +511,7 @@ static char *locale_ctype;
 static char *server_encoding_string;
 static char *server_version_string;
 static int	server_version_num;
+static char *ssl_library_string;
 static char *timezone_string;
 static char *log_timezone_string;
 static char *timezone_abbreviations_string;
@@ -1830,6 +1831,17 @@ static struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
+#ifdef USE_SECURETRANSPORT
+	{
+		{"ssl_keychain_use_default", PGC_SIGHUP, CONN_AUTH_SSL,
+			gettext_noop("Allow using the default Keychain of the current user."),
+			NULL
+		},
+		&ssl_keychain_use_default,
+		false,
+		NULL, NULL, NULL
+	},
+#endif
 
 	/* End-of-list marker */
 	{
@@ -3517,6 +3529,18 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		/* Can't be set in postgresql.conf */
+		{"ssl_library", PGC_INTERNAL, PRESET_OPTIONS,
+			gettext_noop("Shows the SSL library used."),
+			NULL,
+			GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+		},
+		&ssl_library_string,
+		SSL_LIBRARY,
+		NULL, NULL, NULL
+	},
+
+	{
 		/* Not for general use --- used by SET ROLE */
 		{"role", PGC_USERSET, UNGROUPED,
 			gettext_noop("Sets the current role."),
@@ -3778,6 +3802,18 @@ static struct config_string ConfigureNamesString[] =
 		NULL, NULL, NULL
 	},
 
+#ifdef USE_SECURETRANSPORT
+	{
+		{"ssl_keychain_file", PGC_SIGHUP, CONN_AUTH_SSL,
+			gettext_noop("Location of the Keychain file."),
+			NULL
+		},
+		&ssl_keychain_file,
+		"",
+		NULL, NULL, NULL
+	},
+#endif
+
 	{
 		{"stats_temp_directory", PGC_SIGHUP, STATS_COLLECTOR,
 			gettext_noop("Writes temporary statistics files to the specified directory."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 4e61bc6..eb19aa0 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -106,6 +106,7 @@
 #ssl_dh_params_file = ''
 #ssl_passphrase_command = ''
 #ssl_passphrase_command_supports_reload = off
+#ssl_keychain_file = ''
 
 
 #------------------------------------------------------------------------------
diff --git a/src/include/common/securetransport.h b/src/include/common/securetransport.h
new file mode 100644
index 0000000..d3a38e2
--- /dev/null
+++ b/src/include/common/securetransport.h
@@ -0,0 +1,514 @@
+/*-------------------------------------------------------------------------
+ *
+ * securetransport_common.h
+ *	  Apple Secure Transport support
+ *
+ * These definitions are used by both frontend and backend code.
+ *
+ * Copyright (c) 2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *        src/include/common/securetransport.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SECURETRANSPORT_H
+#define SECURETRANSPORT_H
+
+#include <Security/SecureTransport.h>
+
+/*
+ * pg_SSLciphername
+ *
+ * Translate a SSLCipherSuite code into a string literal suitable for printing
+ * in log/informational messages to the user. Since this implementation of the
+ * Secure Transport lib doesn't support SSLv2/v3 these ciphernames are omitted.
+ *
+ * The SSLCipherSuite enum is defined in Security/CipherSuite.h
+ *
+ * This only removes the TLS_ portion of the SSLCipherSuite enum label for the
+ * ciphers to match what most Secure Transport implementations seem to be doing
+ */
+static const char *
+pg_SSLciphername(SSLCipherSuite cipher)
+{
+	switch (cipher)
+	{
+		case TLS_NULL_WITH_NULL_NULL:
+			return "NULL";
+
+		/* TLS addenda using AES, per RFC 3268 */
+		case TLS_RSA_WITH_AES_128_CBC_SHA:
+			return "RSA_WITH_AES_128_CBC_SHA";
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+			return "DH_DSS_WITH_AES_128_CBC_SHA";
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+			return "DH_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+			return "DHE_DSS_WITH_AES_128_CBC_SHA";
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+			return "DHE_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+			return "DH_anon_WITH_AES_128_CBC_SHA";
+		case TLS_RSA_WITH_AES_256_CBC_SHA:
+			return "RSA_WITH_AES_256_CBC_SHA";
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+			return "DH_DSS_WITH_AES_256_CBC_SHA";
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+			return "DH_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+			return "DHE_DSS_WITH_AES_256_CBC_SHA";
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+			return "DHE_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+			return "DH_anon_WITH_AES_256_CBC_SHA";
+
+		/* ECDSA addenda, RFC 4492 */
+		case TLS_ECDH_ECDSA_WITH_NULL_SHA:
+			return "ECDH_ECDSA_WITH_NULL_SHA";
+		case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+			return "ECDH_ECDSA_WITH_RC4_128_SHA";
+		case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+			return "ECDH_ECDSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+			return "ECDH_ECDSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+			return "ECDHE_ECDSA_WITH_NULL_SHA";
+		case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+			return "ECDHE_ECDSA_WITH_RC4_128_SHA";
+		case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDH_RSA_WITH_NULL_SHA:
+			return "ECDH_RSA_WITH_NULL_SHA";
+		case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+			return "ECDH_RSA_WITH_RC4_128_SHA";
+		case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+			return "ECDH_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+			return "ECDH_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDHE_RSA_WITH_NULL_SHA:
+			return "ECDHE_RSA_WITH_NULL_SHA";
+		case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+			return "ECDHE_RSA_WITH_RC4_128_SHA";
+		case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+			return "ECDHE_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+			return "ECDHE_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDH_anon_WITH_NULL_SHA:
+			return "ECDH_anon_WITH_NULL_SHA";
+		case TLS_ECDH_anon_WITH_RC4_128_SHA:
+			return "ECDH_anon_WITH_RC4_128_SHA";
+		case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_anon_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+			return "ECDH_anon_WITH_AES_128_CBC_SHA";
+		case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+			return "ECDH_anon_WITH_AES_256_CBC_SHA";
+
+		/* Server provided RSA certificate for key exchange. */
+		case TLS_RSA_WITH_NULL_MD5:
+			return "RSA_WITH_NULL_MD5";
+		case TLS_RSA_WITH_NULL_SHA:
+			return "RSA_WITH_NULL_SHA";
+		case TLS_RSA_WITH_RC4_128_MD5:
+			return "RSA_WITH_RC4_128_MD5";
+		case TLS_RSA_WITH_RC4_128_SHA:
+			return "RSA_WITH_RC4_128_SHA";
+		case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_RSA_WITH_NULL_SHA256:
+			return "RSA_WITH_NULL_SHA256";
+		case TLS_RSA_WITH_AES_128_CBC_SHA256:
+			return "RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_RSA_WITH_AES_256_CBC_SHA256:
+			return "RSA_WITH_AES_256_CBC_SHA256";
+
+		/*
+		 * Server-authenticated (and optionally client-authenticated)
+		 * Diffie-Hellman.
+		 */
+		case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+			return "DH_DSS_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "DH_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+			return "DH_DSS_WITH_AES_128_CBC_SHA256";
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+			return "DH_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+			return "DHE_DSS_WITH_AES_128_CBC_SHA256";
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+			return "DHE_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+			return "DH_DSS_WITH_AES_256_CBC_SHA256";
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+			return "DH_RSA_WITH_AES_256_CBC_SHA256";
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+			return "DHE_DSS_WITH_AES_256_CBC_SHA256";
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+			return "DHE_RSA_WITH_AES_256_CBC_SHA256";
+
+		/* Completely anonymous Diffie-Hellman */
+		case TLS_DH_anon_WITH_RC4_128_MD5:
+			return "DH_anon_WITH_RC4_128_MD5";
+		case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+			return "DH_anon_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+			return "DH_anon_WITH_AES_128_CBC_SHA256";
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+			return "DH_anon_WITH_AES_256_CBC_SHA256";
+
+		/* Addendum from RFC 4279, TLS PSK */
+		case TLS_PSK_WITH_RC4_128_SHA:
+			return "PSK_WITH_RC4_128_SHA";
+		case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "PSK_WITH_3DES_EDE_CBC_SHA";
+		case TLS_PSK_WITH_AES_128_CBC_SHA:
+			return "PSK_WITH_AES_128_CBC_SHA";
+		case TLS_PSK_WITH_AES_256_CBC_SHA:
+			return "PSK_WITH_AES_256_CBC_SHA";
+		case TLS_DHE_PSK_WITH_RC4_128_SHA:
+			return "DHE_PSK_WITH_RC4_128_SHA";
+		case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_PSK_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+			return "DHE_PSK_WITH_AES_128_CBC_SHA";
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+			return "DHE_PSK_WITH_AES_256_CBC_SHA";
+		case TLS_RSA_PSK_WITH_RC4_128_SHA:
+			return "RSA_PSK_WITH_RC4_128_SHA";
+		case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "RSA_PSK_WITH_3DES_EDE_CBC_SHA";
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+			return "RSA_PSK_WITH_AES_128_CBC_SHA";
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+			return "RSA_PSK_WITH_AES_256_CBC_SHA";
+
+		/* RFC 4785, Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */
+		case TLS_PSK_WITH_NULL_SHA:
+			return "PSK_WITH_NULL_SHA";
+		case TLS_DHE_PSK_WITH_NULL_SHA:
+			return "DHE_PSK_WITH_NULL_SHA";
+		case TLS_RSA_PSK_WITH_NULL_SHA:
+			return "RSA_PSK_WITH_NULL_SHA";
+
+		/*
+		 * Addenda from RFC 5288, AES Galois Counter Mode (GCM) Cipher Suites
+		 * for TLS.
+		 */
+		case TLS_RSA_WITH_AES_128_GCM_SHA256:
+			return "RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_RSA_WITH_AES_256_GCM_SHA384:
+			return "RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+			return "DHE_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+			return "DHE_RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+			return "DH_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+			return "DH_RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+			return "DHE_DSS_WITH_AES_128_GCM_SHA256";
+		case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+			return "DHE_DSS_WITH_AES_256_GCM_SHA384";
+		case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+			return "DH_DSS_WITH_AES_128_GCM_SHA256";
+		case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+			return "DH_DSS_WITH_AES_256_GCM_SHA384";
+		case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+			return "DH_anon_WITH_AES_128_GCM_SHA256";
+		case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+			return "DH_anon_WITH_AES_256_GCM_SHA384";
+
+		/* RFC 5487 - PSK with SHA-256/384 and AES GCM */
+		case TLS_PSK_WITH_AES_128_GCM_SHA256:
+			return "PSK_WITH_AES_128_GCM_SHA256";
+		case TLS_PSK_WITH_AES_256_GCM_SHA384:
+			return "PSK_WITH_AES_256_GCM_SHA384";
+		case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+			return "DHE_PSK_WITH_AES_128_GCM_SHA256";
+		case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+			return "DHE_PSK_WITH_AES_256_GCM_SHA384";
+		case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+			return "RSA_PSK_WITH_AES_128_GCM_SHA256";
+		case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+			return "RSA_PSK_WITH_AES_256_GCM_SHA384";
+		case TLS_PSK_WITH_AES_128_CBC_SHA256:
+			return "PSK_WITH_AES_128_CBC_SHA256";
+		case TLS_PSK_WITH_AES_256_CBC_SHA384:
+			return "PSK_WITH_AES_256_CBC_SHA384";
+		case TLS_PSK_WITH_NULL_SHA256:
+			return "PSK_WITH_NULL_SHA256";
+		case TLS_PSK_WITH_NULL_SHA384:
+			return "PSK_WITH_NULL_SHA384";
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+			return "DHE_PSK_WITH_AES_128_CBC_SHA256";
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+			return "DHE_PSK_WITH_AES_256_CBC_SHA384";
+		case TLS_DHE_PSK_WITH_NULL_SHA256:
+			return "DHE_PSK_WITH_NULL_SHA256";
+		case TLS_DHE_PSK_WITH_NULL_SHA384:
+			return "DHE_PSK_WITH_NULL_SHA384";
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+			return "RSA_PSK_WITH_AES_128_CBC_SHA256";
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+			return "RSA_PSK_WITH_AES_256_CBC_SHA384";
+		case TLS_RSA_PSK_WITH_NULL_SHA256:
+			return "RSA_PSK_WITH_NULL_SHA256";
+		case TLS_RSA_PSK_WITH_NULL_SHA384:
+			return "RSA_PSK_WITH_NULL_SHA384";
+
+		/*
+		 * Addenda from RFC 5289, Elliptic Curve Cipher Suites with
+		 * HMAC SHA-256/384.
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+			return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+			return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384";
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+			return "ECDH_ECDSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+			return "ECDH_ECDSA_WITH_AES_256_CBC_SHA384";
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+			return "ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+			return "ECDHE_RSA_WITH_AES_256_CBC_SHA384";
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+			return "ECDH_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+			return "ECDH_RSA_WITH_AES_256_CBC_SHA384";
+
+		/*
+		 * Addenda from RFC 5289, Elliptic Curve Cipher Suites with
+		 * SHA-256/384 and AES Galois Counter Mode (GCM)
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+			return "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+			return "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
+		case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+			return "ECDH_ECDSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+			return "ECDH_ECDSA_WITH_AES_256_GCM_SHA384";
+		case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+			return "ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+			return "ECDHE_RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+			return "ECDH_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+			return "ECDH_RSA_WITH_AES_256_GCM_SHA384";
+
+		default:
+			break;
+	}
+
+	return NULL;
+}
+
+#ifndef FRONTEND
+/*
+ * pg_SSLcipherbits
+ *
+ * Return the number of bits in the encryption for the specified cipher.
+ * Ciphers with NULL encryption are omitted from the switch statement. This
+ * function is currently only used in the libpq backend.
+ */
+static int
+pg_SSLcipherbits(SSLCipherSuite cipher)
+{
+	switch (cipher)
+	{
+		/* TLS addenda using AES, per RFC 3268 */
+		case TLS_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+			return 128;
+
+		case TLS_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+			return 256;
+
+		/* ECDSA addenda, RFC 4492 */
+		case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+
+		case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+		case TLS_ECDH_anon_WITH_RC4_128_SHA:
+		case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+		case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+		case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+			return 128;
+
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+			return 256;
+
+		/* Server provided RSA certificate for key exchange. */
+		case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_RSA_WITH_RC4_128_MD5:
+		case TLS_RSA_WITH_RC4_128_SHA:
+		case TLS_RSA_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_RSA_WITH_AES_256_CBC_SHA256:
+			return 256;
+
+		/*
+		 * Server-authenticated (and optionally client-authenticated)
+		 * Diffie-Hellman.
+		 */
+		case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+			return 256;
+
+		/* Completely anonymous Diffie-Hellman */
+		case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_DH_anon_WITH_RC4_128_MD5:
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+			return 256;
+
+		/* Addendum from RFC 4279, TLS PSK */
+		case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+		case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_PSK_WITH_RC4_128_SHA:
+		case TLS_DHE_PSK_WITH_RC4_128_SHA:
+		case TLS_PSK_WITH_AES_128_CBC_SHA:
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+		case TLS_RSA_PSK_WITH_RC4_128_SHA:
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+			return 128;
+		case TLS_PSK_WITH_AES_256_CBC_SHA:
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+			return 256;
+
+		/*
+		 * Addenda from RFC 5288, AES Galois Counter Mode (GCM) Cipher Suites
+		 * for TLS.
+		 */
+		case TLS_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+		case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+		case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+			return 128;
+
+		case TLS_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+		case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+		case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+			return 256;
+
+		/* RFC 5487 - PSK with SHA-256/384 and AES GCM */
+
+
+		case TLS_PSK_WITH_AES_128_GCM_SHA256:
+		case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+		case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+		case TLS_PSK_WITH_AES_128_CBC_SHA256:
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+		case TLS_PSK_WITH_AES_256_GCM_SHA384:
+		case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+		case TLS_PSK_WITH_AES_256_CBC_SHA384:
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+			return 256;
+
+		/*
+		 * Addenda from RFC 5289, Elliptic Curve Cipher Suites with
+		 * HMAC SHA-256/384.
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+			return 256;
+
+		/*
+		 * Addenda from RFC 5289, Elliptic Curve Cipher Suites with
+		 * SHA-256/384 and AES Galois Counter Mode (GCM)
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+		case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+		case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+			return 128;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+		case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+		case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+			return 256;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+#endif
+
+#endif							/* SECURETRANSPORT_H */
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index f3fd0d0..6d35f47 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -50,7 +50,7 @@
 #ifndef _PG_SHA2_H_
 #define _PG_SHA2_H_
 
-#ifdef USE_SSL
+#if defined(USE_SSL) && defined(USE_OPENSSL)
 #include <openssl/sha.h>
 #endif
 
@@ -69,7 +69,7 @@
 #define PG_SHA512_DIGEST_STRING_LENGTH	(PG_SHA512_DIGEST_LENGTH * 2 + 1)
 
 /* Context Structures for SHA-1/224/256/384/512 */
-#ifdef USE_SSL
+#if defined(USE_SSL) && defined(USE_OPENSSL)
 typedef SHA256_CTX pg_sha256_ctx;
 typedef SHA512_CTX pg_sha512_ctx;
 typedef SHA256_CTX pg_sha224_ctx;
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ef5528c..69aa93d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -22,6 +22,14 @@
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#elif USE_SECURETRANSPORT
+/*
+ * Ideally we should include the Secure Transport headers here but doing so
+ * cause namespace collisions with CoreFoundation on, among others "Size"
+ * and ACL definitions. To avoid polluting with workarounds, use void * for
+ * instead of the actual Secure Transport variables and perform type casting
+ * in the Secure Transport implementation.
+ */
 #endif
 #ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
@@ -183,12 +191,15 @@ typedef struct Port
 	bool		peer_cert_valid;
 
 	/*
-	 * OpenSSL structures. (Keep these last so that the locations of other
-	 * fields are the same whether or not you build with OpenSSL.)
+	 * SSL library structures. (Keep these last so that the locations of
+	 * other fields are the same whether or not you build with SSL.)
 	 */
 #ifdef USE_OPENSSL
 	SSL		   *ssl;
 	X509	   *peer;
+#elif USE_SECURETRANSPORT
+	void	   *ssl;
+	int			ssl_buffered;
 #endif
 } Port;
 
@@ -214,7 +225,7 @@ CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
 
 /*
  * These functions are implemented by the glue code specific to each
- * SSL implementation (e.g. be-secure-openssl.c)
+ * SSL implementation (e.g. be-secure-<implementation>.c)
  */
 
 /*
@@ -269,6 +280,7 @@ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
  *
  * This is not supported with old versions of OpenSSL that don't have
  * the X509_get_signature_nid() function.
+ * It's also not supported with Apple Secure Transport.
  */
 #if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
 #define HAVE_BE_TLS_GET_CERTIFICATE_HASH
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 36baf6b..284e1cc 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -83,6 +83,10 @@ extern char *ssl_crl_file;
 extern char *ssl_dh_params_file;
 extern char *ssl_passphrase_command;
 extern bool ssl_passphrase_command_supports_reload;
+#ifdef USE_SECURETRANSPORT
+extern char *ssl_keychain_file;
+extern bool	ssl_keychain_use_default;
+#endif
 
 extern int	secure_initialize(bool isServerStart);
 extern bool secure_loaded_verify_locations(void);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5d40796..4fad10d 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -932,6 +932,10 @@
 /* Use replacement snprintf() functions. */
 #undef USE_REPL_SNPRINTF
 
+/* Define to build with Apple Secure Transport support.
+   (--with-securetransport) */
+#undef USE_SECURETRANSPORT
+
 /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
 #undef USE_SLICING_BY_8_CRC32C
 
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index b036525..2731f82 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -162,11 +162,17 @@
 
 /*
  * USE_SSL code should be compiled only when compiling with an SSL
- * implementation.  (Currently, only OpenSSL is supported, but we might add
- * more implementations in the future.)
+ * implementation.
  */
-#ifdef USE_OPENSSL
+#if defined(USE_OPENSSL) || defined(USE_SECURETRANSPORT)
 #define USE_SSL
+#if defined(USE_OPENSSL)
+#define SSL_LIBRARY "OpenSSL"
+#elif defined(USE_SECURETRANSPORT)
+#define SSL_LIBRARY "Secure Transport"
+#endif
+#else
+#define SSL_LIBRARY "None"
 #endif
 
 /*
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index ec0122a..a021364 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -57,6 +57,11 @@ else
 OBJS += sha2.o
 endif
 
+ifeq ($(with_securetransport), yes)
+OBJS += fe-secure-securetransport.o
+override CFLAGS += -framework Security -framework CoreFoundation -fconstant-cfstrings
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 45bc067..a8ac764 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -325,6 +325,16 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
 	offsetof(struct pg_conn, target_session_attrs)},
 
+#if defined(USE_SECURETRANSPORT)
+	{"keychain_use_default", NULL, NULL, NULL,
+		"UseDefaultKeychain", "", 1,
+	offsetof(struct pg_conn, keychain_use_default)},
+
+	{"keychain", "PGKEYCHAIN", NULL, NULL,
+		"Keychain", "", 64,
+	offsetof(struct pg_conn, keychain)},
+#endif
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
@@ -3536,6 +3546,9 @@ makeEmptyPGconn(void)
 	conn->verbosity = PQERRORS_DEFAULT;
 	conn->show_context = PQSHOW_CONTEXT_ERRORS;
 	conn->sock = PGINVALID_SOCKET;
+#ifdef USE_SECURETRANSPORT
+	conn->keychain_use_default = true;
+#endif
 
 	/*
 	 * We try to send at least 8K at a time, which is the usual size of pipe
diff --git a/src/interfaces/libpq/fe-secure-securetransport.c b/src/interfaces/libpq/fe-secure-securetransport.c
new file mode 100644
index 0000000..f1a0455
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-securetransport.c
@@ -0,0 +1,1477 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-securetransport.c
+ *	  Apple Secure Transport support
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-securetransport.c
+ *
+ * NOTES
+ *	  Unlike the OpenSSL support there is no shared state between connections
+ *	  so there is no special handling for ENABLE_THREAD_SAFETY.
+ *
+ *	  There are a lot of functions (mostly the Core Foundation CF* family) that
+ *	  pass NULL as the first parameter. This is because they allow for a custom
+ *	  allocator to be used for memory allocations which is referenced with the
+ *	  first parameter. We are using the standard allocator however, and that
+ *	  means passing NULL all the time. Defining a suitably named preprocessor
+ *	  macro would aid readiblitity in case this is confusing (and/or ugly).
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
+
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include "common/securetransport.h"
+
+
+#define KC_PREFIX		"keychain:"
+#define KC_PREFIX_LEN	(strlen("keychain:"))
+
+/*
+ * Private API call used in the Webkit code for creating an identity from a
+ * certificate with a key. While stable and used in many open source projects
+ * it should be replaced with a published API call since private APIs aren't
+ * subject to the same deprecation rules. Could potentially be replaced by
+ * using SecIdentityCreateWithCertificate() ?
+ */
+extern SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
+										SecCertificateRef certificate,
+										SecKeyRef privateKey);
+
+static char * pg_SSLerrmessage(OSStatus errcode);
+static void pg_SSLerrfree(char *err_buf);
+static int pg_SSLsessionstate(PGconn *conn, char *msg, size_t len);
+
+static OSStatus pg_SSLSocketRead(SSLConnectionRef conn, void *data,
+							  size_t *len);
+static OSStatus pg_SSLSocketWrite(SSLConnectionRef conn, const void *data,
+							   size_t *len);
+static OSStatus pg_SSLOpenClient(PGconn *conn);
+static OSStatus pg_SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array,
+								   CFArrayRef *key_array,
+								   CFArrayRef *rootcert_array);
+
+static OSStatus import_certificate_keychain(const char *common_name,
+											SecCertificateRef *certificate,
+											CFArrayRef keychains,
+											char *hostname);
+static OSStatus import_identity_keychain(const char *common_name,
+										 SecIdentityRef *identity,
+										 CFArrayRef keychains);
+static OSStatus import_pem(const char *path, char *passphrase,
+						   CFArrayRef *cert_arr);
+
+/* ------------------------------------------------------------ */
+/*						 Public interface						*/
+/* ------------------------------------------------------------ */
+
+/*
+ * pgtls_init_library
+ *
+ * Exported function to allow application to tell us it's already initialized
+ * Secure Transport and/or libcrypto. Since the current implementation only
+ * allow do_crypto be set for the OpenSSL backend, we should always get the
+ * same value passed in both variables.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+	Assert(do_ssl ? do_crypto : !do_crypto);
+	return;
+}
+
+/*
+ * pgtls_open_client
+ *		Begin, or continue, negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	OSStatus		open_status;
+	CFArrayRef		certificate = NULL;
+	CFArrayRef		key = NULL;
+	CFArrayRef		rootcert = NULL;
+
+	/*
+	 * There is no API to load CRL lists in Secure Transport, they can however
+	 * be imported into a Keychain with the commandline application "certtool".
+	 * For libpq to use them, the certificate/key and root certificate needs to
+	 * be using an identity in a Keychain into which the CRL have been
+	 * imported. That needs to be documented.
+	 */
+	if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("CRL files are not supported with Secure Transport\n"));
+		return PGRES_POLLING_FAILED;
+	}
+
+	/*
+	 * If the SSL context hasn't been set up then initiate it, else continue
+	 * with handshake
+	 */
+	if (conn->ssl == NULL)
+	{
+		conn->ssl_key_bits = 0;
+		conn->ssl_buffered = 0;
+		conn->st_rootcert = NULL;
+
+		conn->ssl = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+		if (!conn->ssl)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+				   libpq_gettext("could not create SSL context\n"));
+			return PGRES_POLLING_FAILED;
+		}
+
+		open_status = SSLSetProtocolVersionMin(conn->ssl, kTLSProtocol12);
+		if (open_status != noErr)
+			goto error;
+
+		open_status = SSLSetConnection(conn->ssl, conn);
+		if (open_status != noErr)
+			goto error;
+
+		/*
+		 * Set the low level functions for reading and writing off a socket
+		 */
+		open_status = SSLSetIOFuncs(conn->ssl, pg_SSLSocketRead, pg_SSLSocketWrite);
+		if (open_status != noErr)
+			goto error;
+
+		/*
+		 * Load client certificate, private key, and trusted CA certs. The
+		 * conn->errorMessage will be populated by the certificate loading
+		 * so we can return without altering it in case of error.
+		 */
+		if (pg_SSLLoadCertificate(conn, &certificate, &key, &rootcert) != noErr)
+		{
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+
+		/*
+		 * If we are asked to verify the peer hostname, set it as a requirement
+		 * on the connection. This must be set before calling SSLHandshake().
+		 */
+		if (strcmp(conn->sslmode, "verify-full") == 0)
+		{
+			/* If we are asked to verify a hostname we dont have, error out */
+			if (!conn->pghost)
+			{
+				pgtls_close(conn);
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("hostname missing for verify-full\n"));
+				return PGRES_POLLING_FAILED;
+			}
+
+			SSLSetPeerDomainName(conn->ssl, conn->pghost, strlen(conn->pghost));
+		}
+	}
+
+	/*
+	 * Perform handshake
+	 */
+	open_status = pg_SSLOpenClient(conn);
+	if (open_status == noErr)
+	{
+		conn->ssl_in_use = true;
+		return PGRES_POLLING_OK;
+	}
+
+error:
+	if (open_status != noErr)
+	{
+		char *err_msg = pg_SSLerrmessage(open_status);
+		if (conn->errorMessage.len > 0)
+			appendPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext(", ssl error: %s\n"), err_msg);
+		else
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not establish SSL connection: %s\n"),
+									err_msg);
+		pg_SSLerrfree(err_msg);
+
+		pgtls_close(conn);
+	}
+
+	return PGRES_POLLING_FAILED;
+}
+
+/*
+ * pg_SSLOpenClient
+ *		Validates remote certificate and performs handshake.
+ *
+ * If the user has supplied a root certificate we add that to the chain here
+ * before initiating validation. The caller is responsible for invoking error
+ * logging in the case of errors returned.
+ */
+static OSStatus
+pg_SSLOpenClient(PGconn *conn)
+{
+	OSStatus			status;
+	SecTrustRef			trust = NULL;
+	SecTrustResultType	trust_eval = 0;
+	bool				trusted = false;
+	bool				only_anchor = true;
+
+	SSLSetSessionOption(conn->ssl, kSSLSessionOptionBreakOnServerAuth, true);
+
+	/*
+	 * Call SSLHandshake until we get another response than errSSLWouldBlock.
+	 * Busy-waiting is pretty silly, but what is commonly used for handshakes
+	 * in Secure Transport. Setting an upper bound on retries should be done
+	 * though, and perhaps a small timeout to play nice.
+	 */
+	do
+	{
+		status = SSLHandshake(conn->ssl);
+		/* busy-wait loop */
+	}
+	while (status == errSSLWouldBlock || status == -1);
+
+	if (status != errSSLServerAuthCompleted)
+		return status;
+
+	/*
+	 * Get peer server certificate and validate it. SSLCopyPeerTrust() is not
+	 * supposed to return a NULL trust on noErr but have been reported to do
+	 * in the past so add a belts-and-suspenders check
+	 */
+	status = SSLCopyPeerTrust(conn->ssl, &trust);
+	if (status != noErr || trust == NULL)
+		return (trust == noErr ? errSecInternalError : status);
+
+	/*
+	 * If we have our own root certificate configured then add it to the chain
+	 * of trust and specify that it should be trusted.
+	 */
+	if (conn->st_rootcert)
+	{
+		status = SecTrustSetAnchorCertificates(trust,
+											   (CFArrayRef) conn->st_rootcert);
+		if (status != noErr)
+			return status;
+
+		/* We have a trusted local root cert, trust more than anchor */
+		only_anchor = false;
+	}
+
+	status = SecTrustSetAnchorCertificatesOnly(trust, only_anchor);
+	if (status != noErr)
+		return status;
+
+	status = SecTrustEvaluate(trust, &trust_eval);
+	if (status == errSecSuccess)
+	{
+		switch (trust_eval)
+		{
+			/*
+			 * If 'Unspecified' then a valid anchor certificate was verified
+			 * without encountering any explicit user trust. If 'Proceed' then
+			 * the user has chosen to explicitly trust a certificate in the
+			 * chain by clicking "Trust" in the Keychain app. Both cases are
+			 * considered valid so trust the chain.
+			 */
+			case kSecTrustResultUnspecified:
+				trusted = true;
+				break;
+			case kSecTrustResultProceed:
+				trusted = true;
+				break;
+
+			/*
+			 * 'RecoverableTrustFailure' indicates that the certificate was
+			 * rejected but might be trusted with minor changes to the eval
+			 * context (ignoring expired certificate etc). For the verify
+			 * sslmodes there is little to do here, but in require sslmode we
+			 * can recover in some cases.
+			 */
+			case kSecTrustResultRecoverableTrustFailure:
+			{
+				CFArrayRef 			trust_prop;
+				CFDictionaryRef		trust_dict;
+				CFStringRef			trust_error;
+				const char		   *error;
+
+				/* Assume the error is in fact not recoverable */
+				trusted = false;
+
+				/*
+				 * In sslmode "require" we accept some certificate verification
+				 * failures when we don't have a rootcert since MITM protection
+				 * isn't enforced. Check the reported failure and trust in case
+				 * the cert is missing, self signed or expired/future.
+				 */
+				if (strcmp(conn->sslmode, "require") == 0 && !conn->st_rootcert)
+				{
+					trust_prop = SecTrustCopyProperties(trust);
+					trust_dict = CFArrayGetValueAtIndex(trust_prop, 0);
+					trust_error = CFDictionaryGetValue(trust_dict,
+													   kSecPropertyTypeError);
+					if (trust_error)
+					{
+						error = CFStringGetCStringPtr(trust_error,
+													  kCFStringEncodingUTF8);
+
+						/* Self signed, or missing CA */
+						if (strcmp(error, "CSSMERR_TP_INVALID_ANCHOR_CERT") == 0 ||
+							strcmp(error, "CSSMERR_TP_NOT_TRUSTED") == 0 ||
+							strcmp(error, "CSSMERR_TP_INVALID_CERT_AUTHORITY") == 0)
+							trusted = true;
+						/* Expired or future dated */
+						else if (strcmp(error, "CSSMERR_TP_CERT_EXPIRED") == 0 ||
+								 strcmp(error, "CSSMERR_TP_CERT_NOT_VALID_YET") == 0)
+							trusted = true;
+					}
+
+					CFRelease(trust_prop);
+				}
+
+				break;
+			}
+
+			/*
+			 * The below results are all cases where the certificate should be
+			 * rejected without further questioning.
+			 */
+
+			/*
+			 * 'Deny' means that the user has explicitly set the certificate to
+			 * untrusted.
+			 */
+			case kSecTrustResultDeny:
+				/* fall-through */
+			case kSecTrustResultInvalid:
+				/* fall-through */
+			case kSecTrustResultFatalTrustFailure:
+				/* fall-through */
+			case kSecTrustResultOtherError:
+				/* fall-through */
+			default:
+				trusted = false;
+				break;
+		}
+	}
+
+	CFRelease(trust);
+
+	if (!trusted)
+		return errSecNoAccessForItem;
+
+	/*
+	 * If we reach here the documentation states we need to run the Handshake
+	 * again after validating the trust
+	 */
+	return pg_SSLOpenClient(conn);
+}
+
+/*
+ * pgtls_read_pending
+ *		Is there unread data waiting in the SSL read buffer?
+ */
+bool
+pgtls_read_pending(PGconn *conn)
+{
+	OSStatus read_status;
+	size_t len = 0;
+
+	read_status = SSLGetBufferedReadSize(conn->ssl, &len);
+
+	/*
+	 * Should we get an error back then we assume that subsequent read
+	 * operations will fail as well.
+	 */
+	return (read_status == noErr && len > 0);
+}
+
+/*
+ * pgtls_read
+ *		Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message into
+ * conn->errorMessage.  The caller must still inspect errno, but only to decide
+ * whether to continue or retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+	OSStatus	read_status;
+	size_t		n = 0;
+	ssize_t		ret = 0;
+	int			read_errno = 0;
+	char		sess_msg[25];
+
+	/*
+	 * Double-check that we have a connection which is in the correct state for
+	 * reading before attempting to pull any data off the wire.
+	 */
+	if (pg_SSLsessionstate(conn, sess_msg, sizeof(sess_msg)) == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("SSL connection is: %s\n"), sess_msg);
+		read_errno = ECONNRESET;
+		return -1;
+	}
+
+	read_status = SSLRead(conn->ssl, ptr, len, &n);
+	ret = (ssize_t) n;
+
+	switch (read_status)
+	{
+		case noErr:
+			break;
+
+		case errSSLWouldBlock:
+			/* Only set read_errno to EINTR iff we didn't get any data back */
+			if (n == 0)
+				read_errno = EINTR;
+			break;
+
+		/*
+		 * Clean disconnections
+		 */
+		case errSSLClosedNoNotify:
+			/* fall through */
+		case errSSLClosedGraceful:
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			read_errno = ECONNRESET;
+			ret = -1;
+			break;
+
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("unrecognized SSL error %d\n"), read_status);
+			read_errno = ECONNRESET;
+			ret = -1;
+			break;
+	}
+
+	SOCK_ERRNO_SET(read_errno);
+	return ret;
+}
+
+/*
+ * pgtls_write
+ *		Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message into
+ * conn->errorMessage.  The caller must still inspect errno, but only to decide
+ * whether to continue or retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	OSStatus	write_status;
+	size_t		n = 0;
+	ssize_t		ret = 0;
+	int			write_errno = 0;
+	char		sess_msg[25];
+
+	/*
+	 * Double-check that we have a connection which is in the correct state
+	 * for writing before attempting to push any data on to the wire or the
+	 * local SSL buffer.
+	 */
+	if (pg_SSLsessionstate(conn, sess_msg, sizeof(sess_msg)) == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("SSL connection is: %s\n"), sess_msg);
+		write_errno = ECONNRESET;
+		return -1;
+	}
+
+	if (conn->ssl_buffered > 0)
+	{
+		write_status = SSLWrite(conn->ssl, NULL, 0, &n);
+
+		if (write_status == noErr)
+		{
+			ret = conn->ssl_buffered;
+			conn->ssl_buffered = 0;
+		}
+		else if (write_status == errSSLWouldBlock || write_status == -1)
+		{
+			ret = 0;
+			write_errno = EINTR;
+		}
+		else
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("unrecognized SSL error: %d\n"), write_status);
+			ret = -1;
+			write_errno = ECONNRESET;
+		}
+	}
+	else
+	{
+		write_status = SSLWrite(conn->ssl, ptr, len, &n);
+		ret = n;
+
+		switch (write_status)
+		{
+			case noErr:
+				break;
+
+			case errSSLWouldBlock:
+				conn->ssl_buffered = len;
+				ret = 0;
+#ifdef EAGAIN
+				write_errno = EAGAIN;
+#else
+				write_errno = EINTR;
+#endif
+				break;
+
+			/*
+			 * Clean disconnections
+			 */
+			case errSSLClosedNoNotify:
+				/* fall through */
+			case errSSLClosedGraceful:
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("SSL connection has been closed unexpectedly\n"));
+				write_errno = ECONNRESET;
+				ret = -1;
+				break;
+
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("unrecognized SSL error %d\n"), write_status);
+				write_errno = ECONNRESET;
+				ret = -1;
+				break;
+		}
+	}
+
+	SOCK_ERRNO_SET(write_errno);
+	return ret;
+}
+
+/*
+ * pgtls_init
+ *		Initialize SSL system.
+ *
+ * There is little state or context to initialize for Secure Transport, the
+ * heavy lifting is performed by pgtls_open_client.
+ */
+int
+pgtls_init(PGconn *conn)
+{
+	conn->ssl_buffered = 0;
+	conn->ssl_in_use = false;
+
+	return 0;
+}
+
+/*
+ * pgtls_close
+ *		Close SSL connection.
+ *
+ * This function must cope with connections in all states of disrepair since
+ * it will be called from pgtls_open_client to clean up any potentially used
+ * resources in case it breaks halfway.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	if (!conn->ssl)
+		return;
+
+	if (conn->st_rootcert != NULL)
+		CFRelease((CFArrayRef) conn->st_rootcert);
+
+	SSLClose(conn->ssl);
+	CFRelease(conn->ssl);
+
+	conn->ssl = NULL;
+	conn->ssl_in_use = false;
+}
+
+/*
+ * pg_SSLSocketRead
+ *		The amount of read bytes is returned in the len variable
+ */
+static OSStatus
+pg_SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len)
+{
+	OSStatus	status = noErr;
+	int			res;
+
+	res = pqsecure_raw_read((PGconn *) conn, data, *len);
+
+	if (res < 0)
+	{
+		switch (SOCK_ERRNO)
+		{
+			case ENOENT:
+				status = errSSLClosedGraceful;
+				break;
+
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+		}
+
+		*len = 0;
+	}
+	else
+		*len = res;
+
+	return status;
+}
+
+static OSStatus
+pg_SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len)
+{
+	OSStatus	status = noErr;
+	int			res;
+
+	res = pqsecure_raw_write((PGconn *) conn, data, *len);
+
+	if (res < 0)
+	{
+		switch (SOCK_ERRNO)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	*len = res;
+
+	return status;
+}
+
+/*
+ * import_identity_keychain
+ *		Import the identity for the specified certificate from a Keychain
+ *
+ * Queries the specified Keychain, or the default unless not defined, for a
+ * identity with a certificate matching the passed certificate reference.
+ * Keychains are searched by creating a dictionary of key/value pairs with the
+ * search criteria and then asking for a copy of the matching entry/entries to
+ * the search criteria.
+ */
+static OSStatus
+import_identity_keychain(const char *common_name, SecIdentityRef *identity,
+							CFArrayRef keychains)
+{
+	OSStatus				status = errSecItemNotFound;
+	CFMutableDictionaryRef	query;
+	CFStringRef				cert;
+	SecIdentityRef			temp;
+
+	/*
+	 * Make sure the user didn't just specify keychain: as the sslcert config.
+	 * The passed certificate will have the keychain prefix stripped so in that
+	 * case the string is expected to be empty here.
+	 */
+	if (strlen(common_name) == 0)
+		return errSecInvalidValue;
+
+	query = CFDictionaryCreateMutable(NULL, 0,
+									  &kCFTypeDictionaryKeyCallBacks,
+									  &kCFTypeDictionaryValueCallBacks);
+
+	cert = CFStringCreateWithCString(NULL, common_name, kCFStringEncodingUTF8);
+
+	/*
+	 * If we didn't get a Keychain passed, skip adding it to the dictionary
+	 * thus prompting a search in the users default Keychain.
+	 */
+	if (keychains)
+		CFDictionaryAddValue(query, kSecMatchSearchList, keychains);
+
+	/*
+	 * We don't need to set a kSecMatchLimit key since the default is to only
+	 * return a single reference.  Older versions of macOS had issues with
+	 * certificate matching on labels, but we don't support older versions so
+	 * no need to extract all and match ourselves.
+	 */
+	CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
+	CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+	CFDictionaryAddValue(query, kSecMatchPolicy, SecPolicyCreateSSL(true, NULL));
+	CFDictionaryAddValue(query, kSecAttrLabel, cert);
+
+	status = SecItemCopyMatching(query, (CFTypeRef *) &temp);
+
+	if (status == noErr)
+		*identity = (SecIdentityRef) CFRetain(temp);
+
+	CFRelease(query);
+	CFRelease(cert);
+
+	return status;
+}
+
+static OSStatus
+import_certificate_keychain(const char *common_name, SecCertificateRef *certificate,
+							CFArrayRef keychains, char *hostname)
+{
+	OSStatus				status = errSecItemNotFound;
+	CFMutableDictionaryRef	query;
+	CFStringRef				cert;
+	CFStringRef				host = NULL;
+	CFArrayRef				temp;
+	SecPolicyRef			ssl_policy;
+	int						i;
+
+	/*
+	 * Make sure the user didn't just specify the keychain prefix as the
+	 * certificate config.  The passed certificate will have the keychain
+	 * prefix stripped so in that case the string is expected to be empty.
+	 */
+	if (strlen(common_name) == 0)
+		return errSecInvalidValue;
+
+	query = CFDictionaryCreateMutable(NULL, 0,
+									  &kCFTypeDictionaryKeyCallBacks,
+									  &kCFTypeDictionaryValueCallBacks);
+
+	cert = CFStringCreateWithCString(NULL, common_name, kCFStringEncodingUTF8);
+	CFDictionaryAddValue(query, kSecAttrLabel, cert);
+
+	CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
+	CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+	CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
+
+	/*
+	 * If we didn't get a set of Keychains passed, skip adding it to the
+	 * dictionary thus prompting a search in the users default Keychain.
+	 */
+	if (keychains)
+		CFDictionaryAddValue(query, kSecMatchSearchList, keychains);
+
+	/*
+	 * Specifying a hostname requires it to match the hostname in the leaf
+	 * certificate.
+	 */
+	if (hostname)
+		host = CFStringCreateWithCString(NULL, hostname, kCFStringEncodingUTF8);
+	ssl_policy = SecPolicyCreateSSL(true, host);
+	CFDictionaryAddValue(query, kSecMatchPolicy, ssl_policy);
+
+	/*
+	 * Normally we could have used kSecMatchLimitOne in the above dictionary
+	 * but since there are versions of macOS where the certificate matching on
+	 * the label has been reported to not work (revisions of 10.12), we request
+	 * all and find the one we want.  Copy all the results to a temp array and
+	 * scan it for the certificate we are interested in.
+	 */
+	status = SecItemCopyMatching(query, (CFTypeRef *) &temp);
+	if (status == noErr)
+	{
+		for (i = 0; i < CFArrayGetCount(temp); i++)
+		{
+			SecCertificateRef	search_cert;
+			CFStringRef			cn;
+
+			search_cert = (SecCertificateRef) CFArrayGetValueAtIndex(temp, i);
+
+			if (search_cert != NULL)
+			{
+				SecCertificateCopyCommonName(search_cert, &cn);
+				if (CFStringCompare(cn, cert, 0) == kCFCompareEqualTo)
+				{
+					CFRelease(cn);
+					*certificate = (SecCertificateRef) CFRetain(search_cert);
+					break;
+				}
+
+				CFRelease(cn);
+				CFRelease(search_cert);
+			}
+		}
+
+		CFRelease(temp);
+	}
+
+	CFRelease(ssl_policy);
+	CFRelease(query);
+	CFRelease(cert);
+	if (host)
+		CFRelease(host);
+
+	return status;
+}
+
+static OSStatus
+import_pem(const char *path, char *passphrase, CFArrayRef *certificate)
+{
+	CFDataRef							data_ref;
+	CFStringRef							file_type;
+	SecExternalItemType					item_type;
+	SecItemImportExportKeyParameters	params;
+	SecExternalFormat					format;
+	FILE							   *fp;
+	UInt8							   *certdata;
+	struct stat							buf;
+
+	if (!path || strlen(path) == 0)
+		return errSecInvalidValue;
+
+	if (stat(path, &buf) != 0)
+	{
+		if (errno == ENOENT || errno == ENOTDIR)
+			return -1;
+
+		return errSecInvalidValue;
+	}
+
+	fp = fopen(path, "r");
+	if (!fp)
+		return errSecInvalidValue;
+
+	certdata = malloc(buf.st_size);
+	if (!certdata)
+	{
+		fclose(fp);
+		return errSecAllocate;
+	}
+
+	if (fread(certdata, 1, buf.st_size, fp) != buf.st_size)
+	{
+		fclose(fp);
+		free(certdata);
+		return errSSLBadCert;
+	}
+	fclose(fp);
+
+	data_ref = CFDataCreate(NULL, certdata, buf.st_size);
+	free(certdata);
+
+	memset(&params, 0, sizeof(SecItemImportExportKeyParameters));
+	params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+	/* Set OS default access control on the imported key */
+	params.flags = kSecKeyNoAccessControl;
+	if (passphrase)
+		params.passphrase = CFStringCreateWithCString(NULL, passphrase,
+													  kCFStringEncodingUTF8);
+
+	/*
+	 * Though we explicitly say this is a PEM file, Secure Transport will
+	 * consider that a mere hint. Providing a file ending and a file format is
+	 * what we can do to assist.
+	 */
+	file_type = CFSTR(".pem");
+	if (!file_type)
+		return errSecAllocate;
+
+	format = kSecFormatPEMSequence;
+	item_type = kSecItemTypeCertificate;
+
+	return SecItemImport(data_ref, file_type, &format, &item_type,
+						 0 /* flags */, &params, NULL /* keychain */,
+						 certificate);
+}
+
+/*
+ * Secure Transport has the concept of an identity, which is a packaging of a
+ * private key and the certificate which contains the public key. The identity
+ * is what is used for verifying the connection, so we need to provide a
+ * SecIdentityRef identity to the API.
+ *
+ * A complete, and packaged, identity can be contained in a Keychain, in which
+ * case we can load it directly without having to create anything ourselves.
+ * In the case where we don't have a prepared identity in a Keychain, we need
+ * to create it from its components (certificate and key). The certificate must
+ * in that case be be located in a PEM file on the local filesystem. The key
+ * can either be in a PEM file or in the Keychain.
+ *
+ * While keeping identities in the Keychain is the macOS thing to do, we want
+ * to be functionally compatible with the OpenSSL support in libpq. Thus we not
+ * only need to support creating an identity from a private key contained in a
+ * PEM file, it needs to be the default.  The order in which we discover the
+ * identity is:
+ *
+ * 1. Certificate and key in local files
+ * 2. Certificate in local file and key in Keychain
+ * 3. Identity in Keychain
+ *
+ * Since failures can come from multiple places, the PGconn errorMessage is
+ * populated here even for SSL library errors.
+ */
+static OSStatus
+pg_SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array,
+					  CFArrayRef *key_array, CFArrayRef *rootcert_array)
+{
+	OSStatus			status;
+	struct stat 		buf;
+	char				homedir[MAXPGPATH];
+	char				fnbuf[MAXPGPATH];
+	bool				have_homedir;
+	bool				cert_from_file = false;
+	char	   		   *ssl_err_msg;
+	SecIdentityRef		identity = NULL;
+	SecCertificateRef	cert_ref;
+	SecCertificateRef	root_ref[1];
+	SecKeyRef			key_ref = NULL;
+	CFArrayRef			keychains = NULL;
+	SecKeychainRef		kcref[2];
+	CFMutableArrayRef	cert_connection;
+
+	/*
+	 * If we have a keychain configured, open the referenced keychain as well
+	 * as the default keychain and prepare an array with the references for
+	 * searching. If no additional keychain is specified we don't need to open
+	 * the default keychain and pass to searches since Secure Transport will
+	 * use the default when passing NULL instead of an array of Keychain refs.
+	 */
+	if (conn->keychain)
+	{
+		if (stat(conn->keychain, &buf) == 0)
+		{
+			status = SecKeychainOpen(conn->keychain, &kcref[0]);
+			if (status == noErr && kcref[0] != NULL)
+			{
+				SecKeychainStatus kcstatus;
+				status = SecKeychainGetStatus(kcref[0], &kcstatus);
+				if (status == noErr)
+				{
+					switch (kcstatus)
+					{
+						/*
+						 * If the Keychain is already unlocked, readable or
+						 * writeable, we don't need to do more. If not, try to
+						 * unlock it.
+						 */
+						case kSecUnlockStateStatus:
+						case kSecReadPermStatus:
+						case kSecWritePermStatus:
+							break;
+						default:
+							/*
+							 * TODO: we need to get the passphrase from the
+							 * user, but we currently don't have a good
+							 * mechanism for that. For now, we assume a blank
+							 * passphrase but we need to figure out a good way
+							 * to have the user enter the passphrase.
+							 */
+							SecKeychainUnlock(kcref[0], 0, "", TRUE);
+							break;
+					}
+				}
+
+				/*
+				 * We only need to open, and add, the default Keychain in case
+				 * have a user keychain opened, else we will pass NULL to any
+				 * keychain search which will use the default keychain by..
+				 * default.
+				 */
+				SecKeychainCopyDefault(&kcref[1]);
+				keychains = CFArrayCreate(NULL, (const void **) kcref, 2,
+										  &kCFTypeArrayCallBacks);
+			}
+		}
+	}
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the files
+	 * could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/*
+	 * Try to load the root cert from either a user defined keychain or the
+	 * default Keychain in case none is specified
+	 */
+	if (conn->sslrootcert &&
+		pg_strncasecmp(conn->sslrootcert, KC_PREFIX, KC_PREFIX_LEN) == 0)
+	{
+		root_ref[0] = NULL;
+		strlcpy(fnbuf, conn->sslrootcert + KC_PREFIX_LEN, sizeof(fnbuf));
+
+		import_certificate_keychain(fnbuf, &root_ref[0], keychains, NULL);
+
+		if (root_ref[0])
+			conn->st_rootcert = (void *) CFArrayCreate(NULL,
+													   (const void **) root_ref,
+													   1, &kCFTypeArrayCallBacks);
+	}
+
+	if (!conn->st_rootcert)
+	{
+		if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+			strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0')
+		{
+			if (stat(fnbuf, &buf) != 0)
+			{
+				/*
+				 * stat() failed; assume root file doesn't exist.  If sslmode is
+				 * verify-ca or verify-full, this is an error.  Otherwise, continue
+				 * without performing any server cert verification.
+				 */
+				if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+				{
+					/*
+					 * The only way to reach here with an empty filename is if
+					 * pqGetHomeDirectory failed.  That's a sufficiently unusual
+					 * case that it seems worth having a specialized error message
+					 * for it.
+					 */
+					if (fnbuf[0] == '\0')
+						printfPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not get home directory to locate root certificate file\n"
+														"Either provide the file or change sslmode to disable server certificate verification.\n"));
+					else
+						printfPQExpBuffer(&conn->errorMessage,
+							libpq_gettext("root certificate file \"%s\" does not exist\n"
+										  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+					return errSecInternalError;
+				}
+			}
+			else
+			{
+				status = import_pem(fnbuf, NULL, rootcert_array);
+				if (status != noErr)
+				{
+					ssl_err_msg = pg_SSLerrmessage(status);
+					printfPQExpBuffer(&conn->errorMessage,
+							libpq_gettext("could not load root certificate file \"%s\": %s\n"),
+										  fnbuf, ssl_err_msg);
+					pg_SSLerrfree(ssl_err_msg);
+					return status;
+				}
+
+				if (*rootcert_array != NULL)
+					conn->st_rootcert = (void *) CFRetain(*rootcert_array);
+			}
+		}
+	}
+
+	/*
+	 * If the sslcert config is prefixed with a keychain identifier, the cert
+	 * must be located in either the default or the specified keychain.
+	 */
+	if (conn->sslcert &&
+		pg_strncasecmp(conn->sslcert, KC_PREFIX, KC_PREFIX_LEN) == 0)
+	{
+		strlcpy(fnbuf, conn->sslcert + KC_PREFIX_LEN, sizeof(fnbuf));
+
+		status = import_identity_keychain(fnbuf, &identity, keychains);
+
+		if (identity && status == noErr)
+			SecIdentityCopyPrivateKey(identity, &key_ref);
+		else
+		{
+			ssl_err_msg = pg_SSLerrmessage(status);
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("could not load certificate \"%s\" from keychain: %s\n"),
+							  fnbuf, ssl_err_msg);
+			pg_SSLerrfree(ssl_err_msg);
+
+			return status;
+		}
+	}
+	/*
+	 * No prefix on the sslcert config, the certificate must thus reside in a
+	 * file on disk.
+	 */
+	else
+	{
+		if (conn->sslcert && strlen(conn->sslcert) > 0)
+			strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0')
+		{
+			status = import_pem(fnbuf, NULL, cert_array);
+			if (status != noErr)
+			{
+				if (status == -1)
+					return noErr;
+
+				ssl_err_msg = pg_SSLerrmessage(status);
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("could not load certificate file \"%s\": %s\n"),
+								  fnbuf, ssl_err_msg);
+				pg_SSLerrfree(ssl_err_msg);
+				return status;
+			}
+
+			cert_ref = (SecCertificateRef) CFArrayGetValueAtIndex(*cert_array, 0);
+			cert_from_file = true;
+
+			/*
+			 * We now have a certificate, so we need a private key as well in
+			 * order to create the identity.
+			 */
+
+			/*
+			 * The sslkey config is prefixed with keychain: indicating that the
+			 * key should be loaded from Keychain instead of the filesystem.
+			 * Search for the private key matching the cert_ref in the opened
+			 * Keychains. If found, we get the identity returned.
+			 */
+			if (conn->sslkey &&
+				pg_strncasecmp(conn->sslkey, KC_PREFIX, KC_PREFIX_LEN) == 0)
+			{
+				status = SecIdentityCreateWithCertificate(keychains, cert_ref,
+														  &identity);
+				if (status != noErr)
+				{
+					printfPQExpBuffer(&conn->errorMessage,
+						libpq_gettext("certificate present, but private key \"%s\" not found in Keychain\n"),
+									  fnbuf);
+					return errSecInternalError;
+				}
+
+				SecIdentityCopyPrivateKey(identity, &key_ref);
+			}
+			else
+			{
+				if (conn->sslkey && strlen(conn->sslkey) > 0)
+					strlcpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+				else if (have_homedir)
+					snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+				else
+					fnbuf[0] = '\0';
+
+				/*
+				 * If there is a matching file on the filesystem, require the
+				 * key to be loaded from that file.
+				 */
+				if (fnbuf[0] != '\0')
+				{
+
+					if (stat(fnbuf, &buf) != 0)
+					{
+						printfPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+										  fnbuf);
+						return errSecInvalidKeyRef;
+					}
+#ifndef WIN32
+					if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+					{
+						printfPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+										  fnbuf);
+						return errSecInvalidKeyRef;
+					}
+#endif
+					status = import_pem(fnbuf, NULL, key_array);
+					if (status != noErr)
+					{
+						ssl_err_msg = pg_SSLerrmessage(status);
+						printfPQExpBuffer(&conn->errorMessage,
+							libpq_gettext("could not load private key file \"%s\": %s\n"),
+										  fnbuf, ssl_err_msg);
+						pg_SSLerrfree(ssl_err_msg);
+						return status;
+					}
+
+					key_ref = (SecKeyRef) CFArrayGetValueAtIndex(*key_array, 0);
+				}
+			}
+
+			/*
+			 * We have certificate and a key loaded from files on disk, now we
+			 * can create an identity from this pair.
+			 */
+			if (key_ref)
+				identity = SecIdentityCreate(NULL, cert_ref, key_ref);
+		}
+	}
+
+	if (!identity)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not create identity from certificate/key\n"));
+		return errSecInvalidValue;
+	}
+
+	/*
+	 * If we have created the identity "by hand" without involving the
+	 * Keychain we need to include the certificates in the array passed to
+	 * SSLSetCertificate()
+	 */
+	if (cert_from_file)
+	{
+		cert_connection = CFArrayCreateMutableCopy(NULL, 0, *cert_array);
+		CFArraySetValueAtIndex(cert_connection, 0, identity);
+	}
+	else
+	{
+		cert_connection = CFArrayCreateMutable(NULL, 1L,
+											   &kCFTypeArrayCallBacks);
+		CFArrayInsertValueAtIndex(cert_connection, 0,
+								  (const void *) identity);
+	}
+
+	status = SSLSetCertificate(conn->ssl, cert_connection);
+
+	if (status != noErr)
+	{
+		ssl_err_msg = pg_SSLerrmessage(status);
+		printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("could not set certificate for connection: (%d) %s\n"),
+							  status, ssl_err_msg);
+		pg_SSLerrfree(ssl_err_msg);
+		return status;
+	}
+
+	if (key_ref)
+	{
+		conn->ssl_key_bits = SecKeyGetBlockSize(key_ref);
+		CFRelease(key_ref);
+	}
+
+	return noErr;
+}
+
+/* ------------------------------------------------------------ */
+/*					SSL information functions					*/
+/* ------------------------------------------------------------ */
+
+void *
+PQgetssl(PGconn *conn)
+{
+	/*
+	 * Always return NULL as this is legacy and defined to be equal to
+	 * PQsslStruct(conn, "OpenSSL");
+	 */
+	return NULL;
+}
+
+void *
+PQsslStruct(PGconn *conn, const char *struct_name)
+{
+	if (!conn)
+		return NULL;
+	if (strcmp(struct_name, "SecureTransport") == 0)
+		return conn->ssl;
+	return NULL;
+}
+
+const char *const *
+PQsslAttributeNames(PGconn *conn)
+{
+	static const char *const result[] = {
+		"library",
+		"key_bits",
+		"cipher",
+		"protocol",
+		NULL
+	};
+
+	return result;
+}
+
+const char *
+PQsslAttribute(PGconn *conn, const char *attribute_name)
+{
+	SSLCipherSuite	cipher;
+	SSLProtocol		protocol;
+	OSStatus		status;
+	const char 	   *attribute = NULL;
+
+	if (!conn || !conn->ssl)
+		return NULL;
+
+	if (strcmp(attribute_name, "library") == 0)
+		attribute = "SecureTransport";
+	else if (strcmp(attribute_name, "key_bits") == 0)
+	{
+		if (conn->ssl_key_bits > 0)
+		{
+			static char sslbits_str[10];
+			snprintf(sslbits_str, sizeof(sslbits_str), "%d", conn->ssl_key_bits);
+			attribute = sslbits_str;
+		}
+	}
+	else if (strcmp(attribute_name, "cipher") == 0)
+	{
+		status = SSLGetNegotiatedCipher(conn->ssl, &cipher);
+		if (status == noErr)
+			return pg_SSLciphername(cipher);
+	}
+	else if (strcmp(attribute_name, "protocol") == 0)
+	{
+		status = SSLGetNegotiatedProtocolVersion(conn->ssl, &protocol);
+		if (status == noErr)
+		{
+			switch (protocol)
+			{
+				case kTLSProtocol11:
+					attribute = "TLSv1.1";
+					break;
+				case kTLSProtocol12:
+					attribute = "TLSv1.2";
+					break;
+				default:
+					break;
+			}
+		}
+	}
+
+	return attribute;
+}
+
+/* ------------------------------------------------------------ */
+/*			Secure Transport Information Functions				*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Obtain reason string for passed SSL errcode
+ */
+static char ssl_noerr[] = "no SSL error reported";
+static char ssl_nomem[] = "out of memory allocating error description";
+#define SSL_ERR_LEN 128
+
+static char *
+pg_SSLerrmessage(OSStatus errcode)
+{
+	char 	   *err_buf;
+	const char *tmp;
+	CFStringRef	err_msg;
+
+	if (errcode == noErr || errcode == errSecSuccess)
+		return ssl_noerr;
+
+	err_buf = malloc(SSL_ERR_LEN);
+	if (!err_buf)
+		return ssl_nomem;
+
+	err_msg = SecCopyErrorMessageString(errcode, NULL);
+	if (err_msg)
+	{
+		tmp = CFStringGetCStringPtr(err_msg, kCFStringEncodingUTF8);
+		strlcpy(err_buf, tmp, SSL_ERR_LEN);
+		CFRelease(err_msg);
+	}
+	else
+		snprintf(err_buf, sizeof(err_buf), _("SSL error code %d"), errcode);
+
+	return err_buf;
+}
+
+static void
+pg_SSLerrfree(char *err_buf)
+{
+	if (err_buf && err_buf != ssl_nomem && err_buf != ssl_noerr)
+		free(err_buf);
+}
+
+/*
+ * pg_SSLsessionstate
+ *
+ * Returns 0 if the connection is open and -1 in case the connection is closed,
+ * or its status unknown. If msg is non-NULL the current state is copied with
+ * at most len - 1 characters ensuring a NUL terminated returned string.
+ */
+static int
+pg_SSLsessionstate(PGconn *conn, char *msg, size_t len)
+{
+	SSLSessionState		state;
+	OSStatus			status;
+	const char 		   *status_msg;
+
+	/*
+	 * If conn->ssl isn't defined we will report "Unknown" which it could be
+	 * argued being correct or not, but since we don't know if there has ever
+	 * been a connection at all it's not more correct to say "Closed" or
+	 * "Aborted".
+	 */
+	if (conn->ssl)
+		status = SSLGetSessionState(conn->ssl, &state);
+	else
+	{
+		status = errSecInternalError;
+		state = -1;
+	}
+
+	switch (state)
+	{
+		case kSSLConnected:
+			status_msg = "Connected";
+			status = 0;
+			break;
+		case kSSLHandshake:
+			status_msg = "Handshake";
+			status = 0;
+			break;
+		case kSSLIdle:
+			status_msg = "Idle";
+			status = 0;
+			break;
+		case kSSLClosed:
+			status_msg = "Closed";
+			status = -1;
+			break;
+		case kSSLAborted:
+			status_msg = "Aborted";
+			status = -1;
+			break;
+		default:
+			status_msg = "Unknown";
+			status = -1;
+			break;
+	}
+
+	if (msg)
+		strlcpy(msg, status_msg, len);
+
+	return (status == noErr ? 0 : -1);
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index f7dc249..5fa6734 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -157,8 +157,10 @@ void
 PQinitOpenSSL(int do_ssl, int do_crypto)
 {
 #ifdef USE_SSL
+#ifdef USE_OPENSSL
 	pgtls_init_library(do_ssl, do_crypto);
 #endif
+#endif
 }
 
 /*
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index bdd8f9d..94b56aa 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -80,6 +80,14 @@ typedef struct
 #endif
 #endif							/* USE_OPENSSL */
 
+#ifdef USE_SECURETRANSPORT
+#define Size pg_Size
+#define uint64 pg_uint64
+#include <Security/Security.h>
+#undef Size
+#undef uint64
+#endif
+
 /*
  * POSTGRES backend dependent Constants.
  */
@@ -474,8 +482,19 @@ struct pg_conn
 	void	   *engine;			/* dummy field to keep struct the same if
 								 * OpenSSL version changes */
 #endif
-#endif							/* USE_OPENSSL */
-#endif							/* USE_SSL */
+#endif   /* USE_OPENSSL */
+
+#ifdef USE_SECURETRANSPORT
+	char	   *keychain;
+	bool		keychain_use_default;
+
+	SSLContextRef	 ssl;		/* SSL context reference */
+	void		    *st_rootcert;
+	ssize_t			 ssl_buffered;
+	int				 ssl_key_bits;
+#endif   /* USE_SECURETRANSPORT */
+
+#endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
 	gss_ctx_id_t gctx;			/* GSS context */
@@ -728,6 +747,7 @@ extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
  *
  * This is not supported with old versions of OpenSSL that don't have
  * the X509_get_signature_nid() function.
+ * It's also not supported with Apple Secure Transport.
  */
 #if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
 #define HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
index 97389c9..0c9b4cd 100644
--- a/src/test/ssl/Makefile
+++ b/src/test/ssl/Makefile
@@ -14,6 +14,7 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 export with_openssl
+export with_securetransport
 
 CERTIFICATES := server_ca server-cn-and-alt-names \
 	server-cn-only server-single-alt-name server-multiple-alt-names \
@@ -130,6 +131,20 @@ ssl/root+server.crl: ssl/root.crl ssl/server.crl
 ssl/root+client.crl: ssl/root.crl ssl/client.crl
 	cat $^ > $@
 
+#### Keychains
+
+ifeq ($(with_securetransport),yes)
+
+KEYCHAINS := ssl/client.keychain
+
+# This target generates all Keychains
+keychains: $(KEYCHAINS)
+
+PWD=$(shell pwd)
+ssl/client.keychain: ssl/client.crt ssl/client.key
+	certtool i $(PWD)/ssl/client.crt c k=$(PWD)/ssl/client.keychain r=$(PWD)/ssl/client.key p=
+endif
+
 .PHONY: sslfiles-clean
 sslfiles-clean:
 	rm -f $(SSLFILES) ssl/client_ca.srl ssl/server_ca.srl ssl/client_ca-certindex* ssl/server_ca-certindex* ssl/root_ca-certindex* ssl/root_ca.srl ssl/temp_ca.crt ssl/temp_ca_signed.crt
diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm
index 3b451a3..2331939 100644
--- a/src/test/ssl/ServerSetup.pm
+++ b/src/test/ssl/ServerSetup.pm
@@ -26,12 +26,22 @@ use Test::More;
 
 use Exporter 'import';
 our @EXPORT = qw(
+  set_backend
   configure_test_server_for_ssl
   switch_server_cert
   test_connect_fails
   test_connect_ok
 );
 
+our $current_backend;
+
+sub set_backend
+{
+	my ($backend) = @_;
+
+	$current_backend = $backend;
+}
+
 # Define a couple of helper functions to test connecting to the server.
 
 # The first argument is a base connection string to use for connection.
@@ -56,7 +66,7 @@ sub test_connect_fails
 {
 	local $Test::Builder::Level = $Test::Builder::Level + 1;
 
-	my ($common_connstr, $connstr, $expected_stderr, $test_name) = @_;
+	my ($common_connstr, $connstr, $test_name, %expected_stderr) = @_;
 
 	my $cmd = [
 		'psql', '-X', '-A', '-t', '-c',
@@ -64,7 +74,7 @@ sub test_connect_fails
 		'-d', "$common_connstr $connstr"
 	];
 
-	command_fails_like($cmd, $expected_stderr, $test_name);
+	command_fails_like($cmd, $expected_stderr{$current_backend}, $test_name);
 	return;
 }
 
@@ -155,7 +165,14 @@ sub switch_server_cert
 	print $sslconf "ssl_ca_file='$cafile.crt'\n";
 	print $sslconf "ssl_cert_file='$certfile.crt'\n";
 	print $sslconf "ssl_key_file='$certfile.key'\n";
-	print $sslconf "ssl_crl_file='root+client.crl'\n";
+	if (check_pg_config("#define USE_SECURETRANSPORT 1"))
+	{
+		print $sslconf "ssl_crl_file=''\n";
+	}
+	else
+	{
+		print $sslconf "ssl_crl_file='root+client.crl'\n";
+	}
 	close $sslconf;
 
 	$node->restart;
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 2b875a3..6352bb0 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -6,9 +6,34 @@ use Test::More;
 use ServerSetup;
 use File::Copy;
 
+# Some tests are backend specific, so store the currently used backend and
+# some particular capabilities which are interesting for the test
+my %tls_backend;
+
 if ($ENV{with_openssl} eq 'yes')
 {
-	plan tests => 65;
+	$tls_backend{library} = 'openssl';
+	$tls_backend{library_name} = 'OpenSSL';
+	$tls_backend{tests} = 65;
+	$tls_backend{crl_support} = 1;
+	$tls_backend{keychain_support} = 0;
+}
+elsif ($ENV{with_securetransport} eq 'yes')
+{
+	$tls_backend{library} = 'securetransport';
+	$tls_backend{library_name} = 'Secure Transport';
+	$tls_backend{tests} = 68;
+	$tls_backend{crl_support} = 0;
+	$tls_backend{keychain_support} = 1;
+}
+else
+{
+	$tls_backend{library} = undef;
+}
+
+if (defined $tls_backend{library})
+{
+	plan tests => $tls_backend{tests};
 }
 else
 {
@@ -25,6 +50,8 @@ my $SERVERHOSTADDR = '127.0.0.1';
 # Allocation of base connection string shared among multiple tests.
 my $common_connstr;
 
+set_backend($tls_backend{library});
+
 # The client's private key must not be world-readable, so take a copy
 # of the key stored in the code tree and update its permissions.
 copy("ssl/client.key", "ssl/client_tmp.key");
@@ -52,7 +79,7 @@ $node->start;
 
 # Run this before we lock down access below.
 my $result = $node->safe_psql('postgres', "SHOW ssl_library");
-is($result, 'OpenSSL', 'ssl_library parameter');
+is($result, $tls_backend{library_name}, 'ssl_library parameter');
 
 configure_test_server_for_ssl($node, $SERVERHOSTADDR, 'trust');
 
@@ -65,10 +92,28 @@ print $sslconf "ssl_key_file='server-password.key'\n";
 print $sslconf "ssl_passphrase_command='echo wrongpassword'\n";
 close $sslconf;
 
-command_fails(
-	[ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
-	'restart fails with password-protected key file with wrong password');
-$node->_update_pid(0);
+if ($tls_backend{library} eq 'securetransport')
+{
+	command_ok(
+		[ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
+		'restart succeeds with password-protected key file with wrong password');
+	$node->_update_pid(1);
+
+	$common_connstr =
+	  "user=ssltestuser dbname=trustdb sslcert=invalid hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
+	test_connect_fails(
+		$common_connstr,
+		"sslrootcert=invalid sslmode=require",
+		"connect without server root cert sslmode=require",
+		( securetransport => qr/record overflow/ ));
+}
+else
+{
+	command_fails(
+		[ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ],
+		'restart fails with password-protected key file with wrong password');
+	$node->_update_pid(0);
+}
 
 open $sslconf, '>', $node->data_dir . "/sslconfig.conf";
 print $sslconf "ssl=on\n";
@@ -98,8 +143,8 @@ $common_connstr =
 # The server should not accept non-SSL connections.
 test_connect_fails(
 	$common_connstr, "sslmode=disable",
-	qr/\Qno pg_hba.conf entry\E/,
-	"server doesn't accept non-SSL connections");
+	"server doesn't accept non-SSL connections",
+	( openssl => qr/\Qno pg_hba.conf entry\E/ ,securetransport => qr/\Qno pg_hba.conf entry\E/ ));
 
 # Try without a root cert. In sslmode=require, this should work. In verify-ca
 # or verify-full mode it should fail.
@@ -110,31 +155,50 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"sslrootcert=invalid sslmode=verify-ca",
-	qr/root certificate file "invalid" does not exist/,
-	"connect without server root cert sslmode=verify-ca");
+	"connect without server root cert sslmode=verify-ca",
+	(
+		openssl => qr/root certificate file "invalid" does not exist/,
+		securetransport => qr/root certificate file "invalid" does not exist/
+	));
 test_connect_fails(
 	$common_connstr,
 	"sslrootcert=invalid sslmode=verify-full",
-	qr/root certificate file "invalid" does not exist/,
-	"connect without server root cert sslmode=verify-full");
+	"connect without server root cert sslmode=verify-full",
+	(
+		openssl => qr/root certificate file "invalid" does not exist/,
+		securetransport => qr/root certificate file "invalid" does not exist/
+	));
 
 # Try with wrong root cert, should fail. (We're using the client CA as the
 # root, but the server's key is signed by the server CA.)
 test_connect_fails($common_connstr,
 	"sslrootcert=ssl/client_ca.crt sslmode=require",
-	qr/SSL error/, "connect with wrong server root cert sslmode=require");
+	"connect with wrong server root cert sslmode=require",
+	(
+		openssl => qr/SSL error/,
+		securetransport => qr/The specified item has no access control/
+	));
 test_connect_fails($common_connstr,
 	"sslrootcert=ssl/client_ca.crt sslmode=verify-ca",
-	qr/SSL error/, "connect with wrong server root cert sslmode=verify-ca");
+	"connect with wrong server root cert sslmode=verify-ca",
+	(
+		openssl => qr/SSL error/,
+		securetransport => qr/The specified item has no access control/
+	));
 test_connect_fails($common_connstr,
 	"sslrootcert=ssl/client_ca.crt sslmode=verify-full",
-	qr/SSL error/, "connect with wrong server root cert sslmode=verify-full");
+	"connect with wrong server root cert sslmode=verify-full",
+	(
+		openssl => qr/SSL error/,
+		securetransport => qr/The specified item has no access control/
+	));
 
 # Try with just the server CA's cert. This fails because the root file
 # must contain the whole chain up to the root CA.
 test_connect_fails($common_connstr,
 	"sslrootcert=ssl/server_ca.crt sslmode=verify-ca",
-	qr/SSL error/, "connect with server CA cert, without root CA");
+	"connect with server CA cert, without root CA",
+	( openssl => qr/SSL error/, securetransport => qr/SSL error/));
 
 # And finally, with the correct root cert.
 test_connect_ok(
@@ -163,24 +227,36 @@ test_connect_ok(
 
 # CRL tests
 
-# Invalid CRL filename is the same as no CRL, succeeds
-test_connect_ok(
-	$common_connstr,
-	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid",
-	"sslcrl option with invalid file name");
-
-# A CRL belonging to a different CA is not accepted, fails
-test_connect_fails(
-	$common_connstr,
-	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
-	qr/SSL error/,
-	"CRL belonging to a different CA");
-
-# With the correct CRL, succeeds (this cert is not revoked)
-test_connect_ok(
-	$common_connstr,
-	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
-	"CRL with a non-revoked cert");
+if ($tls_backend{crl_support})
+{
+	# Invalid CRL filename is the same as no CRL, succeeds
+	test_connect_ok(
+		$common_connstr,
+		"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid",
+		"sslcrl option with invalid file name");
+
+	# A CRL belonging to a different CA is not accepted, fails
+	test_connect_fails(
+		$common_connstr,
+		"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
+		"CRL belonging to a different CA",
+		( openssl => qr/SSL error/ ));
+
+	# With the correct CRL, succeeds (this cert is not revoked)
+	test_connect_ok(
+		$common_connstr,
+		"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
+		"CRL with a non-revoked cert");
+}
+else
+{
+	# Test that the presence of a CRL configuration throws an error
+	test_connect_fails(
+		$common_connstr,
+		"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid",
+		"unsupported sslcrl option",
+		( securetransport => qr/CRL files are not supported/ ));
+}
 
 # Check that connecting with verify-full fails, when the hostname doesn't
 # match the hostname in the server's certificate.
@@ -198,8 +274,11 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"sslmode=verify-full host=wronghost.test",
-	qr/\Qserver certificate for "common-name.pg-ssltest.test" does not match host name "wronghost.test"\E/,
-	"mismatch between host name and server certificate sslmode=verify-full");
+	"mismatch between host name and server certificate sslmode=verify-full",
+	(
+		openssl => qr/\Qserver certificate for "common-name.pg-ssltest.test" does not match host name "wronghost.test"\E/,
+		securetransport => qr/The specified item has no access control/
+	));
 
 # Test Subject Alternative Names.
 switch_server_cert($node, 'server-multiple-alt-names');
@@ -223,13 +302,19 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"host=wronghost.alt-name.pg-ssltest.test",
-	qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "wronghost.alt-name.pg-ssltest.test"\E/,
-	"host name not matching with X.509 Subject Alternative Names");
+	"host name not matching with X.509 Subject Alternative Names",
+	(
+		openssl => qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "wronghost.alt-name.pg-ssltest.test"\E/,
+		securetransport => qr/The specified item has no access control/
+	));
 test_connect_fails(
 	$common_connstr,
 	"host=deep.subdomain.wildcard.pg-ssltest.test",
-	qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/,
-	"host name not matching with X.509 Subject Alternative Names wildcard");
+	"host name not matching with X.509 Subject Alternative Names wildcard",
+	(
+		openssl => qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 2 other names) does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/,
+		securetransport => qr/The specified item has no access control/
+	));
 
 # Test certificate with a single Subject Alternative Name. (this gives a
 # slightly different error message, that's all)
@@ -246,14 +331,19 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"host=wronghost.alt-name.pg-ssltest.test",
-	qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "wronghost.alt-name.pg-ssltest.test"\E/,
-	"host name not matching with a single X.509 Subject Alternative Name");
+	"host name not matching with a single X.509 Subject Alternative Name",
+	(
+		openssl => qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "wronghost.alt-name.pg-ssltest.test"\E/,
+		securetransport => qr/The specified item has no access control/
+	));
 test_connect_fails(
 	$common_connstr,
 	"host=deep.subdomain.wildcard.pg-ssltest.test",
-	qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/,
-	"host name not matching with a single X.509 Subject Alternative Name wildcard"
-);
+	"host name not matching with a single X.509 Subject Alternative Name wildcard",
+	(
+		openssl => qr/\Qserver certificate for "single.alt-name.pg-ssltest.test" does not match host name "deep.subdomain.wildcard.pg-ssltest.test"\E/,
+		securetransport => qr/The specified item has no access control/
+	));
 
 # Test server certificate with a CN and SANs. Per RFCs 2818 and 6125, the CN
 # should be ignored when the certificate has both.
@@ -273,8 +363,11 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"host=common-name.pg-ssltest.test",
-	qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 1 other name) does not match host name "common-name.pg-ssltest.test"\E/,
-	"certificate with both a CN and SANs ignores CN");
+	"certificate with both a CN and SANs ignores CN",
+	(
+		openssl => qr/\Qserver certificate for "dns1.alt-name.pg-ssltest.test" (and 1 other name) does not match host name "common-name.pg-ssltest.test"\E/,
+		securetransport => qr/The specified item has no access control/
+	));
 
 # Finally, test a server certificate that has no CN or SANs. Of course, that's
 # not a very sensible certificate, but libpq should handle it gracefully.
@@ -289,8 +382,11 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"sslmode=verify-full host=common-name.pg-ssltest.test",
-	qr/could not get server's host name from server certificate/,
-	"server certificate without CN or SANs sslmode=verify-full");
+	"server certificate without CN or SANs sslmode=verify-full",
+	(
+		openssl => qr/could not get server's host name from server certificate/,
+		securetransport => qr/The specified item has no access control/
+	));
 
 # Test that the CRL works
 switch_server_cert($node, 'server-revoked');
@@ -303,11 +399,46 @@ test_connect_ok(
 	$common_connstr,
 	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca",
 	"connects without client-side CRL");
-test_connect_fails(
-	$common_connstr,
-	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
-	qr/SSL error/,
-	"does not connect with client-side CRL");
+if ($tls_backend{crl_support})
+{
+	test_connect_fails(
+		$common_connstr,
+		"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
+		"does not connect with client-side CRL",
+		( openssl => qr/SSL error/ ));
+}
+else
+{
+	test_connect_fails(
+		$common_connstr,
+		"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/root+server.crl",
+		"does not connect with client-side CRL",
+		( securetransport => qr/CRL files are not supported with Secure Transport/ ));
+}
+
+# test Secure Transport keychain support
+if ($tls_backend{keychain_support})
+{
+	$common_connstr =
+		"user=ssltestuser dbname=certdb hostaddr=$SERVERHOSTADDR sslmode=verify-full";
+	# empty keychain
+	test_connect_fails($common_connstr,
+		"keychain=invalid",
+		"invalid Keychain file reference",
+		( securetransport => qr/The specified item has no access control/ ));
+
+	# correct client cert in keychain with and without proper label
+	test_connect_fails(
+		$common_connstr,
+		"keychain=ssl/client.keychain",
+		"client cert in keychain but without label",
+		( securetransport => qr/The specified item has no access control/ ));
+	test_connect_ok(
+		$common_connstr,
+		"sslcert=keychain:ssltestuser keychain=ssl/client.keychain",
+		"client cert in keychain");
+}
+
 
 ### Server-side tests.
 ###
@@ -322,8 +453,11 @@ $common_connstr =
 test_connect_fails(
 	$common_connstr,
 	"user=ssltestuser sslcert=invalid",
-	qr/connection requires a valid client certificate/,
-	"certificate authorization fails without client cert");
+	"certificate authorization fails without client cert",
+	(
+		openssl => qr/connection requires a valid client certificate/,
+		securetransport => qr/connection requires a valid client certificate/
+	));
 
 # correct client cert
 test_connect_ok(
@@ -335,23 +469,30 @@ test_connect_ok(
 test_connect_fails(
 	$common_connstr,
 	"user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_wrongperms_tmp.key",
-	qr!\Qprivate key file "ssl/client_wrongperms_tmp.key" has group or world access\E!,
-	"certificate authorization fails because of file permissions");
+	"certificate authorization fails because of file permissions",
+	(
+		openssl => qr!\Qprivate key file "ssl/client_wrongperms_tmp.key" has group or world access\E!,
+		securetransport => qr!\Qprivate key file "ssl/client_wrongperms_tmp.key" has group or world access\E!
+	));
 
 # client cert belonging to another user
 test_connect_fails(
 	$common_connstr,
 	"user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key",
-	qr/certificate authentication failed for user "anotheruser"/,
-	"certificate authorization fails with client cert belonging to another user"
-);
+	"certificate authorization fails with client cert belonging to another user",
+	(
+		openssl => qr/certificate authentication failed for user "anotheruser"/,
+		securetransport => qr/certificate authentication failed for user "anotheruser"/
+	));
 
-# revoked client cert
-test_connect_fails(
-	$common_connstr,
-	"user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked_tmp.key",
-	qr/SSL error/,
-	"certificate authorization fails with revoked client cert");
+if ($tls_backend{crl_support})
+{
+	test_connect_fails(
+		$common_connstr,
+		"user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked_tmp.key",
+		"certificate authorization fails with revoked client cert",
+		( openssl => qr/SSL error/, securetransport => qr/SSL error/ ));
+}
 
 # intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file
 switch_server_cert($node, 'server-cn-only', 'root_ca');
@@ -363,8 +504,13 @@ test_connect_ok(
 	"sslmode=require sslcert=ssl/client+client_ca.crt",
 	"intermediate client certificate is provided by client");
 test_connect_fails($common_connstr, "sslmode=require sslcert=ssl/client.crt",
-	qr/SSL error/, "intermediate client certificate is missing");
+	"intermediate client certificate is missing",
+	(
+		openssl => qr/SSL error/,
+		securetransport => qr/certificate authentication failed/
+	));
 
 # clean up
 unlink("ssl/client_tmp.key", "ssl/client_wrongperms_tmp.key",
 	"ssl/client-revoked_tmp.key");
+unlink("ssl/client.keychain") if ($tls_backend{keychain_support});
diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl
index b460a7f..5c36bdd 100644
--- a/src/test/ssl/t/002_scram.pl
+++ b/src/test/ssl/t/002_scram.pl
@@ -8,7 +8,8 @@ use Test::More;
 use ServerSetup;
 use File::Copy;
 
-if ($ENV{with_openssl} ne 'yes')
+if ($ENV{with_openssl} ne 'yes' &&
+	$ENV{with_securetransport} ne 'yes')
 {
 	plan skip_all => 'SSL not supported by this build';
 }
