3.5.9 使用 SSL 安全连接

Connector/J 可以使用 SSL 加密 JDBC 驱动程序和服务器之间通信的所有数据(初始握手除外)。启用连接加密会降低性能,其严重程度取决于多种因素,包括(但不限于)查询的大小、返回的数据量、服务器硬件、使用的 SSL 库、网络带宽、等等。

系统通过两个 Java 密钥库文件工作:一个文件包含服务器的证书信息(truststore在下面的示例中),另一个文件包含客户端的密钥和证书(keystore在下面的示例中)。所有 Java 密钥库文件均受 创建文件时提供给密钥工具的密码保护。您需要文件名和关联的密码来创建 SSL 连接。

要使 SSL 支持起作用,您必须具备以下条件:

默认情况下,Connector/J 与 MySQL 服务器建立安全连接。请注意,MySQL 服务器 5.7 和 8.0 在使用 OpenSSL 编译时,可以在启动时自动生成丢失的 SSL 文件并相应地配置 SSL 连接。

对于 8.0.12 及更早版本:只要服务器正确配置为使用 SSL,就无需在 Connector/J 客户端上进行任何配置即可使用加密连接(当 Connector/J 连接到非常旧的服务器时除外5.6.25 及更早版本或 5.7.5 及更早版本,在这种情况下,客户端必须设置连接属性useSSL=true才能使用加密连接)。客户端可以通过设置连接属性来要求使用 SSL requireSSL=true;如果服务器未配置为使用 SSL,则连接会失败。没有 requireSSL=true,如果服务器未配置为使用 SSL,则连接只会退回到非加密模式。

对于 8.0.13 及更高版本:只要服务器正确配置为使用 SSL,就无需在 Connector/J 客户端上进行任何配置即可使用加密连接。客户端可以通过设置连接属性 、 或 来要求sslMode=REQUIRED使用 VERIFY_CASSL VERIFY_IDENTITY;如果服务器未配置为使用 SSL,则连接会失败。使用 sslMode=PREFERRED,如果服务器未配置为使用 SSL,则连接只会退回到非加密模式。对于 X 协议连接,连接属性 xdevapi.ssl-mode指定 SSL 模式设置,就像sslModeMySQL 协议连接一样(除了 PREFERREDX 协议不支持);如果未明确设置,xdevapi.ssl-mode则采用sslMode(如果 xdevapi.ssl-mode未设置且 sslMode设置为 PREFERREDxdevapi.ssl-mode则设置为 REQUIRED)的值。

为了提高安全性,您可以为客户端设置单向(服务器或客户端)或双向(服务器和客户端)SSL 身份验证,从而允许客户端或服务器验证彼此的身份。

TLS 版本:可以使用连接属性限制 TLS 协议的允许版本 tlsVersions,对于 X DevAPI 连接和 8.0.19 及更高版本, xdevapi.tls-versionsxdevapi.tls-versions未指定时,它会占用 的值tlsVersions)。如果没有指定此类限制,Connector/J 会尝试使用 TLSv1.2 和 TLSv1.3 连接到服务器。

笔记
  • 自 Connector/J 8.0.28 起,连接属性enabledTLSProtocols已重命名为tlsVersions,并 enabledSSLCipherSuites已重命名为tlsCiphersuites; 原始名称保留为别名。

  • 对于 Connector/J 8.0.26 及更高版本: TLSv1 和 TLSv1.1 在 Connector/J 8.0.26 中已弃用,并在 8.0.28 版中删除;删除的值被视为对连接选项和会话设置无效。可以使用更安全的 TLSv1.2 和 TLSv1.3 协议建立连接。使用 TLSv1.3 需要使用 OpenSSL 1.1.1 或更高版本编译服务器,并使用支持 TLSv1.3 的 JVM(例如,Oracle Java 8u261 及更高版本)运行 Connector/J。

  • 对于使用 JDBC API 连接到 MySQL Community Server 5.6 和 5.7 时的 Connector/J 8.0.18 及更早版本:由于与使用 yaSSL 编译的 MySQL Server 的兼容性问题,默认情况下,Connector/J 不启用与 TLSv1.2 及更高版本的连接。当连接到限制连接使用那些更高 TLS 版本的服务器时,通过设置 Connector/J 连接属性 enabledTLSProtocols(例如, set enabledTLSProtocols=TLSv1.2,TLSv1.3)显式启用它们。

