diff --git a/configure b/configure
index 0d76e5ea42..33b1f00bff 100755
--- a/configure
+++ b/configure
@@ -709,6 +709,7 @@ UUID_EXTRA_OBJS
 with_uuid
 with_systemd
 with_selinux
+with_gnutls
 with_openssl
 krb_srvtab
 with_python
@@ -838,6 +839,7 @@ with_bsd_auth
 with_ldap
 with_bonjour
 with_openssl
+with_gnutls
 with_selinux
 with_systemd
 with_readline
@@ -1534,6 +1536,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-gnutls           build with GnuTS 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
@@ -6051,6 +6054,41 @@ fi
 $as_echo "$with_openssl" >&6; }
 
 
+#
+# GnuTLS
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with GnuTLS support" >&5
+$as_echo_n "checking whether to build with GnuTLS support... " >&6; }
+
+
+
+# Check whether --with-gnutls was given.
+if test "${with_gnutls+set}" = set; then :
+  withval=$with_gnutls;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_GNUTLS 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-gnutls option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_gnutls=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_gnutls" >&5
+$as_echo "$with_gnutls" >&6; }
+
+
 #
 # SELinux
 #
@@ -10218,6 +10256,83 @@ done
 
 fi
 
+if test "$with_gnutls" = yes ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gnutls_init" >&5
+$as_echo_n "checking for library containing gnutls_init... " >&6; }
+if ${ac_cv_search_gnutls_init+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gnutls_init ();
+int
+main ()
+{
+return gnutls_init ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' gnutls; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_gnutls_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_gnutls_init+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_gnutls_init+:} false; then :
+
+else
+  ac_cv_search_gnutls_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gnutls_init" >&5
+$as_echo "$ac_cv_search_gnutls_init" >&6; }
+ac_res=$ac_cv_search_gnutls_init
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+  as_fn_error $? "library 'gnutls' is required for GnuTLS" "$LINENO" 5
+fi
+
+  # GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
+  # certificate chains.
+  ac_fn_c_check_decl "$LINENO" "GNUTLS_X509_CRT_LIST_SORT" "ac_cv_have_decl_GNUTLS_X509_CRT_LIST_SORT" "#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+"
+if test "x$ac_cv_have_decl_GNUTLS_X509_CRT_LIST_SORT" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT $ac_have_decl
+_ACEOF
+
+fi
+
 if test "$with_pam" = yes ; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
 $as_echo_n "checking for pam_start in -lpam... " >&6; }
@@ -11015,6 +11130,17 @@ else
 fi
 
 
+fi
+
+if test "$with_gnutls" = yes ; then
+  ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default"
+if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then :
+
+else
+  as_fn_error $? "header file <gnutls/gnutls.h> is required for GnuTLS" "$LINENO" 5
+fi
+
+
 fi
 
 if test "$with_pam" = yes ; then
@@ -15522,9 +15648,11 @@ fi
 # in the template or configure command line.
 
 # If not selected manually, try to select a source automatically.
-if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
+if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_GNUTLS_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
   if test x"$with_openssl" = x"yes" ; then
     USE_OPENSSL_RANDOM=1
+  elif test x"$with_gnutls" = x"yes" ; then
+    USE_GNUTLS_RANDOM=1
   elif test "$PORTNAME" = "win32" ; then
     USE_WIN32_RANDOM=1
   else
@@ -15563,6 +15691,12 @@ $as_echo "#define USE_OPENSSL_RANDOM 1" >>confdefs.h
 
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL" >&5
 $as_echo "OpenSSL" >&6; }
+  elif test x"$USE_GNUTLS_RANDOM" = x"1" ; then
+
+$as_echo "#define USE_GNUTLS_RANDOM 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: GnuTLS" >&5
+$as_echo "GnuTLS" >&6; }
   elif test x"$USE_WIN32_RANDOM" = x"1" ; then
 
 $as_echo "#define USE_WIN32_RANDOM 1" >>confdefs.h
diff --git a/configure.in b/configure.in
index bdc41b071f..62af7525b7 100644
--- a/configure.in
+++ b/configure.in
@@ -734,6 +734,15 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
 AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
+#
+# GnuTLS
+#
+AC_MSG_CHECKING([whether to build with GnuTLS support])
+PGAC_ARG_BOOL(with, gnutls, no, [build with GnuTS support],
+              [AC_DEFINE([USE_GNUTLS], 1, [Define to build with GnuTLS support. (--with-gnutls)])])
+AC_MSG_RESULT([$with_gnutls])
+AC_SUBST(with_gnutls)
+
 #
 # SELinux
 #
@@ -1108,6 +1117,16 @@ if test "$with_openssl" = yes ; then
   AC_CHECK_FUNCS([CRYPTO_lock])
 fi
 
