diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index d23df0261c..42263431ed 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1342,10 +1342,11 @@
 
 <para>
 <firstterm>SASL</> is a framework for authentication in connection-oriented
-protocols. At the moment, <productname>PostgreSQL</> implements only one SASL
-authentication mechanism, SCRAM-SHA-256, but more might be added in the
-future. The below steps illustrate how SASL authentication is performed in
-general, while the next subsection gives more details on SCRAM-SHA-256.
+protocols. At the moment, <productname>PostgreSQL</> implements only two SASL
+authentication mechanisms, SCRAM-SHA-256 and SCRAM-SHA-256-PLUS, but more
+might be added in the future. The below steps illustrate how SASL
+authentication is performed in general, while the next subsection gives
+more details on SCRAM-SHA-256 and SCRAM-SHA-256-PLUS.
 </para>
 
 <procedure>
@@ -1430,7 +1431,9 @@ the password is in.
   </para>
 
   <para>
-<firstterm>Channel binding</> has not been implemented yet.
+<firstterm>Channel binding</> is supported in builds with SSL support, and
+uses as mechanism name <literal>SCRAM-SHA-256-PLUS</> for this purpose as
+defined per IANA.
   </para>
 
 <procedure>
@@ -1439,14 +1442,18 @@ the password is in.
 <para>
   The server sends an AuthenticationSASL message. It includes a list of
   SASL authentication mechanisms that the server can accept.
+  <literal>SCRAM-SHA-256</> and <literal>SCRAM-SHA-256-PLUS</> are the
+  two mechanism names that the server lists in this message. Support for
+  channel binding is not included if the server is built without SSL
+  support and if the connection attempt is done without SSL.
 </para>
 </step>
 <step id="scram-client-first">
 <para>
   The client responds by sending a SASLInitialResponse message, which
-  indicates the chosen mechanism, <literal>SCRAM-SHA-256</>. In the Initial
-  Client response field, the message contains the SCRAM
-  <structname>client-first-message</>.
+  indicates the chosen mechanism, <literal>SCRAM-SHA-256</> or
+  <literal>SCRAM-SHA-256-PLUS</>. In the Initial Client response field,
+  the message contains the SCRAM <structname>client-first-message</>.
 </para>
 </step>
 <step id="scram-server-first">
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 99feb0ce94..a372b08bba 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -17,8 +17,6 @@
  *	 by the SASLprep profile, we skip the SASLprep pre-processing and use
  *	 the raw bytes in calculating the hash.
  *
- * - Channel binding is not supported yet.
- *
  *
  * The password stored in pg_authid consists of the iteration count, salt,
  * StoredKey and ServerKey.
@@ -112,6 +110,10 @@ typedef struct
 
 	const char *username;		/* username from startup packet */
 
+	bool		ssl_in_use;
+	char	   *tls_finish_message;
+	int			tls_finish_len;
+
 	int			iterations;
 	char	   *salt;			/* base64-encoded */
 	uint8		StoredKey[SCRAM_KEY_LEN];
@@ -168,7 +170,11 @@ static char *scram_mock_salt(const char *username);
  * it will fail, as if an incorrect password was given.
  */
 void *
-pg_be_scram_init(const char *username, const char *shadow_pass)
+pg_be_scram_init(const char *username,
+				 const char *shadow_pass,
+				 bool ssl_in_use,
+				 char *tls_finish_message,
+				 int tls_finish_len)
 {
 	scram_state *state;
 	bool		got_verifier;
@@ -176,6 +182,9 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
 	state = (scram_state *) palloc0(sizeof(scram_state));
 	state->state = SCRAM_AUTH_INIT;
 	state->username = username;
+	state->ssl_in_use = ssl_in_use;
+	state->tls_finish_message = tls_finish_message;
+	state->tls_finish_len = tls_finish_len;
 
 	/*
 	 * Parse the stored password verifier.
@@ -767,43 +776,97 @@ read_client_first_message(scram_state *state, char *input)
 	 *------
 	 */
 
-	/* read gs2-cbind-flag */
+	/*
+	 * Read gs2-cbind-flag. Server handles its value as described in RFC 5802,
+	 * section 6 dealing with channel binding.
+	 */
 	switch (*input)
 	{
 		case 'n':
-			/* Client does not support channel binding */
+			/*
+			 * Client does not support channel binding, this is authorized
+			 * only in builds not supporting SSL.  If SSL is supported, the
+			 * server cannot support this option either.
+			 */
+#ifdef USE_SSL
+			if (state->ssl_in_use)
+				ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("client does not support SCRAM channel binding, but server needs it for SSL connections")));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("client does not support SCRAM channel binding, but server does")));
+#endif
+			input++;
+			if (*input != ',')
+				ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("malformed SCRAM message (comma expected, got %s)",
+								sanitize_char(*input))));
 			input++;
 			break;
 		case 'y':
