繁体   English   中英

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

[英]Options for Programmatically Adding Certificates to Java KeyStore

我收到 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

嗯,这有效。 万岁。 问题是我们将把我们的应用程序放在像 rackspace 或 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 吗? 我觉得我要么错误地将证书添加到 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);
...

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


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

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

别。 您不应该修改 JRE 附带的文件。 下一次升级,您的更新将消失。 您应该发布自己的信任库,该信任库由 JRE 附带信任库以及您想要信任的任何额外证书构建而成。 这是您的应用程序构建的一部分。

如果您想在运行时修改您自己的信任库,请继续,但是您需要注意 JVM 在重新启动之前不一定会看到更改:它肯定不会在您使用的相同SSLContext中看到它们获取要添加的证书。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM