diff --git a/configure b/configure
index 2a1ee251f2..2d1b24c67a 100755
--- a/configure
+++ b/configure
@@ -12908,6 +12908,17 @@ _ACEOF
 fi
 done
 
+  # Function introduced in OpenSSL 1.1.1 to allow client to obtain server's CA list
+  for ac_func in SSL_get0_peer_CA_list
+do :
+  ac_fn_c_check_func "$LINENO" "SSL_get0_peer_CA_list" "ac_cv_func_SSL_get0_peer_CA_list"
+if test "x$ac_cv_func_SSL_get0_peer_CA_list" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SSL_GET0_PEER_CA_LIST 1
+_ACEOF
+
+fi
+done
 
 $as_echo "#define USE_OPENSSL 1" >>confdefs.h
 
diff --git a/configure.ac b/configure.ac
index 52fd7af446..58ec6d6271 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1381,6 +1381,7 @@ if test "$with_ssl" = openssl ; then
   AC_CHECK_FUNCS([CRYPTO_lock])
   # Function introduced in OpenSSL 1.1.1.
   AC_CHECK_FUNCS([X509_get_signature_info])
+  AC_CHECK_FUNCS([SSL_get0_peer_CA_list])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d0d5aefadc..118a2c3f39 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1778,6 +1778,32 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslcertdir" xreflabel="sslcertdir">
+      <term><literal>sslcertdir</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the directory to automatically find a suitable
+        client certificate to send to the server if it requests one. The selection
+        is based on the list of trusted CA names sent from the server in the
+        Certificate Request handshake message. A client certificate whose issuer
+        name equals to one of the trusted CA names is considered trusted by the
+        server. This parameter cannot be used with <literal>sslcert</literal>
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry id="libpq-connect-sslkeydir" xreflabel="sslkeydir">
+      <term><literal>sslkeydir</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the directory to find a private key that forms
+        a match with the public key of the client certificate selected from
+        <literal>sslcertdir</literal>. This parameter cannot be used with
+        <literal>sslkey</literal>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
diff --git a/meson.build b/meson.build
index 8ed51b6aae..661bc17f5a 100644
--- a/meson.build
+++ b/meson.build
@@ -1302,6 +1302,7 @@ if sslopt in ['auto', 'openssl']
 
       # Function introduced in OpenSSL 1.1.1
       ['X509_get_signature_info'],
+      ['SSL_get0_peer_CA_list'],
     ]
 
     are_openssl_funcs_complete = true
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 07e73567dc..923993626c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -381,6 +381,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_cert_cb' function. */
 #undef HAVE_SSL_CTX_SET_CERT_CB
 
+/* Define to 1 if you have the `SSL_get0_peer_CA_list' function. */
+#undef HAVE_SSL_GET0_PEER_CA_LIST
+
 /* Define to 1 if stdbool.h conforms to C99. */
 #undef HAVE_STDBOOL_H
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 64c0b628b3..be610b1fb6 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -284,6 +284,14 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"SSL-Client-Key", "", 64,
 	offsetof(struct pg_conn, sslkey)},
 
+	{"sslcertdir", "PGSSLCERTDIR", NULL, NULL,
+		"SSL-Client-Cert-Dir", "", 64,
+	offsetof(struct pg_conn, sslcertdir)},
+
+	{"sslkeydir", "PGSSLKEYDIR", NULL, NULL,
+		"SSL-Client-Key-Dir", "", 64,
+	offsetof(struct pg_conn, sslkeydir)},
+
 	{"sslcertmode", "PGSSLCERTMODE", NULL, NULL,
 		"SSL-Client-Cert-Mode", "", 8,	/* sizeof("disable") == 8 */
 	offsetof(struct pg_conn, sslcertmode)},
@@ -1736,6 +1744,48 @@ pqConnectOptions2(PGconn *conn)
 			goto oom_error;
 	}
 
