【问题标题】:Options for Programmatically Adding Certificates to Java KeyStore以编程方式将证书添加到 Java KeyStore 的选项
【发布时间】:2014-06-04 17:03:04
【问题描述】:

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

这行得通。万岁。问题是我们将把我们的应用程序放在像 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 来解决这个问题。

【问题讨论】:

  • 只发送您想与应用程序一起使用的信任库并通过 vm 参数引用它怎么样?
  • 另外,这里有一个由 Andreas Sterbenz 编写的名为 InstallCert 的著名示例,它可以完成您想要做的事情,但您必须解析代码才能准确获得所需内容:code.google.com/p/java-use-examples/source/browse/trunk/src/com/…

标签: java ssl keystore programmatically-created pkix


【解决方案1】:

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

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

【讨论】:

  • 这里有很多好信息-谢谢!我目前所做的是自己提供证书。我创建了一个自定义 X509TrustManager,它在从默认管理器中捕获 CertificateException 后,会循环浏览我存储在名为“trustedCerts”的目录中的一堆证书。我将这些转换为 X509 证书并使用 X509Certificate.equals() 对链进行比较检查...如果匹配,我接受证书。你认为在这里使用 equals 是一个有效的比较吗? (顺便说一句,如果这个问题太大而无法发表评论,请告诉我,我将针对它开始一个单独的问题)。
  • “我目前做的是自己提供证书...使用自定义X509TrustManager ...” - 是的,携带服务器预期的证书很好。这就是所谓的固定,它是一个伟大的安全增强。它击败了网络安全模型的鼻涕。请参阅 OWASP 的Certificate and Public Key Pinning
  • 只是小费。将 CA 硬编码为一行中的字符串时,添加 \n 代替换行符。并不是说我会推荐这种方法。我需要它来进行测试
【解决方案2】:

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

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

【讨论】:

  • "如果您想在运行时修改自己的信任库,请继续,但是您需要注意,JVM 在重新启动之前不一定会看到更改:它肯定不会在用于获取要添加的证书的同一 SSLContext 中查看它们。"
  • KeyStore 是从输入流加载而不是通过文件提供的事实的逻辑结果。
猜你喜欢
  • 2018-12-23
  • 2018-11-03
  • 1970-01-01
  • 1970-01-01
  • 2014-08-18
  • 1970-01-01
  • 2013-06-15
  • 1970-01-01
  • 2016-12-30
相关资源
最近更新 更多