使用IAM认证连接AWS RDS MySQL时遇证书链信任锚错误
问题:Java 17通过IAM认证连接AWS RDS MySQL时SSL握手失败
我正在编写基于Java 17的应用,尝试通过IAM认证连接AWS RDS MySQL实例,采用SSL连接方式,先后尝试了以下官方证书包:
- global-bundle.pem
- us-west-2-bundle.pem(RDS实例位于us-west-2区域)
- rds-ca-2019-us-west-2.pem(示例代码使用,但未在SSL文档中列出)
简化后的核心代码如下:
X509Certificate x509Cert; CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); URL url = new File("c:\\users\\me\\documents\\global-bundle.pem").toURI().toURL(); try (InputStream certInputStream = url.openStream()) { x509Cert = (X509Certificate) certFactory.generateCertificate(certInputStream); } File keyStoreFile = File.createTempFile("sys-connect-via-ssl-test-cacerts", ".jks"); try (FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath())) { KeyStore ks = KeyStore.getInstance("JKS", "SUN"); ks.load(null); ks.setCertificateEntry("rootCaCertificate", x509Cert); ks.store(fos, "changeit".toCharArray()); } System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getPath()); System.setProperty("javax.net.ssl.trustStoreType", "JKS"); System.setProperty("javax.net.ssl.trustStorePassword", "changeit"); BasicAWSCredentials awsCredentials = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY); RdsIamAuthTokenGenerator generator = RdsIamAuthTokenGenerator.builder() .credentials(new AWSStaticCredentialsProvider(awsCredentials)).region("us-west-2").build(); String authToken = generator.getAuthToken(GetIamAuthTokenRequest.builder() .hostname(RDS_INSTANCE_HOSTNAME).port(3306).userName(DB_USER).build()); Properties mysqlConnectionProperties = new Properties(); mysqlConnectionProperties.setProperty("verifyServerCertificate","true"); mysqlConnectionProperties.setProperty("useSSL", "true"); mysqlConnectionProperties.setProperty("user",DB_USER); mysqlConnectionProperties.setProperty("password",authToken); DriverManager.getConnection("jdbc:mysql://" + RDS_INSTANCE_HOSTNAME + ":3306", mysqlConnectionProperties);
调用DriverManager.getConnection(...)时触发错误,核心异常为:
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
更换三种证书包后问题依旧。查阅资料得知该错误通常需根证书+区域中间证书,但已确认所用bundle包包含两类证书,仍无法解决,请问故障原因是什么?
故障原因及解决方案
1. 证书加载不完整:仅读取了bundle中的第一个证书
你的代码使用certFactory.generateCertificate(certInputStream)只会加载PEM bundle文件里的第一个证书,但AWS的bundle包包含多个证书(根证书、中间证书),这种方式会丢失后续证书,导致证书链不完整,最终无法通过SSL验证。
正确的加载方式是使用generateCertificates方法读取所有证书,并全部加入密钥库:
// 替换原证书加载逻辑 Collection<? extends Certificate> certs; try (InputStream certInputStream = url.openStream()) { certs = certFactory.generateCertificates(certInputStream); } // 将所有证书添加到密钥库 int certIndex = 0; for (Certificate cert : certs) { ks.setCertificateEntry("ca-cert-" + certIndex++, cert); }
2. 密钥库配置冗余(Java 17环境)
Java 17中无需指定SUN作为JKS密钥库的提供者,直接使用默认实现即可,简化代码:
KeyStore ks = KeyStore.getInstance("JKS"); // 移除第二个参数
3. JDBC连接属性优化
建议在MySQL连接属性中添加requireSSL=true强制SSL连接,确保使用的MySQL驱动版本为8.0+(IAM认证对驱动版本有要求):
mysqlConnectionProperties.setProperty("requireSSL", "true");
4. 验证RDS实例的CA版本匹配
登录AWS控制台查看你的RDS实例的CA证书标识符(如rds-ca-2023),确认下载的bundle包与该版本对应。如果实例使用的是旧版CA,需下载对应年份的区域bundle包。
5. 避免覆盖系统全局信任库(可选)
直接通过系统属性设置信任库会影响应用中其他SSL连接,建议为MySQL连接单独配置SSL上下文:
// 基于加载好的密钥库构建SSL上下文 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // 将SSL上下文传递给MySQL驱动 mysqlConnectionProperties.setProperty("sslSocketFactory", sslContext.getSocketFactory().getClass().getName());
内容的提问来源于stack exchange,提问作者pacoverflow
相关产品推荐
相关产品推荐

