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

Ice*_*nte 12 java ssl keystore pkix programmatically-created

我收到了SSL握手异常错误:PKIX"路径不链"(此处描述).我通过使用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
Run Code Online (Sandbox Code Playgroud)

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

我想,"没问题,我只是以编程方式将此证书链添加到密钥库"所以我将其从JVM中删除:

keytool -delete -alias envmgrchain -keystore cacerts -storepass changeit
Run Code Online (Sandbox Code Playgroud)

并添加了此代码:

    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);
Run Code Online (Sandbox Code Playgroud)

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

仅供参考:我也试图通过实施X509TrustManager来解决这个问题.

jww*_*jww 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);
...
Run Code Online (Sandbox Code Playgroud)

您需要一个受信任的分发渠道,以确保您的程序在坐在等待挑选的服务器上或在安装时沿着网络传输时不会被修改.


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

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

您遇到的问题称为"哪个目录"问题.它是PKI中众所周知的问题.从本质上讲,这意味着客户端不知道从哪里获取缺少的中间证书.您可以通过让服务器发送所有必需的中间产品以及服务器的证书来解决它.请参阅OWASP的TLS备忘单和规则 - 始终提供所有需要的证书.


只是自行车脱落,但是Java中有一整套蠕虫(特别是Java 7及更低版本):

SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);
Run Code Online (Sandbox Code Playgroud)

如果需要,您可以改进它.见SSLSocketFactoryEx哪个密码套件启用SSL套接字?.它弥补了Java中默认提供的协议版本,密码套件等方面的一些空白SSLSocketFactory.

  • 这里有很多很好的信息-谢谢!我目前要做的是自己提供证书。我创建了一个自定义X509TrustManager,从默认管理器捕获到CertificateException后,便会循环浏览存储在名为“ trustedCerts”的目录中的一堆证书。我将它们转换为X509证书,并使用X509Certificate.equals()对链进行比较检查。如果存在匹配项,我接受证书。您认为在这里使用equals是有效的比较吗?(顺便说一句,如果这个问题太大而无法发表评论,请告诉我,我将对此另作一个问题)。 (2认同)