+if test "$with_gnutls" = yes ; then
+  AC_SEARCH_LIBS(gnutls_init, gnutls, [], [AC_MSG_ERROR([library 'gnutls' is required for GnuTLS])])
+  # GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
+  # certificate chains.
+  AC_CHECK_DECLS(GNUTLS_X509_CRT_LIST_SORT, [], [],
+[#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+])
+fi
+
 if test "$with_pam" = yes ; then
   AC_CHECK_LIB(pam,    pam_start, [], [AC_MSG_ERROR([library 'pam' is required for PAM])])
 fi
@@ -1255,6 +1274,10 @@ 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_gnutls" = yes ; then
+  AC_CHECK_HEADER(gnutls/gnutls.h, [], [AC_MSG_ERROR([header file <gnutls/gnutls.h> is required for GnuTLS])])
+fi
+
 if test "$with_pam" = yes ; then
   AC_CHECK_HEADERS(security/pam_appl.h, [],
                    [AC_CHECK_HEADERS(pam/pam_appl.h, [],
@@ -1992,9 +2015,11 @@ fi
 # in the template or configure command line.
 
 # If not selected manually, try to select a source automatically.
-if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
+if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_GNUTLS_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
   if test x"$with_openssl" = x"yes" ; then
     USE_OPENSSL_RANDOM=1
+  elif test x"$with_gnutls" = x"yes" ; then
+    USE_GNUTLS_RANDOM=1
   elif test "$PORTNAME" = "win32" ; then
     USE_WIN32_RANDOM=1
   else
@@ -2011,6 +2036,9 @@ if test "$enable_strong_random" = yes ; then
   if test x"$USE_OPENSSL_RANDOM" = x"1" ; then
     AC_DEFINE(USE_OPENSSL_RANDOM, 1, [Define to use OpenSSL for random number generation])
     AC_MSG_RESULT([OpenSSL])
+  elif test x"$USE_GNUTLS_RANDOM" = x"1" ; then
+    AC_DEFINE(USE_GNUTLS_RANDOM, 1, [Define to use GnuTLS for random number generation])
+    AC_MSG_RESULT([GnuTLS])
   elif test x"$USE_WIN32_RANDOM" = x"1" ; then
     AC_DEFINE(USE_WIN32_RANDOM, 1, [Define to use native Windows API for random number generation])
     AC_MSG_RESULT([Windows native])
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index fae8068150..82c0cca57f 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -184,6 +184,7 @@ with_perl	= @with_perl@
 with_python	= @with_python@
 with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
+with_gnutls    = @with_gnutls@
 with_selinux	= @with_selinux@
 with_systemd	= @with_systemd@
 with_libxml	= @with_libxml@
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 7fa2b02743..9d29037d35 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -19,6 +19,8 @@ OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ifaddr.o pqcomm.o \
 
 ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
+else ifeq ($(with_gnutls),yes)
+OBJS += be-secure-gnutls.o
 endif
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/be-secure-gnutls.c b/src/backend/libpq/be-secure-gnutls.c
new file mode 100644
index 0000000000..cb00302971
--- /dev/null
+++ b/src/backend/libpq/be-secure-gnutls.c
@@ -0,0 +1,913 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-gnutls.c
+ *	  functions for GnuTLS support in the backend.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-gnutls.c
+ *
+ *	  Since the server static private key ($DataDir/server.key)
+ *	  will normally be stored unencrypted so that the database
+ *	  backend can restart automatically, it is important that
+ *	  we select an algorithm that continues to provide confidentiality
+ *	  even if the attacker has the server's private key.  Ephemeral
+ *	  DH (EDH) keys provide this and more (Perfect Forward Secrecy
+ *	  aka PFS).
+ *
+ *	  N.B., the static private key should still be protected to
+ *	  the largest extent possible, to minimize the risk of
+ *	  impersonations.
+ *
+ *	  Another benefit of EDH is that it allows the backend and
+ *	  clients to use DSA keys.  DSA keys can only provide digital
+ *	  signatures, not encryption, and are often acceptable in
+ *	  jurisdictions where RSA keys are unacceptable.
+ *
+ *	  The downside to EDH is that it makes it impossible to
+ *	  use ssldump(1) if there's a problem establishing an SSL
+ *	  session.  In this case you'll need to temporarily disable
+ *	  EDH by commenting out the callback.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#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 <gnutls/x509.h>
+#include <gnutls/pkcs11.h>
+
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/fd.h"
+#include "storage/latch.h"
+#include "tcop/tcopprot.h"
+#include "utils/memutils.h"
+
+
+static ssize_t my_sock_read(gnutls_transport_ptr_t h, void *buf, size_t size);
+static ssize_t my_sock_write(gnutls_transport_ptr_t h, const void *buf, size_t size);
+static int	get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t * peer);
+static bool load_dh_file(gnutls_dh_params_t dh, char *filename, bool isServerStart);
+static bool load_dh_buffer(gnutls_dh_params_t dh, const char *, size_t, bool isServerStart);
+static int ssl_passwd_cb(void *userdata, int attempt,
+			  const char *token_url,
+			  const char *token_label,
+			  unsigned int flags,
+			  char *pin, size_t pin_max);
+static int	verify_cb(gnutls_session_t ssl);
+static bool initialize_dh(gnutls_dh_params_t * dh_params, bool isServerStart);
+
+static gnutls_certificate_credentials_t tls_credentials = NULL;
+static gnutls_dh_params_t tls_dh_params = NULL;
+static gnutls_priority_t tls_priority = NULL;
+static bool tls_initialized = false;
+static bool ssl_passwd_cb_called = false;
+
+/* ------------------------------------------------------------ */
+/*						 Hardcoded values						*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Hardcoded DH parameters, used in ephemeral DH keying.
+ *	As discussed above, EDH protects the confidentiality of
+ *	sessions even if the static private key is compromised,
+ *	so we are *highly* motivated to ensure that we can use
+ *	EDH even if the DBA has not provided custom DH parameters.
+ *
+ *	We could refuse SSL connections unless a good DH parameter
+ *	file exists, but some clients may quietly renegotiate an
+ *	unsecured connection without fully informing the user.
+ *
+ *	Very uncool. Alternatively, the system could refuse to start
+ *	if a DH parameters is not specified, but this would tend to
+ *	piss off DBAs.
+ *
+ *	Alternatively, the backend could attempt to load these files
+ *	on startup if SSL is enabled - and refuse to start if any
+ *	do not exist - but this would tend to piss off DBAs.
+ *
+ *	If you want to create your own hardcoded DH parameters
+ *	for fun and profit, review "Assigned Number for SKIP
+ *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
+ *	for suggestions.
+ */
+
+static const char file_dh2048[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
+89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
+T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
+zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
+Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
+CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
+-----END DH PARAMETERS-----\n";
+
+
+/* ------------------------------------------------------------ */
+/*						 Public interface						*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Initialize global SSL credentials.
+ *
+ * If isServerStart is true, report any errors as FATAL (so we don't return).
+ * Otherwise, log errors at LOG level and return -1 to indicate trouble,
+ * preserving the old SSL state if any.  Returns 0 if OK.
+ */
+int
+be_tls_init(bool isServerStart)
+{
+	gnutls_certificate_credentials_t credentials = NULL;
+	gnutls_priority_t priority = NULL;
+	gnutls_dh_params_t dh_params = NULL;
+	struct stat buf;
+	int			ret;
+	const char *err_pos;
+
+	/* This stuff need be done only once. */
+	if (!tls_initialized)
+	{
+		gnutls_global_init();
+		tls_initialized = true;
+	}
+
+	ret = gnutls_certificate_allocate_credentials(&credentials);
+	if (ret < 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errmsg("could not create SSL credentials: %s",
+						gnutls_strerror(ret))));
+		goto error;
+	}
+
+	/*
+	 * If reloading, override OpenSSL's default handling of
+	 * passphrase-protected files, because we don't want to prompt for a
+	 * passphrase in an already-running server.  (Not that the default
+	 * handling is very desirable during server start either, but some people
+	 * insist we need to keep it.)
+	 *
+	 * We set the callback globally for compatibility with GnuTLS < 3.1.0.
+	 */
+	if (!isServerStart)
+		gnutls_pkcs11_set_pin_function(ssl_passwd_cb, NULL);
+
+	/*
+	 * Load and verify server's certificate and private key
+	 */
+	if (stat(ssl_key_file, &buf) != 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not access private key file \"%s\": %m",
+						ssl_key_file)));
+		goto error;
+	}
+
+	if (!S_ISREG(buf.st_mode))
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("private key file \"%s\" is not a regular file",
+						ssl_key_file)));
+		goto error;
+	}
+
+	/*
+	 * Refuse to load key files owned by users other than us or root.
+	 *
+	 * XXX surely we can check this on Windows somehow, too.
+	 */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+	if (buf.st_uid != geteuid() && buf.st_uid != 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("private key file \"%s\" must be owned by the database user or root",
+						ssl_key_file)));
+		goto error;
+	}
+#endif
+
+	/*
+	 * Require no public access to key file. If the file is owned by us,
+	 * require mode 0600 or less. If owned by root, require 0640 or less to
+	 * allow read access through our gid, or a supplementary gid that allows
+	 * to read system-wide certificates.
+	 *
+	 * XXX temporarily suppress check when on Windows, because there may not
+	 * be proper support for Unix-y file permissions.  Need to think of a
+	 * reasonable check to apply on Windows.  (See also the data directory
+	 * permission check in postmaster.c)
+	 */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+	if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+		(buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("private key file \"%s\" has group or world access",
+						ssl_key_file),
+				 errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+		goto error;
+	}
+#endif
+
+	/*
+	 * OK, try to load the private key file.
+	 */
+	ssl_passwd_cb_called = false;
+
+	ret = gnutls_certificate_set_x509_key_file(credentials, ssl_cert_file, ssl_key_file, GNUTLS_X509_FMT_PEM);
+	if (ret < 0)
+	{
+		if (ssl_passwd_cb_called)
+			ereport(isServerStart ? FATAL : LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
+							ssl_key_file)));
+		else
+			ereport(isServerStart ? FATAL : LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not load server certificate \"%s\" or key file \"%s\": %s",
+							ssl_cert_file, ssl_key_file, gnutls_strerror(ret))));
+		goto error;
+	}
+
+	/* set up ephemeral DH keys */
+	if (!initialize_dh(&dh_params, isServerStart))
+		goto error;
+
+	gnutls_certificate_set_dh_params(credentials, dh_params);
+
+	/* set up the allowed cipher list */
+	ret = gnutls_priority_init(&priority, gnutls_priority, &err_pos);
+	if (ret < 0)
+	{
+		if (ret == GNUTLS_E_INVALID_REQUEST)
+			ereport(isServerStart ? FATAL : LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not set the cipher list: syntax error at %s", err_pos)));
+		else
+			ereport(isServerStart ? FATAL : LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not set the cipher list: %s", gnutls_strerror(ret))));
+		goto error;
+	}
+
+	/*
+	 * Load CA store, so we can verify client certificates if needed.
+	 */
+	if (ssl_ca_file[0])
+	{
+		ret = gnutls_certificate_set_x509_trust_file(credentials, ssl_ca_file, GNUTLS_X509_FMT_PEM);
+		if (ret < 0)
+		{
+			ereport(isServerStart ? FATAL : LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not load root certificate file \"%s\": %s",
+							ssl_ca_file, gnutls_strerror(ret))));
+			goto error;
+		}
+
+		gnutls_certificate_set_verify_function(credentials, verify_cb);
+	}
+
+	/*----------
+	 * Load the Certificate Revocation List (CRL).
+	 * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+	 *----------
+	 */
+	if (ssl_crl_file[0])
+	{
+		ret = gnutls_certificate_set_x509_crl_file(credentials, ssl_crl_file, GNUTLS_X509_FMT_PEM);
+		if (ret < 0)
+		{
+			ereport(isServerStart ? FATAL : LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+							ssl_crl_file, gnutls_strerror(ret))));
+			goto error;
+		}
+	}
+
+	/*
+	 * Success!  Replace any existing credentials.
+	 */
+	if (tls_credentials)
+		gnutls_certificate_free_credentials(tls_credentials);
+	if (tls_priority)
+		gnutls_priority_deinit(tls_priority);
+	if (tls_dh_params)
+		gnutls_dh_params_deinit(tls_dh_params);
+
+	tls_credentials = credentials;
+	tls_priority = priority;
+	tls_dh_params = dh_params;
+
+	/*
+	 * Set flag to remember whether CA store has been loaded.
+	 */
+	if (ssl_ca_file[0])
+		ssl_loaded_verify_locations = true;
+	else
+		ssl_loaded_verify_locations = false;
+
+	return 0;
+
+error:
+	if (credentials)
+		gnutls_certificate_free_credentials(credentials);
+	if (priority)
+		gnutls_priority_deinit(priority);
+	if (dh_params)
+		gnutls_dh_params_deinit(dh_params);
+	return -1;
+}
+
+/*
+ *	Destroy global SSL credentials, if any.
+ */
+void
+be_tls_destroy(void)
+{
+	if (tls_credentials)
+		gnutls_certificate_free_credentials(tls_credentials);
+	if (tls_priority)
+		gnutls_priority_deinit(tls_priority);
+	if (tls_dh_params)
+		gnutls_dh_params_deinit(tls_dh_params);
+	tls_credentials = NULL;
+	tls_priority = NULL;
+	tls_dh_params = NULL;
+	ssl_loaded_verify_locations = false;
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
+{
+	int			ret;
+
+	Assert(!port->ssl);
+	Assert(!port->peer);
+
+	if (!tls_credentials || !tls_priority)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not initialize SSL connection: SSL context not set up")));
+		return -1;
+	}
+
+	ret = gnutls_init(&port->ssl, GNUTLS_SERVER);
+	if (ret < 0)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not initialize SSL connection: %s",
+						gnutls_strerror(ret))));
+		return -1;
+	}
+
+	gnutls_transport_set_ptr(port->ssl, port);
+	gnutls_transport_set_pull_function(port->ssl, my_sock_read);
+	gnutls_transport_set_push_function(port->ssl, my_sock_write);
+
+	ret = gnutls_priority_set(port->ssl, tls_priority);
+	if (ret < 0)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not initialize SSL connection: %s",
+						gnutls_strerror(ret))));
+		return -1;
+	}
+
+	ret = gnutls_credentials_set(port->ssl, GNUTLS_CRD_CERTIFICATE, tls_credentials);
+	if (ret < 0)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not initialize SSL connection: %s",
+						gnutls_strerror(ret))));
+		return -1;
+	}
+
+	if (ssl_loaded_verify_locations)
+		gnutls_certificate_server_set_request(port->ssl, GNUTLS_CERT_REQUEST);
+
+	port->ssl_in_use = true;
+
+	do
+	{
+		ret = gnutls_handshake(port->ssl);
+	}
+	while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not accept SSL connection: %s",
+						gnutls_strerror(ret))));
+		return -1;
+	}
+
+	/* Get client certificate, if available. */
+	ret = get_peer_certificate(port->ssl, &port->peer);
+	if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not load peer certificates: %s",
+						gnutls_strerror(ret))));
+	}
+
+	/* and extract the Common Name from it. */
+	port->peer_cn = NULL;
+	port->peer_cert_valid = false;
+	if (port->peer != NULL)
+	{
+		size_t		len = 0;
+
+		gnutls_x509_crt_get_dn_by_oid(port->peer,
+									  GNUTLS_OID_X520_COMMON_NAME,
+									  0, 0, NULL, &len);
+
+		if (len > 0)
+		{
+			char	   *peer_cn;
+
+			peer_cn = MemoryContextAlloc(TopMemoryContext, len);
+
+			ret = gnutls_x509_crt_get_dn_by_oid(port->peer,
+												GNUTLS_OID_X520_COMMON_NAME,
+												0, 0, peer_cn, &len);
+
+			if (ret != 0)
+			{
+				/* shouldn't happen */
+				pfree(peer_cn);
+				return -1;
+			}
+
+			/*
+			 * Reject embedded NULLs in certificate common name to prevent
+			 * attacks like CVE-2009-4034.
+			 */
+			if (len != strlen(peer_cn))
+			{
+				ereport(COMMERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("SSL certificate's common name contains embedded null")));
+				pfree(peer_cn);
+				return -1;
+			}
+
+
+			if (ret == 0)
+				port->peer_cn = peer_cn;
+			else
+				pfree(peer_cn);
+
+		}
+
+		port->peer_cert_valid = true;
+	}
+
+	ereport(DEBUG2,
+			(errmsg("SSL connection from \"%s\"",
+					port->peer_cn ? port->peer_cn : "(anonymous)")));
+
+	return 0;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	if (port->ssl)
+	{
+		gnutls_bye(port->ssl, GNUTLS_SHUT_RDWR);
+		gnutls_deinit(port->ssl);
+		port->ssl = NULL;
+		port->ssl_in_use = false;
+	}
+
+	if (port->peer)
+	{
+		gnutls_x509_crt_deinit(port->peer);
+		port->peer = NULL;
+	}
+
+	if (port->peer_cn)
+	{
+		pfree(port->peer_cn);
+		port->peer_cn = NULL;
+	}
+}
+
+/*
+ *	Read data from a secure connection.
+ */
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
+{
+	ssize_t		n;
+
+	n = gnutls_record_recv(port->ssl, ptr, len);
+
+	if (n > 0)
+		return n;
+
+	switch (n)
+	{
+		case 0:
+
+			/*
+			 * the SSL connnection was closed, leave it to the caller to
+			 * ereport it
+			 */
+			errno = ECONNRESET;
+			n = -1;
+			break;
+		case GNUTLS_E_AGAIN:
+		case GNUTLS_E_INTERRUPTED:
+			*waitfor = WL_SOCKET_READABLE;
+			errno = EWOULDBLOCK;
+			n = -1;
+			break;
+		default:
+			ereport(COMMERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("SSL error: %s",
+							gnutls_strerror(n))));
+			errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	return n;
+}
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
+{
+	ssize_t		n;
+
+	n = gnutls_record_send(port->ssl, ptr, len);
+
+	if (n >= 0)
+		return n;
+
+	switch (n)
+	{
+		case GNUTLS_E_AGAIN:
+		case GNUTLS_E_INTERRUPTED:
+			*waitfor = WL_SOCKET_WRITEABLE;
+			errno = EWOULDBLOCK;
+			n = -1;
+			break;
+		default:
+			ereport(COMMERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("SSL error: %s",
+							gnutls_strerror(n))));
+			errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						Internal functions						*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Private substitute transport layer: this does the sending and receiving
+ * using send() and recv() instead. This is so that we can enable and disable
+ * interrupts just while calling recv(). We cannot have interrupts occurring
+ * while the bulk of GnuTLS runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ */
+
+static ssize_t
+my_sock_read(gnutls_transport_ptr_t port, void *buf, size_t size)
+{
+	return secure_raw_read((Port *) port, buf, size);
+}
+
+static ssize_t
+my_sock_write(gnutls_transport_ptr_t port, const void *buf, size_t size)
+{
+	return secure_raw_write((Port *) port, buf, size);
+}
+
+#ifndef HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT
+/*
+ * GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
+ * certificate chains, so we skip doing so in these earlier versions.
+ */
+#define GNUTLS_X509_CRT_LIST_SORT 0
+#endif
+
+/*
+ *	Get peer certificate from a session
+ *
+ *	Returns GNUTLS_E_NO_CERTIFICATE_FOUND when not x509 certifcate was found.
+ */
+static int
+get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t * peer)
+{
+	if (gnutls_certificate_type_get(ssl) == GNUTLS_CRT_X509)
+	{
+		unsigned int n;
+		int			ret;
+		gnutls_datum_t const *raw_certs;
+		gnutls_x509_crt_t *certs;
+
+		raw_certs = gnutls_certificate_get_peers(ssl, &n);
+
+		if (n == 0)
+			return GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+		certs = palloc(n * sizeof(gnutls_x509_crt_t));
+
+		ret = gnutls_x509_crt_list_import(certs, &n, raw_certs,
+										  GNUTLS_X509_FMT_DER,
+										  GNUTLS_X509_CRT_LIST_SORT);
+
+		if (ret >= 1)
+		{
+			unsigned int i;
+
+			for (i = 1; i < ret; i++)
+				gnutls_x509_crt_deinit(certs[i]);
+
+			*peer = certs[0];
+
+			ret = GNUTLS_E_SUCCESS;
+		}
+		else if (ret == 0)
+			ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+		pfree(certs);
+
+		return ret;
+	}
+
+	return GNUTLS_E_NO_CERTIFICATE_FOUND;
+}
+
+#define MAX_DH_FILE_SIZE 10240
+
+/*
+ *	Load precomputed DH parameters.
+ *
+ *	To prevent "downgrade" attacks, we perform a number of checks
+ *	to verify that the DBA-generated DH parameters file contains
+ *	what we expect it to contain.
+ */
+static bool
+load_dh_file(gnutls_dh_params_t dh_params, char *filename, bool isServerStart)
+{
+	FILE	   *fp;
+	char		buffer[MAX_DH_FILE_SIZE];
+	gnutls_datum_t datum = {(unsigned char *) buffer};
+	int			ret;
+
+	/* attempt to open file.  It's not an error if it doesn't exist. */
+	if ((fp = AllocateFile(filename, "r")) == NULL)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not open DH parameters file \"%s\": %m",
+						filename)));
+		return false;
+	}
+
+	datum.size = fread(buffer, sizeof(buffer[0]), sizeof(buffer), fp);
+
+	FreeFile(fp);
+
+	if (datum.size < 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not load DH parameters file: %s",
+						gnutls_strerror(ret))));
+		return false;
+	}
+
+	ret = gnutls_dh_params_import_pkcs3(dh_params, &datum, GNUTLS_X509_FMT_PEM);
+
+	if (ret < 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load DH parameters file: %s",
+						gnutls_strerror(ret))));
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ *	Load hardcoded DH parameters.
+ *
+ *	To prevent problems if the DH parameters files don't even
+ *	exist, we can load DH parameters hardcoded into this file.
+ */
+static bool
+load_dh_buffer(gnutls_dh_params_t dh_params, const char *buffer, size_t len, bool isServerStart)
+{
+	gnutls_datum_t datum = {(unsigned char *) buffer, len};
+	int			ret;
+
+	ret = gnutls_dh_params_import_pkcs3(dh_params, &datum, GNUTLS_X509_FMT_PEM);
+
+	if (ret < 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errmsg_internal("DH load buffer: %s", gnutls_strerror(ret))));
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ *	Passphrase collection callback
+ *
+ * If GnuTLS is told to use a passphrase-protected server key, by default
+ * it will issue a prompt on /dev/tty and try to read a key from there.
+ * That's no good during a postmaster SIGHUP cycle, not to mention SSL context
+ * reload in an EXEC_BACKEND postmaster child.  So override it with this dummy
+ * function that just returns an error, guaranteeing failure.
+ */
+static int
+ssl_passwd_cb(void *userdata, int attempt,
+			  const char *token_url,
+			  const char *token_label,
+			  unsigned int flags,
+			  char *pin, size_t pin_max)
+{
+	/* Set flag to change the error message we'll report */
+	ssl_passwd_cb_called = true;
+	return -1;
+}
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback is where we verify the identity of the client.
+ */
+static int
+verify_cb(gnutls_session_t ssl)
+{
+	unsigned int status;
+	int			ret;
+
+	ret = gnutls_certificate_verify_peers2(ssl, &status);
+
+	if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
+		return 0;
+	else if (ret < 0)
+		return ret;
+
+	return status;
+}
+
+/*
+ * Set DH parameters for generating ephemeral DH keys.  The
+ * DH parameters can take a long time to compute, so they must be
+ * precomputed.
+ *
+ * Since few sites will bother to create a parameter file, we also
+ * also provide a fallback to the parameters provided by the
+ * OpenSSL project.
+ *
+ * These values can be static (once loaded or computed) since the
+ * OpenSSL library can efficiently generate random keys from the
+ * information provided.
+ */
+static bool
+initialize_dh(gnutls_dh_params_t *dh_params, bool isServerStart)
+{
+	bool		loaded = false;
+	int			ret;
+
+	ret = gnutls_dh_params_init(dh_params);
+	if (ret < 0)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errmsg_internal("DH init error: %s",
+								 gnutls_strerror(ret))));
+		return false;
+	}
+
+	if (ssl_dh_params_file[0])
+		loaded = load_dh_file(*dh_params, ssl_dh_params_file, isServerStart);
+	if (!loaded)
+		loaded = load_dh_buffer(*dh_params, file_dh2048, sizeof file_dh2048, isServerStart);
+	if (!loaded)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 (errmsg("DH: could not load DH parameters"))));
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * Return information about the SSL connection
+ */
+int
+be_tls_get_cipher_bits(Port *port)
+{
+	if (port->ssl)
+		return gnutls_cipher_get_key_size(gnutls_cipher_get(port->ssl)) * 8;
+	else
+		return 0;
+}
+
+bool
+be_tls_get_compression(Port *port)
+{
+	if (port->ssl)
+	{
+		gnutls_compression_method_t comp = gnutls_compression_get(port->ssl);
+
+		return comp != GNUTLS_COMP_UNKNOWN && comp != GNUTLS_COMP_NULL;
+	}
+	else
+		return false;
+}
+
+void
+be_tls_get_version(Port *port, char *ptr, size_t len)
+{
+	if (port->ssl)
+		strlcpy(ptr, gnutls_protocol_get_name(gnutls_protocol_get_version(port->ssl)), len);
+	else
+		ptr[0] = '\0';
+}
+
+void
+be_tls_get_cipher(Port *port, char *ptr, size_t len)
+{
+	if (port->ssl)
+		strlcpy(ptr, gnutls_cipher_get_name(gnutls_cipher_get(port->ssl)), len);
+	else
+		ptr[0] = '\0';
+}
+
+void
+be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+{
+	if (port->peer)
+	{
+		int			ret;
+
+		ret = gnutls_x509_crt_get_dn_by_oid(port->peer,
+											GNUTLS_OID_X520_COMMON_NAME,
+											0, 0, ptr, &len);
+
+		if (ret != 0)
+			ptr[0] = '\0';
+	}
+	else
+		ptr[0] = '\0';
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 53fefd1b29..303fa783d3 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -59,6 +59,9 @@ char	   *SSLECDHCurve;
 /* GUC variable: if false, prefer client ciphers */
 bool		SSLPreferServerCiphers;
 
+/* GUC variable controlling GnuTLS priorities */
+char	   *gnutls_priority;
+
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
 /* ------------------------------------------------------------ */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bc9f09a086..df15d111c2 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3607,6 +3607,21 @@ static struct config_string ConfigureNamesString[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"gnutls_priority", PGC_SIGHUP, CONN_AUTH_SECURITY,
+			gettext_noop("Sets the list of allowed GnuTLS algorithms."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&gnutls_priority,
+#ifdef USE_SSL
+		"NORMAL:%SERVER_PRECEDENCE",
+#else
+		"none",
+#endif
+		NULL, NULL, NULL
+	},
+
 	{
 		{"ssl_dh_params_file", PGC_SIGHUP, CONN_AUTH_SECURITY,
 			gettext_noop("Location of the SSL DH parameters file."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 53aa006df5..58ce2ab849 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -77,14 +77,22 @@
 
 #authentication_timeout = 1min		# 1s-600s
 #ssl = off
-#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
-#ssl_prefer_server_ciphers = on
-#ssl_ecdh_curve = 'prime256v1'
 #ssl_dh_params_file = ''
 #ssl_cert_file = 'server.crt'
 #ssl_key_file = 'server.key'
 #ssl_ca_file = ''
 #ssl_crl_file = ''
+
+# Parameters for OpenSSL.  Leave these commented out if not using OpenSSL.
+
+#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
+#ssl_prefer_server_ciphers = on
+#ssl_ecdh_curve = 'prime256v1'
+
+# Parameters for GnuTLS.  Leave these commented out if not using GnuTLS.
+
+#gnutls_priority = 'NORMAL:%SERVER_PRECEDENCE'
+
 #password_encryption = md5		# md5 or scram-sha-256
 #db_user_namespace = off
 #row_security = on
diff --git a/src/common/Makefile b/src/common/Makefile
index 80e78d72fe..3e26161f87 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -47,6 +47,8 @@ OBJS_COMMON = base64.o config_info.o controldata_utils.o exec.o ip.o \
 
 ifeq ($(with_openssl),yes)
 OBJS_COMMON += sha2_openssl.o
+else ifeq ($(with_gnutls),yes)
+OBJS_COMMON += sha2_gnutls.o
 else
 OBJS_COMMON += sha2.o
 endif
diff --git a/src/common/sha2_gnutls.c b/src/common/sha2_gnutls.c
new file mode 100644
index 0000000000..279b5370fa
--- /dev/null
+++ b/src/common/sha2_gnutls.c
@@ -0,0 +1,99 @@
+/*-------------------------------------------------------------------------
+ *
+ * sha2_gnutlsl.c
+ *	  Set of wrapper routines on top of GnuTLS to support SHA-224
+ *	  SHA-256, SHA-384 and SHA-512 functions.
+ *
+ * This should only be used if code is compiled with GnuTLS support.
+ *
+ * Portions Copyright (c) 2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		  src/common/sha2_gnutls.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/sha2.h"
+
+/* Interface routines for SHA-256 */
+void
+pg_sha256_init(pg_sha256_ctx *ctx)
+{
+	gnutls_hash_init(ctx, GNUTLS_DIG_SHA256);
+}
+
+void
+pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len)
+{
+	gnutls_hash(*ctx, data, len);
+}
+
+void
+pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest)
+{
+	gnutls_hash_deinit(*ctx, dest);
+}
+
+/* Interface routines for SHA-512 */
+void
+pg_sha512_init(pg_sha512_ctx *ctx)
+{
+	gnutls_hash_init(ctx, GNUTLS_DIG_SHA512);
+}
+
+void
+pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len)
+{
+	gnutls_hash(*ctx, data, len);
+}
+
+void
+pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest)
+{
+	gnutls_hash_deinit(*ctx, dest);
+}
+
+/* Interface routines for SHA-384 */
+void
+pg_sha384_init(pg_sha384_ctx *ctx)
+{
+	gnutls_hash_init(ctx, GNUTLS_DIG_SHA384);
+}
+
+void
+pg_sha384_update(pg_sha384_ctx *ctx, const uint8 *data, size_t len)
+{
+	gnutls_hash(*ctx, data, len);
+}
+
+void
+pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest)
+{
+	gnutls_hash_deinit(*ctx, dest);
+}
+
+/* Interface routines for SHA-224 */
+void
+pg_sha224_init(pg_sha224_ctx *ctx)
+{
+	gnutls_hash_init(ctx, GNUTLS_DIG_SHA224);
+}
+
+void
+pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *data, size_t len)
+{
+	gnutls_hash(*ctx, data, len);
+}
+
+void
+pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest)
+{
+	gnutls_hash_deinit(*ctx, dest);
+}
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index a31b3979d8..0c311dea2f 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -50,8 +50,11 @@
 #ifndef _PG_SHA2_H_
 #define _PG_SHA2_H_
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/sha.h>
+#elif defined(USE_GNUTLS)
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
 #endif
 
 /*** SHA224/256/384/512 Various Length Definitions ***********************/
