当前位置: 首页 > 知识库问答 >
问题:

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

上官扬
2023-03-14

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

< code > OpenSSL s _ client-主机www.envmgr.com-端口443-显示证书

并将其安装到我的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错误再次出现。上面的代码不等同于keytools-import吗?我觉得我要么不正确地将证书添加到KeyStore,要么没有以正确的方式将Keystore安装到TrustManager中。

仅供参考:我还试图通过实现X509TrustManager来解决这个问题。

共有2个答案

庄弘业
2023-03-14

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

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

邹齐智
2023-03-14

下面的代码可供客户端在运行时以编程方式添加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 -主机 www.envmgr.com -端口 443 -显示证书

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

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

只是自行车脱落,但Java(尤其是Java 7及更低版本)还有一大堆蠕虫:

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

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

 类似资料:
  • 问题内容: 我正在将SSL客户端连接到我的SSL服务器。当客户端由于根密钥不存在而无法验证证书时,我需要选择将该证书添加到代码中的本地密钥存储中并继续。有一些示例始终接受所有证书,但是我希望用户验证证书并将其添加到本地密钥存储中,而无需离开应用程序。 关于自定义SocketFactory,TrustManager,SSLContext等的内容很多,我不太了解它们如何组合在一起,或者这将是我实现目标

  • 我正在尝试在Android上添加Wifi网络,我想知道如何连接到不广播其SSID的Wifi网络(它是否有空SSID或带有\0s的清晰SSID)。 这是我目前用于广播其SSID的Wifi网络的内容:

  • 问题内容: 对于RealityKit的文档包括结构:,,和用于添加材料到。 或者,您可以在模型中加载带有附加材料的模型。 我想以编程方式添加自定义材料/纹理。如何在不向Reality Composer或其他3D软件的模型中添加材料的情况下即时实现这一目标? 问题答案: 已更新:2020年6月23日 。 目前,RealityKit 2.0中有4种类型的材料 : 简单材质 不发光材料 咬合材料 Vid

  • AndroidR、 属性。selectableItemBackground存在,但如何通过编程将其添加到ImageButton? 此外,我如何在文档中找到答案?这里提到过,但我看不到任何关于它实际如何使用的解释。实际上,我似乎很少发现文档有用,但我希望这是我的错,而不是文档的错。

  • 问题内容: 我试图自定义现有的JS库,而不修改原始JS代码。这段代码将加载一些我可以访问的外部JS文件,而我想做的就是更改原始文件中包含的功能之一,而无需将整个内容复制并粘贴到第二个JS文件中。 因此,例如,禁区JS可能具有以下功能: 我希望能够以某种方式在该函数中追加或添加一些JS代码。原因主要是在原始的不可触摸的JS中,该功能非常庞大,如果该JS得到更新,则我用它覆盖的功能将过时。 我不确定这

  • Spring非常出色地为注释提供了一个属性。我希望以编程方式提供带有时区的方法。 例如,假设我想在当地时间午夜打开洛杉矶和芝加哥大楼的灯。我想从我的存储库中获取建筑物。 时区可以在运行时提供还是以其他方式注入? 比如: 我知道方法不能接受任何参数,所以以这种方式提供时区是不行的。