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

问题描述 投票:12回答:2

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

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

并将其安装到我的JDK的密钥库中:

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

好吧。万岁。问题是我们会将应用程序放在机架或AWS等云服务器上,我认为我们很有可能无法修改JVM的密钥库来添加此链。

我以为,“没问题,我将以编程方式将此证书链添加到密钥库中,所以我从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吗?我感觉我是在不正确地将证书添加到密钥库中,或者我没有以正确的方式将密钥库安装到TrustManager中。

FYI:我还试图通过实现X509TrustManager来解决此问题。

java ssl keystore programmatically-created pkix
2个回答
17
投票

以下代码可用于客户端,以便在运行时以编程方式添加您的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);
...

您将需要一个受信任的分发渠道,以确保您的程序在坐在服务器上等待被拾取或在安装过程中沿线移动时不会被修改。


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);

如果需要,您可以对其进行改进。请参见SSLSocketFactoryEx处的Which Cipher Suites to enable for SSL Socket?。它弥补了Java SSLSocketFactory默认提供的协议版本,密码套件等方面的空白。


-1
投票

不要。您不应该修改JRE随附的文件。下次升级时,您的更新已消失。您应该运送您的own

© www.soinside.com 2019 - 2024. All rights reserved.