密码套件:自 8.0.19 版以来,Connector/J 可用的密码套件受到一个属性文件的预先限制,该文件可以在 src/main/resources/com/mysql/cj/TlsSettings.properties 的文件夹内src或独立于平台的分发存档中找到(在 .tar.gz.zip 格式)用于连接器/J。该文件包含四个部分,在每个部分中列出了强制、批准、弃用和不可接受的密码。只能使用前三部分中列出的套件。最后一部分(不可接受的)定义了阻止列出不安全密码套件的模式或掩码。实际上,在前三节中已经给出白名单的情况下,第四节中的黑名单模式是多余的;但它们的存在是为了防止不需要的密码。密码套件的白名单和黑名单适用于 JDBC 和 X DevAPI 连接。

可以使用连接属性限制 SSL 连接允许的密码套件 tlsCiphersuites,对于 X DevAPI 连接和版本 8.0.19 及更高版本, xdevapi.tls-ciphersuitesxdevapi.tls-ciphersuites未指定时,它采用 的值tlsCiphersuites)。如果未指定此类限制,Connector/J 会尝试与服务器接受的任何列入白名单的密码套件建立 SSL 连接。

设置服务器身份验证

对于 8.0.12 及更早版本:useSSL当 Connector/J 连接属性AND verifyServerCertificate都为真时,将启用通过服务器证书验证进行的服务器身份验证 不支持主机名验证——主机验证仅通过证书进行。

对于 8.0.13 及更高版本:sslMode当 Connector/J 连接属性设置为VERIFY_CA或 时,启用通过服务器证书验证的服务器身份验证VERIFY_IDENTITY。如果 AND 都为真 sslMode时,将启用通过服务器证书验证进行的服务器身份验证useSSLverifyServerCertificate

由受信任的 CA 签名的证书。  当启用通过服务器证书验证的服务器身份验证时,如果没有对服务器身份验证进行额外的配置,Java 将使用其默认的受信任 CA 证书来验证服务器证书,通常来自 $JAVA_HOME/lib/security/cacerts.

使用自签名证书。  尽管 MySQL 服务器证书是自签名的或由自签名的 CA 证书签名的,但这是很常见的;MySQL 服务器创建的自动生成的证书和密钥基于后者,即服务器生成所有必需的密钥和用于签署服务器和客户端证书的自签名 CA 证书。然后服务器将自身配置为使用 CA 证书和服务器证书。客户端证书文件虽然放在同一个目录下,但并没有被服务端使用。

为了验证服务器证书,Connector/J 需要能够读取签署它的证书,即签署自己的服务器证书或自签署的 CA 证书。这可以通过将证书(ca.pem或任何其他证书)导入 Java 默认信任库(尽管不建议篡改默认信任库)或将其导入自定义 Java 信任库文件并相应地配置 Connector/J 驱动程序来实现。使用 Java 的 keytool(通常位于binJDK 或 JRE 安装的子目录中)导入服务器证书:

$> keytool -importcert -alias MySQLCACert -file ca.pem \
    -keystore truststore -storepass mypassword

为命令选项提供正确的参数。如果信任库文件不存在,将创建一个新文件;否则证书将被添加到现有文件中。与keytool的交互如下所示:

Owner: CN=MySQL_Server_5.7.17_Auto_Generated_CA_Certificate
Issuer: CN=MySQL_Server_5.7.17_Auto_Generated_CA_Certificate
Serial number: 1
Valid from: Thu Feb 16 11:42:43 EST 2017 until: Sun Feb 14 11:42:43 EST 2027
Certificate fingerprints:
	 MD5:  18:87:97:37:EA:CB:0B:5A:24:AB:27:76:45:A4:78:C1
	 SHA1: 2B:0D:D9:69:2C:99:BF:1E:2A:25:4E:8D:2D:38:B8:70:66:47:FA:ED
	 SHA256: C3:29:67:1B:E5:37:06:F7:A9:93:DF:C7:B3:27:5E:09:C7:FD:EE:2D:18:86:F4:9C:40:D8:26:CB:DA:95:A0:24
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1
Trust this certificate? [no]:  yes
Certificate was added to keystore