@@ -69,11 +72,16 @@
 #define PG_SHA512_DIGEST_STRING_LENGTH	(PG_SHA512_DIGEST_LENGTH * 2 + 1)
 
 /* Context Structures for SHA-1/224/256/384/512 */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 typedef SHA256_CTX pg_sha256_ctx;
 typedef SHA512_CTX pg_sha512_ctx;
 typedef SHA256_CTX pg_sha224_ctx;
 typedef SHA512_CTX pg_sha384_ctx;
+#elif defined(USE_GNUTLS)
+typedef gnutls_hash_hd_t pg_sha256_ctx;
+typedef gnutls_hash_hd_t pg_sha512_ctx;
+typedef gnutls_hash_hd_t pg_sha224_ctx;
+typedef gnutls_hash_hd_t pg_sha384_ctx;
 #else
 typedef struct pg_sha256_ctx
 {
@@ -89,7 +97,7 @@ typedef struct pg_sha512_ctx
 } pg_sha512_ctx;
 typedef struct pg_sha256_ctx pg_sha224_ctx;
 typedef struct pg_sha512_ctx pg_sha384_ctx;
-#endif							/* USE_SSL */
+#endif
 
 /* Interface routines for SHA224/256/384/512 */
 extern void pg_sha224_init(pg_sha224_ctx *ctx);
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 7bde744d51..6f487a7daa 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -22,6 +22,8 @@
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#elif defined(USE_GNUTLS)
+#include <gnutls/gnutls.h>
 #endif
 #ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