-			/* Client supports channel binding, but we're not doing it today */
+			/*
+			 * Client supports channel binding, but we're not doing it today
+			 * in the context of a non-SSL connection.  Complain though if
+			 * the client is trying to trick the server in not doing channel
+			 * binding with a downgrade attack if a SSL connection is
+			 * attempted.
+			 */
+			if (state->ssl_in_use)
+				ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("client support SCRAM channel binding, but server needs it for SSL connections")));
+			input++;
+			if (*input != ',')
+				ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("malformed SCRAM message (comma expected, got %s)",
+								sanitize_char(*input))));
 			input++;
 			break;
 		case 'p':
+			{
+#ifdef USE_SSL
+				char *channel_name;
 
-			/*
-			 * Client requires channel binding.  We don't support it.
-			 *
-			 * RFC 5802 specifies a particular error code,
-			 * e=server-does-support-channel-binding, for this.  But it can
-			 * only be sent in the server-final message, and we don't want to
-			 * go through the motions of the authentication, knowing it will
-			 * fail, just to send that error message.
-			 */
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("client requires SCRAM channel binding, but it is not supported")));
+				if (!state->ssl_in_use)
+					ereport(ERROR,
+							(errcode(ERRCODE_PROTOCOL_VIOLATION),
+							 errmsg("client supports SCRAM channel binding, but server does not need it for non-SSL connections")));
+
+				/*
+				 * Read value provided by client, only tls-unique is supported
+				 * for now.
+				 */
+				channel_name = read_attr_value(&input, 'p');
+				if (strcmp(channel_name, "tls-unique") != 0)
+					ereport(ERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 (errmsg("unexpected SCRAM channel-binding type"))));
+#else
+				/*
+				 * Client requires channel binding.  We don't support it.
+				 *
+				 * RFC 5802 specifies a particular error code,
+				 * e=server-does-support-channel-binding, for this.  But it
+				 * can only be sent in the server-final message, and we don't
+				 * want to go through the motions of the authentication,
+				 * knowing it will fail, just to send that error message.
+				 */
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("client requires SCRAM channel binding, but it is not supported")));
+#endif
+			}
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
 					 (errmsg("malformed SCRAM message (unexpected channel-binding flag %s)",
 							 sanitize_char(*input)))));
 	}
-	if (*input != ',')
-		ereport(ERROR,
-				(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				 errmsg("malformed SCRAM message (comma expected, got %s)",
-						sanitize_char(*input))));
-	input++;
 
 	/*
 	 * Forbid optional authzid (authorization identity).  We don't support it.
@@ -1023,14 +1086,47 @@ read_client_final_message(scram_state *state, char *input)
 	 */
 
 	/*
-	 * Read channel-binding.  We don't support channel binding, so it's
-	 * expected to always be "biws", which is "n,,", base64-encoded.
+	 * Read channel-binding.  We don't support channel binding for builds
+	 * without SSL support, so it's expected to always be "biws" in this case,
+	 * which is "n,,", base64-encoded.  In builds supporting SSL, "biws" is
+	 * used for Non-SSL connection attempts, and for SSL connections the
+	 * client has to provide channel binding value.
 	 */
 	channel_binding = read_attr_value(&p, 'c');