命令的输出显示有关导入证书的所有详细信息。确保记住您提供的密码。另外,请注意密码必须以纯文本形式写入您的 Connector/J 配置文件或应用程序源代码中。

下一步是配置 Java 或 Connector/J 以读取您刚刚创建或修改的信任库。这可以通过使用以下三种方法之一来完成:

  1. 使用 Java 命令行参数:

    -Djavax.net.ssl.trustStore=path_to_truststore_file 
    -Djavax.net.ssl.trustStorePassword=mypassword

  2. 直接在客户端代码中设置系统属性:

    System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file"); 
    System.setProperty("javax.net.ssl.trustStorePassword","mypassword");

  3. 设置 Connector/J 连接属性:

    trustCertificateKeyStoreUrl=file:path_to_truststore_file 
    trustCertificateKeyStorePassword=mypassword

请注意,当一起使用时,连接属性会覆盖其他两种方法设置的值。此外,使用连接属性设置的任何值仅在该连接中使用,而使用系统范围值设置的值用于所有连接(除非被连接属性覆盖)。对于 Connector/J 8.0.22 及更高版本:设置连接属性 fallbackToSystemTrustStorefalse防止 Connector/J 在未使用方法 (3) 时回退到您使用方法 (1) 或 (2) 创建的系统范围信任库设置。

通过上述设置并启用服务器身份验证,所有建立的连接都将被 SSL 加密,服务器在 SSL 握手过程中得到身份验证,客户端现在可以安全地信任它正在连接的服务器。

对于 X-Protocol 连接,连接属性 xdevapi.ssl-truststore, xdevapi.ssl-truststore-type, xdevapi.ssl-truststore-password, 并 xdevapi.ssl-fallbackToSystemTrustStore 指定信任库设置,就像 trustCertificateKeyStoreUrl, trustCertificateKeyStoreType, trustCertificateKeyStorePasswordamd fallbackToSystemTrustStore对 MySQL 协议连接所做的一样;如果没有明确设置, 、 xdevapi.ssl-truststorexdevapi.ssl-truststore-typexdevapi.ssl-truststore-password分别 xdevapi.ssl-fallbackToSystemTrustStore取、 、和 的值。 trustCertificateKeyStoreUrltrustCertificateKeyStoreTypetrustCertificateKeyStorePasswordfallbackToSystemTrustStore

服务身份验证。  对于 8.0.13 及更高版本:除了通过服务器证书验证进行服务器身份验证之外,当 sslMode设置为 时VERIFY_IDENTITY,Connector/J 还会通过检查其用于连接的主机名是否与服务器证书中的 Common Name 值匹配来执行主机名身份验证。

设置客户端身份验证

服务器可能想要对客户端进行身份验证并要求客户端向其提供 SSL 证书,服务器会根据其已知的证书颁发机构对其进行验证,或者在需要时对客户端身份执行额外的检查(有关详细信息,请参阅 CREATE USER SSL/TLS 选项)。在这种情况下,Connector/J 需要访问客户端证书,以便在建立新的数据库连接时将其发送到服务器。这是使用 Java 密钥库文件完成的。

要允许客户端身份验证,连接到服务器的客户端必须有自己的一组密钥和 SSL 证书。客户端证书必须经过签名,以便服务器可以对其进行验证。虽然您可以让官方证书颁发机构签署客户端证书,但更常见的是使用中间的私有 CA 证书来签署客户端证书。这样的中间 CA 证书可以是自签名的或由受信任的根 CA 签名的。要求是服务器知道能够验证客户端证书的 CA 证书。

一些 MySQL 服务器构建能够生成用于通信加密的 SSL 密钥和证书,包括证书和私钥(包含在 client-cert.pemclient-key.pem文件中),可供任何客户端使用。此 SSL 证书已由自签名 CA 证书签名ca.pem,服务器可能已配置为使用该证书。

如果您不想使用服务器生成的客户端密钥和证书文件,您也可以使用 创建 SSL 和 RSA 证书和密钥中描述的过程生成新的。请注意,根据服务器的设置,您可能必须重新使用服务器配置为使用的现有 CA 证书来签署新的客户端证书,而不是创建新证书。