@@ -183,12 +185,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 specific 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 defined(USE_GNUTLS)
+	gnutls_session_t	ssl;
+	gnutls_x509_crt_t	peer;
 #endif
 } Port;
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index fd2dd5853c..3a8552f124 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -99,5 +99,6 @@ extern WaitEventSet *FeBeWaitSet;
 extern char *SSLCipherSuites;
 extern char *SSLECDHCurve;
 extern bool SSLPreferServerCiphers;
+extern char *gnutls_priority;
 
 #endif							/* LIBPQ_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 579d195663..3b01116f84 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -131,6 +131,10 @@
    don't. */
 #undef HAVE_DECL_F_FULLFSYNC
 
+/* Define to 1 if you have the declaration of `GNUTLS_X509_CRT_LIST_SORT', and
+   to 0 if you don't. */
+#undef HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT
+
 /* Define to 1 if you have the declaration of `posix_fadvise', and to 0 if you
    don't. */
 #undef HAVE_DECL_POSIX_FADVISE
@@ -815,6 +819,12 @@
    (--enable-float8-byval) */
 #undef USE_FLOAT8_BYVAL
 
+/* Define to build with GnuTLS support. (--with-gnutls) */
+#undef USE_GNUTLS
+
+/* Define to use GnuTLS for random number generation */
+#undef USE_GNUTLS_RANDOM
+
 /* Define to build with ICU support. (--with-icu) */
 #undef USE_ICU
 
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index f3b35297d1..7ecb9183e2 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -169,7 +169,7 @@
  * implementation.  (Currently, only OpenSSL is supported, but we might add
  * more implementations in the future.)
  */