+#ifdef USE_SSL
+	if (state->ssl_in_use)
+	{
+		char	   *enc_tls_message;
+		int			enc_tls_len;
+
+		enc_tls_message = palloc(pg_b64_enc_len(state->tls_finish_len) + 1);
+		enc_tls_len = pg_b64_encode(state->tls_finish_message,
+									state->tls_finish_len,
+									enc_tls_message);
+		enc_tls_message[enc_tls_len] = '\0';
+
+		/*
+		 * Compare the value sent by the client with the TLS finish message
+		 * expected by the server.
+		 */
+		if (strcmp(channel_binding, enc_tls_message) != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 (errmsg("no match for SCRAM channel-binding attribute in client-final-message"))));
+	}
+	else
+	{
+		if (strcmp(channel_binding, "biws") != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 (errmsg("unexpected SCRAM channel-binding attribute in client-final-message"))));
+	}
+#else
 	if (strcmp(channel_binding, "biws") != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_PROTOCOL_VIOLATION),
 				 (errmsg("unexpected SCRAM channel-binding attribute in client-final-message"))));
+#endif
 	state->client_final_nonce = read_attr_value(&p, 'r');
 
 	/* ignore optional extensions */
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 5b68e3b7a1..89dfa744b1 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -841,8 +841,10 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	void	   *scram_opaq;
 	char	   *output = NULL;
 	int			outputlen = 0;
-	char	   *input;
+	char	   *input, *p;
+	char	   *sasl_mechs;
 	int			inputlen;
+	int			listlen = 0;
 	int			result;
 	bool		initial;
 
@@ -861,12 +863,38 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 
 	/*
 	 * Send the SASL authentication request to user.  It includes the list of
-	 * authentication mechanisms (which is trivial, because we only support
-	 * SCRAM-SHA-256 at the moment).  The extra "\0" is for an empty string to
-	 * terminate the list.
+	 * authentication mechanisms that are supported:
+	 * - SCRAM-SHA-256, which is the mechanism with the same name.
+	 * - SCRAM-SHA-256-PLUS, which is SCRAM-SHA-256 with channel binding. This
+	 * is advertised to the client only if connection is attempted with SSL.
+	 * The order of mechanisms is advertised in decreasing order of importance.
+	 * The extra "\0" is for an empty string to terminate the list, and each
+	 * mechanism listed needs to be separated with "\0".
 	 */
-	sendAuthRequest(port, AUTH_REQ_SASL, SCRAM_SHA256_NAME "\0",
-					strlen(SCRAM_SHA256_NAME) + 2);
+	listlen = 0;
+	sasl_mechs = (char *) palloc(strlen(SCRAM_SHA256_PLUS_NAME) +
+								 strlen(SCRAM_SHA256_NAME) + 3);
+	p = sasl_mechs;
+
+	/* add SCRAM-SHA-256-PLUS, which depends on if SSL is in use */
+	if (port->ssl_in_use)
+	{
+		strcpy(p, SCRAM_SHA256_PLUS_NAME);
+		listlen += strlen(SCRAM_SHA256_PLUS_NAME) + 1;
+		p += strlen(SCRAM_SHA256_PLUS_NAME) + 1;
+	}
+
+	/* add generic SCRAM-SHA-256 */
+	strcpy(p, SCRAM_SHA256_NAME);
+	listlen += strlen(SCRAM_SHA256_NAME) + 1;
+	p += strlen(SCRAM_SHA256_NAME) + 1;
+
+	/* put "\0" to mark that list is finished */
+	p[0] = '\0';
+	listlen++;
+
+	sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs, listlen);
+	pfree(sasl_mechs);
 
 	/*
 	 * Initialize the status tracker for message exchanges.
@@ -879,7 +907,11 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	 * This is because we don't want to reveal to an attacker what usernames
 	 * are valid, nor which users have a valid password.
 	 */
-	scram_opaq = pg_be_scram_init(port->user_name, shadow_pass);
+	scram_opaq = pg_be_scram_init(port->user_name,
+								  shadow_pass,
+								  port->ssl_in_use,
+								  port->tls_finish,
+								  port->tls_finish_len);
 
 	/*
 	 * Loop through SASL message exchange.  This exchange can consist of
@@ -933,7 +965,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 			 * is an error.
 			 */
 			selected_mech = pq_getmsgrawstring(&buf);
