You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用AWS Java SDK通过IoT凭证获取临时凭证时遇bad_certificate错误

问题:Java SDK调用AWS IoT凭证提供者时出现SSLHandshakeException: bad_certificate

已按AWS IoT凭证提供者流程完成配置,curl请求可正常获取临时凭证,但Java SDK实现时抛出javax.net.ssl.SSLHandshakeException: bad_certificate。已验证设备证书、私钥有效,且用keytool确认客户端PKCS12密钥库和信任库创建正常。尝试过以下操作仍未解决:

  • 通过System.setProperty配置客户端密钥库
  • 使用TlsKeyManagersProvider传入密钥管理器
  • 切换为系统默认cacerts信任库

相关配置与代码

  1. 生成PKCS12客户端密钥库命令
openssl pkcs12 -export -in certificate.pem -inkey private.key -out keystore.p12 -name exampleName -CAFile AmazonRootCA1.pem -caname root -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1
  1. 信任库生成代码
private void createTrustStore() {
    Path pemPath = Paths.get("/directory/AmazonRootCA1.pem");
    try (final InputStream is = Files.newInputStream(pemPath)) {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(is);
        String alias = cert.getSubjectX500Principal().getName();
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(null);
        keystore.setCertificateEntry(alias, cert);
        FileOutputStream out = new FileOutputStream("/directory/CAtrustStore.jks");
        keystore.store(out, "password".toCharArray());
        System.setProperty("javax.net.ssl.trustStore", "/directory/CAtrustStore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "password");
        System.setProperty("javax.net.ssl.trustStoreType", "jks");
    } catch (Exception e) {
        // 异常处理
    }
}
  1. 凭证请求代码
public void getCredentials() {
    HttpExecuteResponse response;
    Path clientKeyStore = Path.of("keystore.p12");
    TlsKeyManagersProvider provider = FileStoreTlsKeyManagersProvider.create(clientKeyStore, "pkcs12", "password");
    KeyManager[] keyManagers = provider.keyManagers();
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keyManagers, null, null);
    ConnectionSocketFactory socketFactory = new SdkTlsSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    try (SdkHttpClient client = ApacheHttpClient.builder().socketFactory(socketFactory).build()) {
        SdkHttpRequest httpRequest = SdkHttpRequest.builder()
                .method(SdkHttpMethod.GET)
                .uri(new URI("https://<myPrefix>.credentials.iot.us-east-2.amazonaws.com/role-aliases/<myAlias>/credentials"))
                .putHeader("x-amzn-iot-thingname", "<myThingName>")
                .build();
        HttpExecuteRequest request = HttpExecuteRequest.builder()
                .request(httpRequest)
                .build();
        response = client.prepareRequest(request).call();
    } catch (Exception e) {
        e.printStackTrace();
    }
    Log.MainLog().info(response.httpResponse().toString());
    Log.MainLog().info(response.responseBody().toString());
}

排查与解决步骤

1. 确认客户端密钥库包含完整证书链

bad_certificate常见原因是客户端未提供完整证书链(设备证书 + AWS IoT根CA)。检查PKCS12密钥库内容:

keytool -list -v -keystore keystore.p12 -storetype pkcs12

输出中需看到exampleName别名下包含设备证书和AmazonRootCA1的完整链条。若缺失,重新生成PKCS12时确保-CAFile参数正确指向根CA文件。

2. 显式加载信任库到SSL上下文

当前代码中sslContext.init(keyManagers, null, null)未传入信任管理器,可能导致系统默认信任库未正确加载。修改代码显式加载信任库:

// 加载信任库
KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream trustStoreStream = Files.newInputStream(Paths.get("/directory/CAtrustStore.jks"))) {
    trustStore.load(trustStoreStream, "password".toCharArray());
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

// 初始化SSLContext时传入信任管理器
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagers, trustManagers, new SecureRandom());

3. 验证设备证书与Thing Name的关联

  • 登录AWS IoT控制台,检查<myThingName>关联的证书列表,确认当前使用的设备证书已激活且关联正确。
  • 检查IoT角色别名的权限策略,确保允许该证书对应的Thing调用凭证提供者。

4. 恢复主机名验证(禁用存在安全风险)

当前代码使用NoopHostnameVerifier.INSTANCE跳过主机名验证,Java SSL默认会验证服务器域名与证书SAN/CN字段是否匹配。建议移除该配置,使用默认验证器,同时确认请求URL域名与AWS IoT凭证提供者证书的SAN字段一致。

5. 指定兼容的TLS版本

AWS IoT服务要求使用TLS 1.2及以上版本,显式指定TLS版本避免兼容性问题:

SSLContext sslContext = SSLContext.getInstance("TLSv1.2");

同时检查Java环境jdk.tls.client.protocols系统属性是否包含TLSv1.2。

6. 启用SSL调试日志定位细节

添加JVM参数启用SSL调试日志,查看握手过程中的具体错误:

-Djavax.net.debug=ssl:handshake:verbose

日志会显示证书验证失败的核心原因(如证书过期、链不完整、根CA不被信任等)。


内容的提问来源于stack exchange,提问作者Joshua S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 21:54:57