-#ifdef USE_OPENSSL
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
 #define USE_SSL
 #endif
 
diff --git a/src/interfaces/libpq/.gitignore b/src/interfaces/libpq/.gitignore
index 6c02dc7055..1d1a8db482 100644
--- a/src/interfaces/libpq/.gitignore
+++ b/src/interfaces/libpq/.gitignore
@@ -27,6 +27,7 @@
 /scram-common.c
 /sha2.c
 /sha2_openssl.c
+/sha2_gnutls.c
 /saslprep.c
 /unicode_norm.c
 /encnames.c
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 87f22d242f..014b4fc107 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -53,6 +53,8 @@ OBJS += base64.o ip.o md5.o scram-common.o saslprep.o unicode_norm.o
 
 ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o sha2_openssl.o
+else ifeq ($(with_gnutls),yes)
+OBJS += fe-secure-gnutls.o sha2_gnutls.o
 else
 OBJS += sha2.o
 endif
@@ -78,12 +80,12 @@ endif
 # shared library link.  (The order in which you list them here doesn't
 # matter.)
 ifneq ($(PORTNAME), win32)
-SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lgnutls -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
 else
-SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lgnutls -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
 endif
 ifeq ($(PORTNAME), win32)
-SHLIB_LINK += -lshell32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))
+SHLIB_LINK += -lshell32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lgnutls -lcomerr32 -lkrb5_32, $(LIBS))
 endif
 
 SHLIB_EXPORTS = exports.txt
@@ -106,7 +108,7 @@ backend_src = $(top_srcdir)/src/backend
 chklocale.c crypt.c erand48.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pg_strong_random.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c thread.c win32error.c win32setlocale.c: % : $(top_srcdir)/src/port/%
 	rm -f $@ && $(LN_S) $< .
 
-ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c saslprep.c unicode_norm.c: % : $(top_srcdir)/src/common/%
+ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c sha2_gnutls.c saslprep.c unicode_norm.c: % : $(top_srcdir)/src/common/%
 	rm -f $@ && $(LN_S) $< .
 
 encnames.c wchar.c: % : $(backend_src)/utils/mb/%
@@ -156,7 +158,7 @@ clean distclean: clean-lib
 	rm -f pg_config_paths.h
 # Remove files we (may have) symlinked in from src/port and other places
 	rm -f chklocale.c crypt.c erand48.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pg_strong_random.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c thread.c win32error.c win32setlocale.c
-	rm -f ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c saslprep.c unicode_norm.c
+	rm -f ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c sha2_gnutls.c saslprep.c unicode_norm.c
 	rm -f encnames.c wchar.c
 
 maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/libpq/fe-secure-gnutls.c b/src/interfaces/libpq/fe-secure-gnutls.c