-			if (strcmp(selected_mech, SCRAM_SHA256_NAME) != 0)
+			if (strcmp(selected_mech, SCRAM_SHA256_NAME) != 0 &&
+				strcmp(selected_mech, SCRAM_SHA256_PLUS_NAME) != 0)
 			{
 				ereport(COMMERROR,
 						(errcode(ERRCODE_PROTOCOL_VIOLATION),
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 44c84a7869..eff8b300de 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -458,6 +458,7 @@ be_tls_open_server(Port *port)
 	int			err;
 	int			waitfor;
 	unsigned long ecode;
+	char		tls_finish_buf[20];
 
 	Assert(!port->ssl);
 	Assert(!port->peer);
@@ -616,6 +617,25 @@ aloop:
 	/* set up debugging/info callback */
 	SSL_CTX_set_info_callback(SSL_context, info_cb);
 
+	/*
+	 * Save the TLS finish message expected to be found, useful for
+	 * authentication checks related to channel binding.
+	 * SSL_get_peer_finished() does not offer a way to know the exact length
+	 * of a TLS finish message beforehand, so attempt first with a fixed-length
+	 * buffer, and try again if the message does not fit.
+	 */
+	port->tls_finish_len = SSL_get_peer_finished(port->ssl,
+												 tls_finish_buf,
+												 sizeof(tls_finish_buf));
+	port->tls_finish = MemoryContextAlloc(TopMemoryContext,
+										  port->tls_finish_len);
+	if (port->tls_finish_len > sizeof(tls_finish_buf))
+		memcpy(port->tls_finish, tls_finish_buf, port->tls_finish_len);
+	else
+		(void) SSL_get_peer_finished(port->ssl,
+									 port->tls_finish,
+									 port->tls_finish_len);
+
 	return 0;
 }
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 0669b924cf..6eb39e0678 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -181,6 +181,8 @@ typedef struct Port
 	bool		ssl_in_use;
 	char	   *peer_cn;
 	bool		peer_cert_valid;
+	char	   *tls_finish;		/* TLS message expected from client */
+	int			tls_finish_len;	/* length expected of TLS message */
 
 	/*
 	 * OpenSSL structures. (Keep these last so that the locations of other
diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h
index 14b48af12f..fc6fe2431f 100644
--- a/src/include/libpq/scram.h
+++ b/src/include/libpq/scram.h
@@ -13,8 +13,9 @@
 #ifndef PG_SCRAM_H
 #define PG_SCRAM_H
 
-/* Name of SCRAM-SHA-256 per IANA */
+/* Name of SCRAM mechanisms per IANA */
 #define SCRAM_SHA256_NAME "SCRAM-SHA-256"
+#define SCRAM_SHA256_PLUS_NAME "SCRAM-SHA-256-PLUS"	/* with channel binding */
 
 /* Status codes for message exchange */
 #define SASL_EXCHANGE_CONTINUE		0
@@ -22,7 +23,9 @@
 #define SASL_EXCHANGE_FAILURE		2
 
 /* Routines dedicated to authentication */
-extern void *pg_be_scram_init(const char *username, const char *shadow_pass);
+extern void *pg_be_scram_init(const char *username, const char *shadow_pass,
+					 bool ssl_in_use, char *tls_finish_message,
+					 int tls_finish_len);
 extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen, char **logdetail);
 
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index d2e355a8b8..5e0837a2c9 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -44,6 +44,9 @@ typedef struct
 	/* These are supplied by the user */
 	const char *username;
 	char	   *password;
+	bool		ssl_in_use;
+	char	   *tls_finish_message;
+	int			tls_finish_len;
 
 	/* We construct these */
 	uint8		SaltedPassword[SCRAM_KEY_LEN];
@@ -81,7 +84,11 @@ static bool pg_frontend_random(char *dst, int len);
  * Initialize SCRAM exchange status.
  */
 void *
-pg_fe_scram_init(const char *username, const char *password)
+pg_fe_scram_init(const char *username,
+				 const char *password,
+				 bool ssl_in_use,
+				 char *tls_finish_message,
+				 int tls_finish_len)
 {
 	fe_scram_state *state;
 	char	   *prep_password;
@@ -93,6 +100,9 @@ pg_fe_scram_init(const char *username, const char *password)
 	memset(state, 0, sizeof(fe_scram_state));
 	state->state = FE_SCRAM_INIT;
 	state->username = username;
+	state->ssl_in_use = ssl_in_use;
+	state->tls_finish_message = tls_finish_message;
+	state->tls_finish_len = tls_finish_len;
 
 	/* Normalize the password with SASLprep, if possible */
 	rc = pg_saslprep(password, &prep_password);
@@ -297,9 +307,10 @@ static char *
 build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 {
 	char		raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
-	char	   *buf;
-	char		buflen;
+	char	   *result;
+	int			channel_info_len;
 	int			encoded_len;
+	PQExpBufferData buf;
 
 	/*
 	 * Generate a "raw" nonce.  This is converted to ASCII-printable form by
@@ -328,26 +339,55 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 	 * prepared with SASLprep, the message parsing would fail if it includes
 	 * '=' or ',' characters.
 	 */
-	buflen = 8 + strlen(state->client_nonce) + 1;
-	buf = malloc(buflen);
-	if (buf == NULL)
-	{
-		printfPQExpBuffer(errormessage,
-						  libpq_gettext("out of memory\n"));
-		return NULL;
-	}
-	snprintf(buf, buflen, "n,,n=,r=%s", state->client_nonce);
+	initPQExpBuffer(&buf);
 
-	state->client_first_message_bare = strdup(buf + 3);
+	/*
+	 * First build the query field for channel binding. If the client is not
+	 * built with SSL support, it cannot support channel binding so it needs
+	 * to use "n" to let the server know. If built with SSL support, client
+	 * needs to use "y" to let the server know that client has such support
+	 * but that it is not using it as a non-SSL connection is requested.
+	 * Finally if a SSL connection is done, use p=cb-name, for which only
+	 * "tls-unique" is supported now.
+	 */
+#ifdef USE_SSL
+	if (state->ssl_in_use)
+		appendPQExpBuffer(&buf, "p=tls-unique");
+	else
+		appendPQExpBuffer(&buf, "y");
+#else
+	appendPQExpBuffer(&buf, "n");
+#endif
+
+	if (PQExpBufferDataBroken(buf))
+		goto oom_error;
+
+	channel_info_len = buf.len;
+
+	appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
+	if (PQExpBufferDataBroken(buf))
+		goto oom_error;
+
+	/*
+	 * The first message content needs to be saved without channel binding
+	 * information.
+	 */
+	state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
 	if (!state->client_first_message_bare)
-	{
-		free(buf);
-		printfPQExpBuffer(errormessage,
-						  libpq_gettext("out of memory\n"));
-		return NULL;
-	}
+		goto oom_error;
 
-	return buf;
+	result = strdup(buf.data);
+	if (result == NULL)
+		goto oom_error;
+
+	termPQExpBuffer(&buf);
+	return result;
+
+oom_error:
+	termPQExpBuffer(&buf);
+	printfPQExpBuffer(errormessage,
+					  libpq_gettext("out of memory\n"));
+	return NULL;
 }
 
 /*
@@ -365,8 +405,30 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
 	/*
 	 * Construct client-final-message-without-proof.  We need to remember it
 	 * for verifying the server proof in the final step of authentication.
+	 * Client needs to provide a b64 encoded string of the TLS finish message
+	 * only if a SSL connection is attempted.
 	 */
-	appendPQExpBuffer(&buf, "c=biws,r=%s", state->nonce);
+#ifdef USE_SSL
+	if (state->ssl_in_use)
+	{
+		appendPQExpBuffer(&buf, "c=");
+		if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(state->tls_finish_len)))
+			goto oom_error;
+		buf.len += pg_b64_encode(state->tls_finish_message,
+								 state->tls_finish_len,
+								 buf.data + buf.len);
+		buf.data[buf.len] = '\0';
+	}
+	else
+		appendPQExpBuffer(&buf, "c=biws");
+#else
+	appendPQExpBuffer(&buf, "c=biws");
+#endif
+
+	if (PQExpBufferDataBroken(buf))
+		goto oom_error;
+
+	appendPQExpBuffer(&buf, ",r=%s", state->nonce);
 	if (PQExpBufferDataBroken(buf))
 		goto oom_error;
 
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index f4397afc64..ea4bbbae95 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -494,7 +494,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 	/*
 	 * Parse the list of SASL authentication mechanisms in the
 	 * AuthenticationSASL message, and select the best mechanism that we
-	 * support.  (Only SCRAM-SHA-256 is supported at the moment.)
+	 * support.  SCRAM-SHA-256 and SCRAM-SHA-256-PLUS are the only ones
+	 * supported at the moment.
 	 */
 	selected_mechanism = NULL;
 	for (;;)
