java - 以编程方式将证书添加到 Java KeyStore 的选项

标签 java ssl keystore programmatically-created pkix

我收到 SSL 握手异常错误:PKIX“路径不链接”(described here)。 我通过使用 openssl 导入证书链来修复它:

openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt

并将其安装到我的 JDK keystore 中:

keytool -import -alias envmgrchain -file cert_chain.crt -keystore cacerts -storepass changeit

这很好用。万岁。问题是我们将把我们的应用程序放在像 rackspace 或 AWS 这样的云服务器上,我认为我们很有可能无法修改 JVM 的 keystore 来添加这个链。

我想,“没问题,我只需以编程方式将此证书链添加到 keystore ”,所以我将其从我的 JVM 中删除:

keytool -delete -alias envmgrchain -keystore cacerts -storepass changeit

并添加了这段代码:

    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    //Create an empty keystore that we can load certificate into
    trustStore.load(null);
    InputStream fis = new FileInputStream("cert_chain.crt");
    BufferedInputStream bis = new BufferedInputStream(fis);

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    while(bis.available()>0) {
        Collection<? extends Certificate> certs = cf.generateCertificates(bis);
        Iterator<? extends Certificate> iter = certs.iterator();
        //Add each cert in the chain one at a time
        for(int i=0; i<certs.size(); i++) {
            Certificate cert = iter.next();
            String alias = "chaincert"+((i>0)?i:"");
            trustStore.setCertificateEntry(alias, cert);
        }
    }
    bis.close();
    fis.close();
//Add custom keystore to TrustManager
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(trustStore);
    SSLContext ctx = SSLContext.getInstance("TLSv1");
    ctx.init(null, tmf.getTrustManagers(), null);

但是当我运行它时,PKIX 错误再次出现。上面的代码不等同于 keytool -import 吗?我觉得我要么错误地将证书添加到 KeyStore,要么我没有以正确的方式将 Keystore 安装到 TrustManager。

仅供引用:我也试图通过实现 X509TrustManager 来解决这个问题。

最佳答案

您可以使用以下代码让客户端在运行时以编程方式添加您的 CA。您无需将其存放在任何商店 - 只需随身携带 PEM 编码文件即可。您甚至可以将其硬编码到您的程序中,这样就无需管理单独的文件。

static String CA_FILE = "ca-cert.pem";
...

FileInputStream fis = new FileInputStream(CA_FILE);
X509Certificate ca = (X509Certificate) CertificateFactory.getInstance("X.509")
                        .generateCertificate(new BufferedInputStream(fis));

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(Integer.toString(1), ca);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
...

您将需要一个可信赖的分发 channel ,以确保您的程序在等待被挑选的服务器上或在安装过程中传输时不会被修改。


openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt

您应该只需要信任根证书,而不是整个链。服务器负责发送构建链所需的所有中间证书。如果服务器没有发送构建链所需的所有中间证书,则服务器配置错误。

您遇到的问题称为“哪个目录”问题。这是 PKI 中众所周知的问题。从本质上讲,这意味着客户端不知道去哪里获取丢失的中间证书。您可以通过让服务器发送所有必需的中间体以及服务器的证书来解决它。请参阅 OWASP 的 TLS 备忘单和 Rule - Always Provide All Needed Certificates .


只是自行车脱落,但这里有一大堆关于 Java 的蠕虫病毒(尤其是 Java 7 及更低版本):

SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);

如果需要,您可以对其进行改进。请参阅 Which Cipher Suites to enable for SSL Socket? 处的 SSLSocketFactoryEx .它弥补了 Java SSLSocketFactory 中默认提供的协议(protocol)版本、密码套件等方面的一些差距。

关于java - 以编程方式将证书添加到 Java KeyStore 的选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24043397/

相关文章:

Java DefaultSslContextFactory keystore 动态更新

java - enum.values() - 是返回枚举确定性的顺序

.htaccess - 除了在 .htaccess 中强制使用 HTTPS 之外,我还需要启用 HSTS 吗?

java - 使用 Wildfly 16.0.0.Final 和 ejb 客户端的 TLS/SSL 失败并显示 org.xnio.http.UpgradeFailedException : Invalid response code 200

ant - 通过 TLS 的 Apache Ant FTP 连接

jenkins - 从 Jenkins 凭证管理器导出 keystore

android - 在 Android 4+ (ICS) 中将 Android TrustStore 用于 aSmack

java - 播放列表随机播放开/关

java - 使用 split() 拆分一个没有任何分隔的数字 - java

java - 如何使用 Java native 接口(interface)将字节数组传递给以 char* 作为参数的 C 函数?