【问题标题】:Java TooTallNate Secure Websocket Server on FireTV with Lets Encrypt Certificate: Handshake / Chain Error带有 Lets Encrypt 证书的 FireTV 上的 Java TooTallNate 安全 Websocket 服务器:握手/链错误
【发布时间】:2020-10-08 17:16:48
【问题描述】:

我正在使用 Java Websocket 服务器 (TooTallNate)。 Javascript 应用程序通过 LetsEncrypt 证书安全连接。它通过 certbot 自动更新,并且也在同一台机器上为 Apache 提供服务。在所有经过测试的浏览器上一切正常,对于 https 和 wss。

我想将我的应用作为打包的 FireTV 应用提交。我在“Web App Tester”应用程序中对其进行了测试。一旦 JS 尝试连接到 WSS,它就会引发 SSL 错误,读取 adb-logcat

I/X509util: Failed to validate the certificate chain, error: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

但有时只是

E/chromium(13208): [ERROR:ssl_client_socket_impl.cc(947)] handshake failed; returned -1, SSL error code 1, net_error -202

从 TooTallNate 填充 SSLContext 的相关 Java 代码是:

private static SSLContext getContext() {
    SSLContext context;
    String password = "CHANGEIT";
    String pathname = "pem";
    try {
        context = SSLContext.getInstance("TLS");

        byte[] certBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "cert.pem")),"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
        byte[] keyBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "privkey.pem")),"-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");

        X509Certificate cert = generateCertificateFromDER(certBytes);
        RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);

        KeyStore keystore = KeyStore.getInstance("JKS");
        keystore.load(null);
        keystore.setCertificateEntry("cert-alias", cert);
        keystore.setKeyEntry("key-alias", key, password.toCharArray(), new Certificate[]{cert});

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keystore, password.toCharArray());

        KeyManager[] km = kmf.getKeyManagers();

        context.init(km, null, null);
    }
    catch (Exception e) {
        context = null;
    }
    return context;
}

【问题讨论】:

    标签: ssl websocket lets-encrypt


    【解决方案1】:

    它需要一些阅读才能使其工作。关于这个特殊问题的信息很少,所以我决定自己回答:

    这确实是 SSLContext 中缺少链的问题。不敢相信,因为它适用于所有其他基于铬的浏览器。 在我的 LetsEncrypt 文件夹中,我有四个文件:privkey.pem(not at thing)、cert.pem、chain.pem 和 fullchain.pem,其中 fullchain.pem 只是 chain.pem 和 cert.pem 的串联。 cert.pem 是我们自己的证书,chain.pem 是 Digital Signature Trust DST Root CA X3 证书,您可以看到自己在使用它:

    cat chain.pem | openssl x509 -text
    

    我比较了通过 https 和 wss 提供的证书:

    openssl s_client -connect myurl:443 | openssl x509 -text
    

    openssl s_client -connect myurl:8080 | openssl x509 -text
    

    后者在输出开头显示两个错误

    verify error:num=20:unable to get local issuer certificate
    

    当来自 apache 的 https 响应显示时

    depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
    verify return:1
    depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
    verify return:1
    depth=0 CN = myurl
    verify return:1
    

    最后,工作解决方案比我同时拥有的解决方案要容易得多。无需下载任何其他链证书。只需像 cert.pem 一样加载您在 LetsEncrypt 文件夹中已有的 chain.pem,并将两者作为链添加到 setKeyEntry 函数中的密钥库:

    private static SSLContext getContext() {
        SSLContext context;
        String password = "CHANGEIT";
        String pathname = "pem";
        try {
            context = SSLContext.getInstance("TLS");
    
            byte[] certBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "cert.pem")),"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
            byte[] chainBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "chain.pem")),"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
            byte[] keyBytes = parseDERFromPEM(getBytes(new File(pathname + File.separator + "privkey.pem")),"-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
    
            X509Certificate cert = generateCertificateFromDER(certBytes);
            X509Certificate chain = generateCertificateFromDER(chainBytes);
            RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
    
            KeyStore keystore = KeyStore.getInstance("JKS");
            keystore.load(null);
            keystore.setCertificateEntry("cert-alias", cert);
            keystore.setKeyEntry("key-alias", key, password.toCharArray(), new Certificate[]{cert, chain});
    
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(keystore, password.toCharArray());
    
            KeyManager[] km = kmf.getKeyManagers();
    
            context.init(km, null, null);
        }
        catch (Exception e) {
            context = null;
        }
        return context;
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-11
      • 2013-12-25
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      相关资源
      最近更新 更多