@@ -522,7 +523,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 		/*
 		 * Do we support this mechanism?
 		 */
-		if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0)
+		if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0 ||
+			strcmp(mechanism_buf.data, SCRAM_SHA256_PLUS_NAME) == 0)
 		{
 			char	   *password;
 
@@ -537,10 +539,18 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 				goto error;
 			}
 
-			conn->sasl_state = pg_fe_scram_init(conn->pguser, password);
+			conn->sasl_state = pg_fe_scram_init(conn->pguser,
+												password,
+												conn->ssl_in_use,
+												conn->tls_finish,
+												conn->tls_finish_len);
 			if (!conn->sasl_state)
 				goto oom_error;
-			selected_mechanism = SCRAM_SHA256_NAME;
+
+			if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0)
+				selected_mechanism = SCRAM_SHA256_NAME;
+			else
+				selected_mechanism = SCRAM_SHA256_PLUS_NAME;
 		}
 	}
 
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 9f4c2a50d8..2ee9c6c48c 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -23,7 +23,9 @@ extern int	pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn);
 extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
 
 /* Prototypes for functions in fe-auth-scram.c */
-extern void *pg_fe_scram_init(const char *username, const char *password);
+extern void *pg_fe_scram_init(const char *username, const char *password,
+					 bool ssl_in_use, char *tls_finish_message,
+					 int tls_finish_len);
 extern void pg_fe_scram_free(void *opaq);
 extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen,
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index a7c3d7af64..b701b18c1c 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -1297,6 +1297,7 @@ static PostgresPollingStatusType
 open_client_SSL(PGconn *conn)
 {
 	int			r;
+	char		tls_finish_buf[20];
 
 	ERR_clear_error();
 	r = SSL_connect(conn->ssl);
@@ -1376,6 +1377,24 @@ open_client_SSL(PGconn *conn)
 		return PGRES_POLLING_FAILED;
 	}
 