new file mode 100644
index 0000000000..8a890a1888
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gnutls.c
@@ -0,0 +1,1027 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-gnutls.c
+ *	  OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-gnutls.c
+ *
+ * NOTES
+ *
+ *	  We don't provide informational callbacks here (like
+ *	  info_cb() in be-secure.c), since there's no good mechanism to
+ *	  display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+#include "libpq-int.h"
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+static bool verify_peer_name_matches_certificate(PGconn *);
+static int verify_peer_name_matches_certificate_name(PGconn *conn,
+										  size_t namelen,
+										  char *namedata,
+										  char **store_name);
+static int	initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+
+static ssize_t my_sock_read(gnutls_transport_ptr_t h, void *buf, size_t size);
+static ssize_t my_sock_write(gnutls_transport_ptr_t h, const void *buf, size_t size);
+static int	get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t *peer);
+static int	verify_cb(gnutls_session_t ssl);
+
+static bool pq_init_ssl_lib = true;
+
+static bool ssl_lib_initialized = false;
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifndef WIN32
+static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t ssl_config_mutex = NULL;
+static long win32_ssl_create_mutex = 0;
+#endif
+#endif							/* ENABLE_THREAD_SAFETY */
+
+
+/* ------------------------------------------------------------ */
+/*			 Procedures common to all secure sessions			*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Exported function to allow application to tell us it's already
+ *	initialized GnuTLS.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+	pq_init_ssl_lib = do_ssl;
+}
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	/* First time through? */
+	if (conn->ssl == NULL)
+	{
+		/*
+		 * Create a connection-specific SSL object, and load client
+		 * certificate, private key, and trusted CA certs.
+		 */
+		if (initialize_SSL(conn) != 0)
+		{
+			/* initialize_SSL already put a message in conn->errorMessage */
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/* Begin or continue the actual handshake */
+	return open_client_SSL(conn);
+}
+
+/*
+ *	Is there unread data waiting in the SSL read buffer?
+ */
+bool
+pgtls_read_pending(PGconn *conn)
+{
+	return gnutls_record_check_pending(conn->ssl);
+}
+
+/*
+ *	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 determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno;
+	char		sebuf[256];
+
+	n = gnutls_record_recv(conn->ssl, ptr, len);
+
+	if (n > 0)
+	{
+		SOCK_ERRNO_SET(0);
+		return n;
+	}
+
+	switch (n)
+	{
+		case 0:
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		case GNUTLS_E_REHANDSHAKE:
+			/* Ignore re-handsake requests and have the caller retry */
+		case GNUTLS_E_INTERRUPTED:
+			result_errno = EINTR;
+			n = -1;
+			break;
+		case GNUTLS_E_AGAIN:
+			result_errno = EAGAIN;
+			n = -1;
+			break;
+		case GNUTLS_E_PREMATURE_TERMINATION:
+		case GNUTLS_E_PUSH_ERROR:
+			result_errno = SOCK_ERRNO;
+			n = -1;
+			if (result_errno == EPIPE || result_errno == ECONNRESET)
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext(
+												"server closed the connection unexpectedly\n"
+												"\tThis probably means the server terminated abnormally\n"
+												"\tbefore or while processing the request.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("SSL SYSCALL error: %s\n"),
+								  SOCK_STRERROR(result_errno,
+												sebuf, sizeof(sebuf)));
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL error: %s\n"),
+							  gnutls_strerror(n));
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/*
+ *	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 determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno;
+	char		sebuf[256];
+
+	n = gnutls_record_send(conn->ssl, ptr, len);
+
+	if (n >= 0)
+	{
+		SOCK_ERRNO_SET(0);
+		return n;
+	}
+
+	switch (n)
+	{
+		case GNUTLS_E_INTERRUPTED:
+			result_errno = EINTR;
+			n = -1;
+			break;
+		case GNUTLS_E_AGAIN:
+			result_errno = EAGAIN;
+			n = -1;
+			break;
+		case GNUTLS_E_PREMATURE_TERMINATION:
+		case GNUTLS_E_PUSH_ERROR:
+			result_errno = SOCK_ERRNO;
+			n = -1;
+			if (result_errno == EPIPE || result_errno == ECONNRESET)
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext(
+												"server closed the connection unexpectedly\n"
+												"\tThis probably means the server terminated abnormally\n"
+												"\tbefore or while processing the request.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("SSL SYSCALL error: %s\n"),
+								  SOCK_STRERROR(result_errno,
+												sebuf, sizeof(sebuf)));
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL error: %s\n"),
+							  gnutls_strerror(n));
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						GnuTLS specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ *	1. We only match the '*' character as wildcard
+ *	2. We match only wildcards at the start of the string
+ *	3. The '*' character does *not* match '.', meaning that we match only
+ *	   a single pathname component.
+ *	4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+	int			lenpat = strlen(pattern);
+	int			lenstr = strlen(string);
+
+	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+	if (lenpat < 3 ||
+		pattern[0] != '*' ||
+		pattern[1] != '.')
+		return 0;
+
+	if (lenpat > lenstr)
+		/* If pattern is longer than the string, we can never match */
+		return 0;
+
+	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
+
+		/*
+		 * If string does not end in pattern (minus the wildcard), we don't
+		 * match
+		 */
+		return 0;
+
+	if (strchr(string, '.') < string + lenstr - lenpat)
+
+		/*
+		 * If there is a dot left of where the pattern started to match, we
+		 * don't match (rule 3)
+		 */
+		return 0;
+
+	/* String ended with pattern, and didn't have a dot before, so we match */
+	return 1;
+}
+
+/*
+ * Check if a name from a server's certificate matches the peer's hostname.
+ *
+ * Returns 1 if the name matches, and 0 if it does not. On error, returns
+ * -1, and sets the libpq error message.
+ *
+ * The name extracted from the certificate is returned in *store_name. The
+ * caller is responsible for freeing it.
+ */
+static int
+verify_peer_name_matches_certificate_name(PGconn *conn, size_t len,
+										  char *namedata, char **store_name)
+{
+	char	   *name;
+	int			result;
+	char	   *host = PQhost(conn);
+
+	name = malloc(len + 1);
+	if (name == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory\n"));
+		return -1;
+	}
+	memcpy(name, namedata, len);
+	name[len] = '\0';
+
+	/*
+	 * Reject embedded NULLs in certificate common or alternative name to
+	 * prevent attacks like CVE-2009-4034.
+	 */
+	if (len != strlen(name))
+	{
+		free(name);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("SSL certificate's name contains embedded null\n"));
+		return -1;
+	}
+
+	if (pg_strcasecmp(name, host) == 0)
+	{
+		/* Exact name match */
+		result = 1;
+	}
+	else if (wildcard_certificate_match(name, host))
+	{
+		/* Matched wildcard name */
+		result = 1;
+	}
+	else
+	{
+		result = 0;
+	}
+
+	*store_name = name;
+	return result;
+}
+
+#define MAX_CN 256
+
+/*
+ *	Verify that the server certificate matches the hostname we connected to.
+ *
+ * The certificate's Common Name and Subject Alternative Names are considered.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
+	char	   *host = PQhost(conn);
+	char		namedata[MAX_CN];
+	size_t		namelen;
+	int			i;
+	int			ret;
+	int			rc;
+	char	   *first_name = NULL;
+	int			names_examined = 0;
+	bool		found_match = false;
+	bool		got_error = false;
+
+	/*
+	 * If told not to verify the peer name, don't do it. Return true
+	 * indicating that the verification was successful.
+	 */
+	if (strcmp(conn->sslmode, "verify-full") != 0)
+		return true;
+
+	/* Check that we have a hostname to compare with. */
+	if (!(host && host[0] != '\0'))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
+		return false;
+	}
+
+	/*
+	 * First, get the Subject Alternative Names (SANs) from the certificate,
+	 * and compare them against the originally given hostname.
+	 */
+	for (i = 0;; i++)
+	{
+		namelen = sizeof(namedata);
+		ret = gnutls_x509_crt_get_subject_alt_name(conn->peer, i,
+												   namedata,
+												   &namelen,
+												   NULL);
+
+		if (ret < 0)
+			break;
+
+		/*
+		 * Count IP addresses too even if we do not match them to make sure
+		 * SAN takes precedence over the Common Name.
+		 */
+		names_examined++;
+
+		if (ret == GNUTLS_SAN_DNSNAME)
+		{
+			char	   *alt_name = NULL;
+
+			rc = verify_peer_name_matches_certificate_name(conn, namelen, namedata, &alt_name);
+
+			if (rc == -1)
+				got_error = true;
+			if (rc == 1)
+				found_match = true;
+
+			if (alt_name)
+			{
+				if (!first_name)
+					first_name = alt_name;
+				else
+					free(alt_name);
+			}
+		}
+
+		if (found_match || got_error)
+			break;
+	}
+
+	/*
+	 * If there is no subjectAltName extension of type dNSName, check the
+	 * Common Name.
+	 *
+	 * (Per RFC 2818 and RFC 6125, if the subjectAltName extension of type
+	 * dNSName is present, the CN must be ignored.)
+	 */
+	if (names_examined == 0)
+	{
+		namelen = sizeof(namedata);
+		ret = gnutls_x509_crt_get_dn_by_oid(conn->peer, GNUTLS_OID_X520_COMMON_NAME, 0, 0, namedata, &namelen);
+
+		if (ret >= 0)
+		{
+			rc = verify_peer_name_matches_certificate_name(conn, namelen, namedata, &first_name);
+
+			if (rc == -1)
+				got_error = true;
+			if (rc == 1)
+				found_match = true;
+		}
+	}
+
+	if (!found_match && !got_error)
+	{
+		/*
+		 * No match. Include the name from the server certificate in the error
+		 * message, to aid debugging broken configurations. If there are
+		 * multiple names, only print the first one to avoid an overly long
+		 * error message.
+		 */
+		if (names_examined > 1)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"\n",
+											 "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"\n",
+											 names_examined - 1),
+							  first_name, names_examined - 1, host);
+		}
+		else if (names_examined == 1)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
+							  first_name, host);
+		}
+		else
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not get server's host name from server certificate\n"));
+		}
+	}
+
+	/* clean up */
+	if (first_name)
+		free(first_name);
+
+	return found_match && !got_error;
+}
+
+/*
+ * Initialize SSL library.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+	/* Also see similar code in fe-connect.c, default_threadlock() */
+	if (ssl_config_mutex == NULL)
+	{
+		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
+			 /* loop, another thread own the lock */ ;
+		if (ssl_config_mutex == NULL)
+		{
+			if (pthread_mutex_init(&ssl_config_mutex, NULL))
+				return -1;
+		}
+		InterlockedExchange(&win32_ssl_create_mutex, 0);
+	}
+#endif
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return -1;
+#endif							/* ENABLE_THREAD_SAFETY */
+
+	if (!ssl_lib_initialized)
+	{
+		if (pq_init_ssl_lib)
+		{
+			gnutls_global_init();
+		}
+		ssl_lib_initialized = true;
+	}
+
+#ifdef ENABLE_THREAD_SAFETY
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	return 0;
+}
+
+/*
+ *	Create per-connection SSL object, and load the client certificate,
+ *	private key, and trusted CA certs.
+ *
+ *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+	gnutls_certificate_credentials_t creds;
+	int			ret;
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		keybuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+
+	/*
+	 * 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) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else						/* won't need it */
+		have_homedir = false;
+
+	ret = gnutls_certificate_allocate_credentials(&creds);
+	if (ret < 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not create SSL credentials: %s\n"),
+						  gnutls_strerror(ret));
+		return -1;
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	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' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		ret = gnutls_certificate_set_x509_trust_file(creds, fnbuf, GNUTLS_X509_FMT_PEM);
+		if (ret < 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+							  fnbuf, gnutls_strerror(ret));
+			gnutls_certificate_free_credentials(creds);
+			return -1;
+		}
+
+		gnutls_certificate_set_verify_function(creds, verify_cb);
+
+		if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+			strlcpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0' && stat(fnbuf, &buf) == 0)
+		{
+			ret = gnutls_certificate_set_x509_crl_file(creds, fnbuf, GNUTLS_X509_FMT_PEM);
+			if (ret < 0)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not read crl file \"%s\": %s\n"),
+								  fnbuf, gnutls_strerror(ret));
+				gnutls_certificate_free_credentials(creds);
+				return -1;
+			}
+		}
+	}
+	else
+	{
+		/*
+		 * 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);
+			gnutls_certificate_free_credentials(creds);
+			return -1;
+		}
+	}
+
+	/* Read the client certificate file */
+	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')
+	{
+		/* no home directory, proceed without a client cert */
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			gnutls_certificate_free_credentials(creds);
+			return -1;
+		}
+	}
+	else
+	{
+		if (conn->sslkey && strlen(conn->sslkey) > 0)
+			strlcpy(keybuf, conn->sslkey, sizeof(keybuf));
+		else if (have_homedir)
+			snprintf(keybuf, sizeof(keybuf), "%s/%s", homedir, USER_KEY_FILE);
+		else
+			keybuf[0] = '\0';
+
+		if (keybuf[0] != '\0')
+		{
+			if (stat(keybuf, &buf) != 0)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+								  keybuf);
+				return -1;
+			}
+#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"),
+								  keybuf);
+				return -1;
+			}
+#endif
+		}
+
+		ret = gnutls_certificate_set_x509_key_file(creds, fnbuf, keybuf, GNUTLS_X509_FMT_PEM);
+		if (ret < 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not read certificate and key files \"%s\" \"%s\": %s\n"),
+							  fnbuf, keybuf, gnutls_strerror(ret));
+			gnutls_certificate_free_credentials(creds);
+			return -1;
+		}
+	}
+
+	ret = gnutls_init(&conn->ssl, GNUTLS_CLIENT);
+	if (ret < 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not establish SSL connection: %s\n"),
+						  gnutls_strerror(ret));
+		gnutls_certificate_free_credentials(creds);
+		return -1;
+	}
+
+	gnutls_priority_set_direct(conn->ssl, "NORMAL", NULL);
+	if (ret < 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not establish SSL connection: %s\n"),
+						  gnutls_strerror(ret));
+		gnutls_certificate_free_credentials(creds);
+		return -1;
+	}
+
+	ret = gnutls_credentials_set(conn->ssl, GNUTLS_CRD_CERTIFICATE, creds);
+	if (ret < 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not establish SSL connection: %s\n"),
+						  gnutls_strerror(ret));
+		gnutls_deinit(conn->ssl);
+		gnutls_certificate_free_credentials(creds);
+		return -1;
+	}
+
+	gnutls_transport_set_ptr(conn->ssl, conn);
+	gnutls_transport_set_pull_function(conn->ssl, my_sock_read);
+	gnutls_transport_set_push_function(conn->ssl, my_sock_write);
+
+	conn->ssl_in_use = true;
+
+	return 0;
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+static PostgresPollingStatusType
+open_client_SSL(PGconn *conn)
+{
+	int			ret;
+
+	do
+	{
+		ret = gnutls_handshake(conn->ssl);
+	}
+	while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+	if (ret < 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("SSL error: %s\n"),
+						  gnutls_strerror(ret));
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	/*
+	 * We already checked the server certificate in gnutls_handshake() using
+	 * verify_cb(), if root.crt exists.
+	 */
+
+	/* get server certificate */
+	ret = get_peer_certificate(conn->ssl, &conn->peer);
+	if (conn->peer == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("certificate could not be obtained: %s\n"),
+						  gnutls_strerror(ret));
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	if (!verify_peer_name_matches_certificate(conn))
+	{
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	/* SSL handshake is complete */
+	return PGRES_POLLING_OK;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	if (conn->ssl)
+	{
+		gnutls_bye(conn->ssl, GNUTLS_SHUT_RDWR);
+		gnutls_deinit(conn->ssl);
+		conn->ssl = NULL;
+		conn->ssl_in_use = false;
+	}
+
+	if (conn->peer)
+	{
+		gnutls_x509_crt_deinit(conn->peer);
+		conn->peer = NULL;
+	}
+}
+
+/* ------------------------------------------------------------ */
+/*					SSL information functions					*/
+/* ------------------------------------------------------------ */
+
+int
+PQsslInUse(PGconn *conn)
+{
+	if (!conn)
+		return 0;
+	return conn->ssl_in_use;
+}
+
+/*
+ *	Return pointer to OpenSSL object, which is none for GnuTLS.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	return NULL;
+}
+
+void *
+PQsslStruct(PGconn *conn, const char *struct_name)
+{
+	if (!conn)
+		return NULL;
+	if (strcmp(struct_name, "GnuTLS") == 0)
+		return conn->ssl;
+	return NULL;
+}
+
+const char *const *
+PQsslAttributeNames(PGconn *conn)
+{
+	static const char *const result[] = {
+		"library",
+		"key_bits",
+		"cipher",
+		"compression",
+		"protocol",
+		NULL
+	};
+
+	return result;
+}
+
+const char *
+PQsslAttribute(PGconn *conn, const char *attribute_name)
+{
+	if (!conn)
+		return NULL;
+	if (conn->ssl == NULL)
+		return NULL;
+
+	if (strcmp(attribute_name, "library") == 0)
+		return "GnuTLS";
+
+	if (strcmp(attribute_name, "key_bits") == 0)
+	{
+		static char sslbits_str[10];
+		int			sslbytes;
+
+		sslbytes = gnutls_cipher_get_key_size(gnutls_cipher_get(conn->ssl));
+
+		if (sslbytes == 0)
+			return NULL;
+
+		snprintf(sslbits_str, sizeof(sslbits_str), "%d", sslbytes * 8);
+		return sslbits_str;
+	}
+
+	if (strcmp(attribute_name, "cipher") == 0)
+		return gnutls_cipher_get_name(gnutls_cipher_get(conn->ssl));
+
+	if (strcmp(attribute_name, "compression") == 0)
+	{
+		gnutls_compression_method_t comp = gnutls_compression_get(conn->ssl);
+
+		if (comp == GNUTLS_COMP_NULL || comp == GNUTLS_COMP_UNKNOWN)
+			return "off";
+		else
+			return "on";
+	}
+
+	if (strcmp(attribute_name, "protocol") == 0)
+		return gnutls_protocol_get_name(gnutls_protocol_get_version(conn->ssl));
+
+	return NULL;				/* unknown attribute */
+}
+
+/*
+ * Private substitute transport layer: this does the sending and receiving using
+ * pqsecure_raw_write() and pqsecure_raw_read() instead, to allow those
+ * functions to disable SIGPIPE and give better error messages on I/O errors.
+ */
+
+static ssize_t
+my_sock_read(gnutls_transport_ptr_t conn, void *buf, size_t size)
+{
+	return pqsecure_raw_read((PGconn *) conn, buf, size);
+}
+
+static ssize_t
+my_sock_write(gnutls_transport_ptr_t conn, const void *buf, size_t size)
+{
+	return pqsecure_raw_write((PGconn *) conn, buf, size);
+}
+
+#ifndef HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT
+/*
+ * GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
+ * certificate chains, so we skip doing so in these earlier versions.
+ */
+#define GNUTLS_X509_CRT_LIST_SORT 0
+#endif
+
+/*
+ *	Get peer certificate from a session
+ *
+ *	Returns GNUTLS_E_NO_CERTIFICATE_FOUND when not x509 certifcate was found.
+ */
+static int
+get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t *peer)
+{
+	if (gnutls_certificate_type_get(ssl) == GNUTLS_CRT_X509)
+	{
+		unsigned int n;
+		int			ret;
+		gnutls_datum_t const *raw_certs;
+		gnutls_x509_crt_t *certs;
+
+		raw_certs = gnutls_certificate_get_peers(ssl, &n);
+
+		if (n == 0)
+			return GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+		certs = palloc(n * sizeof(gnutls_x509_crt_t));
+
+		ret = gnutls_x509_crt_list_import(certs, &n, raw_certs,
+										  GNUTLS_X509_FMT_DER,
+										  GNUTLS_X509_CRT_LIST_SORT);
+
+		if (ret >= 1)
+		{
+			unsigned int i;
+
+			for (i = 1; i < ret; i++)
+				gnutls_x509_crt_deinit(certs[i]);
+
+			*peer = certs[0];
+
+			ret = GNUTLS_E_SUCCESS;
+		}
+		else if (ret == 0)
+			ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+		pfree(certs);
+
+		return ret;
+	}
+
+	return GNUTLS_E_NO_CERTIFICATE_FOUND;
+}
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback is where we verify the identity of the server.
+ */
+static int
+verify_cb(gnutls_session_t ssl)
+{
+	unsigned int status;
+	int			ret;
+
+	ret = gnutls_certificate_verify_peers2(ssl, &status);
+	if (ret < 0)
+		return ret;
+
+	return status;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 42913604e3..af9ca66214 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -78,7 +78,9 @@ typedef struct
 #ifndef OPENSSL_NO_ENGINE
 #define USE_SSL_ENGINE
 #endif
-#endif							/* USE_OPENSSL */
+#elif defined(USE_GNUTLS)
+#include <gnutls/gnutls.h>
+#endif
 
 /*
  * POSTGRES backend dependent Constants.
@@ -467,7 +469,10 @@ struct pg_conn
 	void	   *engine;			/* dummy field to keep struct the same if
 								 * OpenSSL version changes */
 #endif
-#endif							/* USE_OPENSSL */
+#elif defined(USE_GNUTLS)
+	gnutls_session_t	ssl;	/* SSL status, if have SSL connection */
+	gnutls_x509_crt_t	peer;	/* X509 cert of server */
+#endif
 #endif							/* USE_SSL */
 
 #ifdef ENABLE_GSS
diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c
index c6ee5ea1d4..0591b3d812 100644
--- a/src/port/pg_strong_random.c
+++ b/src/port/pg_strong_random.c
@@ -27,6 +27,10 @@
 #ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
 #ifdef WIN32
 #include <wincrypt.h>
 #endif
@@ -85,8 +89,9 @@ random_from_file(char *filename, void *buf, size_t len)
  * We support a number of sources:
  *
  * 1. OpenSSL's RAND_bytes()
- * 2. Windows' CryptGenRandom() function
- * 3. /dev/urandom
+ * 2. GnuTLS's gnutls_rnd()
+ * 3. Windows' CryptGenRandom() function
+ * 4. /dev/urandom
  *
  * The configure script will choose which one to use, and set
  * a USE_*_RANDOM flag accordingly.
@@ -107,6 +112,14 @@ pg_strong_random(void *buf, size_t len)
 		return true;
 	return false;
 
+	/*
+	 * When built with GnuTLS, use GnuTLS's gnutls_rnd function.
+	 */
+#elif defined(USE_GNUTLS_RANDOM)
+	if (gnutls_rnd(GNUTLS_RND_RANDOM, buf, len) == 0)
+		return true;
+	return false;
+
 	/*
 	 * Windows has CryptoAPI for strong cryptographic numbers.
 	 */
diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
index e4437d19c3..a8fe6fc7d4 100644
--- a/src/test/ssl/Makefile
+++ b/src/test/ssl/Makefile
@@ -13,6 +13,10 @@ subdir = src/test/ssl
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
+ifeq ($(with_openssl),yes)
+export WITH_OPENSSL=yes
+endif
+
 CERTIFICATES := server_ca server-cn-and-alt-names \
 	server-cn-only server-single-alt-name server-multiple-alt-names \
 	server-no-names server-revoked server-ss \
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 32df273929..01eaf19cc4 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -99,10 +99,14 @@ test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=require");
 test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=verify-ca");
 test_connect_fails("sslrootcert=ssl/client_ca.crt sslmode=verify-full");
 
-# Try with just the server CA's cert. This fails because the root file
-# must contain the whole chain up to the root CA.
-note "connect with server CA cert, without root CA";
-test_connect_fails("sslrootcert=ssl/server_ca.crt sslmode=verify-ca");
+SKIP: {
+	skip 'Not compiled with OpenSSL', 1 unless $ENV{'WITH_OPENSSL'};
+
+	# Try with just the server CA's cert. This fails because the root file
+	# must contain the whole chain up to the root CA.
+	note "connect with server CA cert, without root CA";
+	test_connect_fails("sslrootcert=ssl/server_ca.crt sslmode=verify-ca");	
+}
 
 # And finally, with the correct root cert.
 note "connect with correct server CA cert file";
@@ -121,9 +125,13 @@ note "testing sslcrl option with a non-revoked cert";
 test_connect_ok(
 	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid");
 
-# A CRL belonging to a different CA is not accepted, fails
-test_connect_fails(
-"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl");
+SKIP: {
+	skip 'Not compiled with OpenSSL', 1 unless $ENV{'WITH_OPENSSL'};
+
+	# A CRL belonging to a different CA is not accepted, fails
+	test_connect_fails(
+	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl");
+}
 
 # With the correct CRL, succeeds (this cert is not revoked)
 test_connect_ok(
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 686c7369f6..a3c59dd954 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -118,6 +118,10 @@ sub mkvcbuild
 	{
 		push(@pgcommonallfiles, 'sha2_openssl.c');
 	}
+	elsif ($solution->{options}->{gnutls})
+	{
+		push(@pgcommonallfiles, 'sha2_gnutls.c');
+	}
 	else
 	{
 		push(@pgcommonallfiles, 'sha2.c');
@@ -244,6 +248,11 @@ sub mkvcbuild
 		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
 		$libpq->RemoveFile('src/common/sha2_openssl.c');
 	}
+	elsif (!$solution->{options}->{gnutls})
+	{
+		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-gnutls.c');
+		$libpq->RemoveFile('src/common/sha2_gnutls.c');
+	}
 	else
 	{
 		$libpq->RemoveFile('src/common/sha2.c');