+#ifndef HAVE_SSL_GET0_PEER_CA_LIST
+	/*
+	 * Without a SSL_get0_peer_ca_list support, the current implementation can't
+	 * select from multiple client certificate based on server's trusted CA list,
+	 * so "sslcertdir" and "sslkeydir" options are useless in this case.
+	 */
+	if (conn->sslcertdir || conn->sslkeydir)
+	{
+		conn->status = CONNECTION_BAD;
+		libpq_append_conn_error(conn, "%s and %s are not supported (check OpenSSL version). "
+					"Please use %s and %s to specify a client certificate and key",
+					"sslcertdir",
+					"sslkeydir",
+					"sslcert",
+					"sslkey");
+		return false;
+	}
+#else
+	/*
+	 * validate sslcertdir and sslkeydir conflicts
+	 */
+	if (conn->sslcertdir && conn->sslcert)
+	{
+		conn->status = CONNECTION_BAD;
+		libpq_append_conn_error(conn, "%s and %s cannot be specified"
+					"at the same time",
+					"sslcertdir",
+					"sslcert");
+		return false;
+	}
+
+	if (conn->sslkeydir && conn->sslkey)
+	{
+		conn->status = CONNECTION_BAD;
+		libpq_append_conn_error(conn, "%s and %s cannot be specified"
+					"at the same time",
+					"sslkeydir",
+					"sslkey");
+		return false;
+	}
+#endif
+
 	/*
 	 * Only if we get this far is it appropriate to try to connect. (We need a
 	 * state flag, rather than just the boolean result of this function, in
@@ -4373,6 +4423,8 @@ freePGconn(PGconn *conn)
 	free(conn->sslmode);
 	free(conn->sslcert);
 	free(conn->sslkey);
+	free(conn->sslcertdir);
+	free(conn->sslkeydir);
 	if (conn->sslpassword)
 	{
 		explicit_bzero(conn->sslpassword, strlen(conn->sslpassword));
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 6bc216956d..cc15c4770f 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -25,6 +25,7 @@
 #include <signal.h>
 #include <fcntl.h>
 #include <ctype.h>
+#include <dirent.h>
 
 #include "libpq-fe.h"
 #include "fe-auth.h"
@@ -459,23 +460,237 @@ verify_cb(int ok, X509_STORE_CTX *ctx)
 	return ok;
 }
 
+#ifdef HAVE_SSL_GET0_PEER_CA_LIST
+/*
+ * Helper function to read a certificate file
+ *
+ * This function tries to load 'filename' into X509 structure
+ */
+static X509*
+load_certificate(const char* filename)
+{
+	FILE *file = fopen(filename, "r");
+	X509 *cert = NULL;
+
+	if (!file)
+		return NULL;
+
+	cert = PEM_read_X509(file, NULL, NULL, NULL);
+	fclose(file);
+
+	return cert;
+}
+
+/*
+ * Utility function to find a suitable client certificate
+ *
+ * This function tries to find a client certificate in 'directory' whose issuer
+ * name matches one of the subjects listed in 'peercas'. Returns NULL if none
+ * found.
+ */
+static X509*
+find_client_certificate_by_issuer(const STACK_OF(X509_NAME) * peercas, const char* directory,
+		PGconn * conn)
+{
+	X509 *cert_candidate = NULL;
+	DIR *dir = opendir(directory);
+	struct dirent *entry;
+	int i = 0, numcas = 0;
+
+	if (!dir)
+	{
+		libpq_append_conn_error(conn, "cannot access certificate directory %s"
+				,directory);
+		return NULL;
+	}
+
+	numcas = sk_X509_NAME_num(peercas);
+
+	while ((entry = readdir(dir)) != NULL)
+	{
+		if (entry->d_type == DT_REG)
+		{
+			char filepath[1024] = {0};
+			snprintf(filepath, sizeof(filepath), "%s/%s", directory, entry->d_name);
+
+			cert_candidate = load_certificate(filepath);
+			if (cert_candidate)
+			{
+				/*
+				 * found a x509 certificate file, check if it can be trusted by comparing its
+				 * issuer against the list of subjects in peercas
+				 */
+				for (i = 0; i < numcas; i++)
+				{
+					X509_NAME *ca_name = sk_X509_NAME_value(peercas, i);
+					if(!X509_NAME_cmp(X509_get_issuer_name(cert_candidate), ca_name))
+					{
+						/*
+						 * this candidate certificate's issuer matches one of the peercas's
+						 * subject names, it should be trusted by the server
+						 */
+						conn->sslcert = strdup(filepath);
+						closedir(dir);
+						return cert_candidate;
+					}
+				}
+				X509_free(cert_candidate);
+			}
+		}
+	}
+	closedir(dir);
+	return NULL;  /* no suitable client certificate found */
+}
+
+/*
+ * Helper function to read a private key file
+ *
+ * This function tries to load 'filename' into EVP_PKEY structure
+ */
+static EVP_PKEY*
+load_pkey(const char* filename)
+{
+	FILE *file = fopen(filename, "r");
+	EVP_PKEY *pkey = NULL;
+
+	if (!file)
+		return NULL;
+
+	pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
+	fclose(file);
+
+	return pkey;
+}
+
+/*
+ * Utility function to find matching private key
+ *
+ * This function tries to find a private key in 'directory' that matches the public
+ * key inside 'clientcert'. Returns NULL if none is found.
+ */
+static EVP_PKEY*
+find_client_key(X509* clientcert, const char* directory, PGconn * conn)
+{
+	EVP_PKEY *pkey_candidate = NULL;
+	DIR *dir = opendir(directory);
+	struct dirent *entry;
+
+	if (!dir)
+	{
+		libpq_append_conn_error(conn, "cannot access pkey directory %s"
+				,directory);
+		return NULL;
+	}
+
+	while ((entry = readdir(dir)) != NULL)
+	{
+		if (entry->d_type == DT_REG)
+		{
+			char filepath[1024] = {0};
+			snprintf(filepath, sizeof(filepath), "%s/%s", directory, entry->d_name);
+
+			pkey_candidate = load_pkey(filepath);
+			if (pkey_candidate)
+			{
+				/* make sure this pkey matches clientcert */
+				if (X509_check_private_key(clientcert, pkey_candidate))
+				{
+					/* it is a match */
+					conn->sslkey = strdup(filepath);
+					closedir(dir);
+					return pkey_candidate;
+				}
+				EVP_PKEY_free(pkey_candidate);
+			}
+		}
+	}
+	closedir(dir);
+	return NULL;  /* no suitable certificate found */
+}
+#endif
+
 #ifdef HAVE_SSL_CTX_SET_CERT_CB
 /*
  * Certificate selection callback
  *
- * This callback lets us choose the client certificate we send to the server
- * after seeing its CertificateRequest.  We only support sending a single
- * hard-coded certificate via sslcert, so we don't actually set any certificates
- * here; we just use it to record whether or not the server has actually asked
- * for one and whether we have one to send.
+ * This callback lets us choose a client certificate to send to the server
+ * after seeing its CertificateRequest. If the server sends its CA list while
+ * sslcertdir and sslkeydir are specified, we will be able to select the most
+ * suitable client certificate to send. Otherwise we just use this callback to
+ * record whether or not the server has actually asked for a client certificate
+ * and whether we have one to send.
  */
 static int
 cert_cb(SSL *ssl, void *arg)
 {
 	PGconn	   *conn = arg;
 
+#ifdef HAVE_SSL_GET0_PEER_CA_LIST
+	X509	   *clientcert = NULL;
+	EVP_PKEY   *clientkey = NULL;
+
+	/* obtain a list of CA list sent from the server, if any */
+	const STACK_OF(X509_NAME) * peercas = NULL;
+	peercas = SSL_get0_peer_CA_list(ssl);
+#endif
+
+	/* mark that the server has requested a client certificate */
 	conn->ssl_cert_requested = true;
 
+#ifdef HAVE_SSL_GET0_PEER_CA_LIST
+	/*
+	 * if sslcertdir and sslkeydir are specified and the server has sent a CA list,
+	 * then we can try to find the most suitable certificate to send to the server.
+	 */
+	if (conn->sslcertmode[0] != 'd' &&	/* sslcertmode is not disabled */
+		peercas && conn->sslcertdir && conn->sslkeydir)
+	{
+		/* try to select a suitable client certificate */
+		clientcert = find_client_certificate_by_issuer(peercas, conn->sslcertdir, conn);
+		if (!clientcert)
+		{
+			libpq_append_conn_error(conn, "Server requests a client certificate but no suitable "
+					"certificate is found from the directory %s", conn->sslcertdir);
+			conn->ssl_cert_sent = false;
+
+			/*
+			 * we return 1 here to allow TLS handshake to continue even though no client certificate
+			 * is set, making it up to the server to decide if handshake should be aborted with the
+			 * absence of client certificate
+			 */
+			return 1;
+		}
+
+		/* try to find a matching private key */
+		clientkey = find_client_key(clientcert, conn->sslkeydir, conn);
+		if (!clientkey)
+		{
+			libpq_append_conn_error(conn, "A suitable client certificate exists but "
+					"no matching private key is found from the directory %s", conn->sslkeydir);
+			conn->ssl_cert_sent = false;
+			X509_free(clientcert);
+
+			/*
+			 * we return 1 here to allow TLS handshake to continue even though no client certificate
+			 * is set, making it up to the server to decide if handshake should be aborted with the
+			 * absence of client certificate
+			 */
+			return 1;
+		}
+
+		/*
+		 * we should now have both the client certificate and private key. Set them to SSL. Note
+		 * that we use SSL_use_certificte_chain_file() to load the certificate file instead of using
+		 * clientcert because it would load intermediate or root CAs appended in the same client
+		 * cert file if any. These may be needed to verify server's certificate in the next handshake
+		 * stage
+		 */
+		X509_free(clientcert);
+		SSL_use_certificate_chain_file(ssl, conn->sslcert);
+		SSL_use_PrivateKey(ssl, clientkey);
+	}
+#endif
+
 	/* Do we have a certificate loaded to send back? */
 	if (SSL_get_certificate(ssl))
 		conn->ssl_cert_sent = true;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 82c18f870d..433572ab56 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -391,6 +391,8 @@ struct pg_conn
 	char	   *sslcompression; /* SSL compression (0 or 1) */
 	char	   *sslkey;			/* client key filename */
 	char	   *sslcert;		/* client certificate filename */
+	char	   *sslcertdir;	/* path to a directory of certificate files */
+	char	   *sslkeydir;		/* path to a directory of key files */
 	char	   *sslpassword;	/* client key file password */
 	char	   *sslcertmode;	/* client cert mode (require,allow,disable) */
 	char	   *sslrootcert;	/* root certificate filename */
