diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 14f35d37f66..6659300e3ff 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -8397,23 +8397,35 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
   <para>
    If the server attempts to verify the identity of the
    client by requesting the client's leaf certificate,
-   <application>libpq</application> will send the certificates stored in
+   <application>libpq</application> will send the certificate(s) stored in
    file <filename>~/.postgresql/postgresql.crt</filename> in the user's home
    directory.  The certificates must chain to the root certificate trusted
    by the server.  A matching
    private key file <filename>~/.postgresql/postgresql.key</filename> must also
-   be present. The private
-   key file must not allow any access to world or group; achieve this by the
-   command <command>chmod 0600 ~/.postgresql/postgresql.key</command>.
+   be present.
    On Microsoft Windows these files are named
    <filename>%APPDATA%\postgresql\postgresql.crt</filename> and
-   <filename>%APPDATA%\postgresql\postgresql.key</filename>, and there
-   is no special permissions check since the directory is presumed secure.
+   <filename>%APPDATA%\postgresql\postgresql.key</filename>.
    The location of the certificate and key files can be overridden by the
-   connection parameters <literal>sslcert</literal> and <literal>sslkey</literal> or the
+   connection parameters <literal>sslcert</literal>
+   and <literal>sslkey</literal>, or by the
    environment variables <envar>PGSSLCERT</envar> and <envar>PGSSLKEY</envar>.
   </para>
 
+  <para>
+   On Unix systems, the permissions on the private key file must disallow
+   any access to world or group; achieve this by a command such as
+   <command>chmod 0600 ~/.postgresql/postgresql.key</command>.
+   Alternatively, the file can be owned by root and have group read access
+   (that is, <literal>0640</literal> permissions).  That setup is intended
+   for installations where certificate and key files are managed by the
+   operating system.  The user of <application>libpq</application> should
+   then be made a member of the group that has access to those certificate
+   and key files.  (On Microsoft Windows, there is no file permissions
+   check, since the <filename>%APPDATA%\postgresql</filename> directory is
+   presumed secure.)
+  </para>
+
   <para>
    The first certificate in <filename>postgresql.crt</filename> must be the
    client's certificate because it must match the client's private key.
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index a212308666a..612f6f3777e 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -143,6 +143,7 @@ check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
 		return false;
 	}
 
+	/* Key file must be a regular file */
 	if (!S_ISREG(buf.st_mode))
 	{
 		ereport(loglevel,
@@ -153,9 +154,19 @@ check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
 	}
 
 	/*
-	 * Refuse to load key files owned by users other than us or root.
+	 * Refuse to load key files owned by users other than us or root, and
+	 * require no public access to the 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 either our gid or a supplementary gid that
+	 * allows us to read system-wide certificates.
 	 *
-	 * XXX surely we can check this on Windows somehow, too.
+	 * Note that similar checks are performed in
+	 * src/interfaces/libpq/fe-secure-openssl.c so any changes here may need
+	 * to be made there as well.
+	 *
+	 * Ideally we would do similar permissions checks on Windows, but it is
+	 * not clear how that would work since Unix-style permissions may not be
+	 * available.
 	 */
 #if !defined(WIN32) && !defined(__CYGWIN__)
 	if (buf.st_uid != geteuid() && buf.st_uid != 0)
@@ -166,20 +177,7 @@ check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
 						ssl_key_file)));
 		return false;
 	}
-#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)))
 	{
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 0ad4dfaa9cc..daaf062b2ab 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -1229,11 +1229,45 @@ initialize_SSL(PGconn *conn)
 							  fnbuf);
 			return -1;
 		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+
+		/* Key file must be a regular file */
+		if (!S_ISREG(buf.st_mode))
 		{
 			appendPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  libpq_gettext("private key file \"%s\" is not a regular file"),
+							  fnbuf);
+			return -1;
+		}
+
+		/*
+		 * Refuse to load key files owned by users other than us or root, and
+		 * require no public access to the 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 either our gid or a supplementary
+		 * gid that allows us to read system-wide certificates.
+		 *
+		 * Note that similar checks are performed in
+		 * src/backend/libpq/be-secure-common.c so any changes here may need
+		 * to be made there as well.
+		 *
+		 * Ideally we would do similar permissions checks on Windows, but it
+		 * is not clear how that would work since Unix-style permissions may
+		 * not be available.
+		 */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+		if (buf.st_uid != geteuid() && buf.st_uid != 0)
+		{
+			appendPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" must be owned by the current user or root\n"),
+							  fnbuf);
+			return -1;
+		}
+
+		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)))
+		{
+			appendPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"),
 							  fnbuf);
 			return -1;
 		}