+	/*
+	 * Save the TLS finish message sent to the server, useful for
+	 * authentication checks related to channel binding. SSL_get_finished()
+	 * does not offer a way to know the exact length of a TLS finish message
+	 * beforehand, so attempt first with a fixed-length buffer, and try again
+	 * if the message does not fit.
+	 */
+	conn->tls_finish_len = SSL_get_finished(conn->ssl,
+											tls_finish_buf,
+											sizeof(tls_finish_buf));
+	conn->tls_finish = malloc(conn->tls_finish_len);
+	if (conn->tls_finish_len > sizeof(tls_finish_buf))
+		memcpy(conn->tls_finish, tls_finish_buf, conn->tls_finish_len);
+	else
+		(void) SSL_get_finished(conn->ssl,
+								conn->tls_finish,
+								conn->tls_finish_len);
+
 	/* SSL handshake is complete */
 	return PGRES_POLLING_OK;
 }
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 335568b790..ab2b9befbf 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -454,11 +454,15 @@ struct pg_conn
 	/* Assorted state for SASL, SSL, GSS, etc */
 	void	   *sasl_state;
 
+	/* SSL structures */
+	bool		ssl_in_use;
+	char       *tls_finish;		/* TLS finish message sent */
+	int			tls_finish_len;	/* length of TLS message sent */
+
 #ifdef USE_SSL
 	bool		allow_ssl_try;	/* Allowed to try SSL negotiation */
 	bool		wait_ssl_try;	/* Delay SSL negotiation until after
 								 * attempting normal connection */
-	bool		ssl_in_use;
 #ifdef USE_OPENSSL
 	SSL		   *ssl;			/* SSL status, if have SSL connection */
 	X509	   *peer;			/* X509 cert of server */