获得要使用的客户端私钥和证书文件后,需要将它们导入 Java 密钥库,以便 Java SSL 库和 Connector/J. 以下说明说明了如何创建密钥库文件:

  • 将客户端密钥和证书文件转换为 PKCS #12 存档:

    $> openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem \
      -name "mysqlclient" -passout pass:mypassword -out client-keystore.p12

  • 将客户端密钥和证书导入 Java 密钥库:

    $> keytool -importkeystore -srckeystore client-keystore.p12 -srcstoretype pkcs12 \
     -srcstorepass mypassword -destkeystore keystore -deststoretype JKS -deststorepass mypassword

    为命令选项提供正确的参数。如果密钥库文件不存在,将创建一个新文件;否则证书将被添加到现有文件中。keytool的输出如下所示:

    Entry for alias mysqlclient successfully imported.
    Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

    确保记住您选择的密码。另外,请注意密码必须以纯文本形式写入您的 Connector/J 配置文件或应用程序源代码中。

在该步骤之后,您可以删除 PKCS #12 存档(client-keystore.p12 在示例中)。

下一步是配置 Java 或 Connector/J,以便它读取您刚刚创建或修改的密钥库。这可以通过使用以下三种方法之一来完成:

  1. 使用 Java 命令行参数:

    -Djavax.net.ssl.keyStore=path_to_keystore_file
    -Djavax.net.ssl.keyStorePassword=mypassword

  2. 直接在客户端代码中设置系统属性:

    System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file"); 
    System.setProperty("javax.net.ssl.keyStorePassword","mypassword");

  3. 通过 Connector/J 连接属性:

    clientCertificateKeyStoreUrl=file:path_to_truststore_file 
    clientCertificateKeyStorePassword=mypassword

请注意,当一起使用时,连接属性会覆盖其他两种方法设置的值。此外,使用连接属性设置的任何值仅在该连接中使用,而使用系统范围值设置的值用于所有连接(除非被连接属性覆盖)。对于 Connector/J 8.0.22 及更高版本:设置连接属性 fallbackToSystemKeyStorefalse防止 Connector/J 在未使用方法 (3) 时回退到您使用方法 (1) 或 (2) 创建的系统范围密钥库设置。

通过上述设置,所有建立的连接都将被 SSL 加密,客户端在 SSL 握手过程中进行身份验证,服务器现在可以安全地信任请求连接的客户端。

对于 Connector/J 8.0.22 及更高版本:对于 X-Protocol 连接,连接属性 xdevapi.ssl-keystorexdevapi.ssl-keystore-typexdevapi.ssl-keystore-passwordxdevapi.ssl-fallbackToSystemKeyStore指定密钥库设置,就像 trustCertificateKeyStoreUrltrustCertificateKeyStoreTypetrustCertificateKeyStorePasswordfallbackToSystemTKeyStore为 MySQL 协议连接做的一样;如果没有明确设置, 、 xdevapi.ssl-keystorexdevapi.ssl-keystore-typexdevapi.ssl-keystore-password分别 xdevapi.ssl-fallbackToSystemKeyStore取、 和 。 clientCertificateKeyStoreUrlclientCertificateKeyStoreTypeclientCertificateKeyStorePasswordfallbackToSystemKeyStore

设置双向验证

应用设置服务器身份验证设置客户端身份验证 中概述的步骤 来设置相互的双向身份验证过程,在该过程中,服务器和客户端在建立连接之前相互进行身份验证。

尽管上述典型设置在两端使用相同的 CA 证书进行相互身份验证,但并非必须如此。唯一的要求是服务器中配置的CA证书必须能够验证客户端证书,并且导入到客户端信任库中的CA证书必须能够验证服务器证书;两端使用的两个 CA 证书可以不同。

调试 SSL 连接

stdout JSSE在您设置系统属性时 提供调试信息-Djavax.net.debug=all。然后,Java 会告诉您正在使用哪些密钥库和信任库,以及在 SSL 握手和证书交换期间发生了什么。当您尝试调试失败的 SSL 连接时,这将很有帮助。