【问题标题】:using KeyStore with .crt将 KeyStore 与 .crt 一起使用
【发布时间】:2016-08-09 12:55:22
【问题描述】:

我需要将应用程序与强制使用 https 的外部 Web 服务集成。此 Web 服务的作者为我提供了 .crt 文件,我应该使用它来发出 https 请求。经过一番调查,我发现以下代码使用 KeyStore 类进行安全 https 访问:

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream instream = new FileInputStream(new File(file));
        try {
            trustStore.load(instream, password.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext =
                SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
        SSLConnectionSocketFactory sslsf =
                new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null,
                                               BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        HttpClients.custom().setSSLSocketFactory(sslsf).build();

在此代码中,KeyStore 需要输入流以及 密码 trustStore.load(instream, password.toCharArray());。但是,据我了解,使用 .crt 文件时我们不需要密码。所以这种加载证书的方式不适合我。同时,从我目前的发现来看,我在这里提供的代码是配置 HttpClient 以使用 SSL 证书的唯一方法。是否有任何解决方法来配置 HttpClient 以使用 .crt 证书?

谢谢,

安德烈

【问题讨论】:

    标签: java ssl https secure-crt


    【解决方案1】:

    我假设 Web 服务正在为您提供自签名证书(即未由知名 CA 签名)。如果它已经由 Java 的 cacerts 文件中的知名 CA 签名,那么您无需执行任何操作。

    否则,您有两种选择:

    1. 将证书导入全局 cacerts 密钥库
    2. 使用特定于应用程序的密钥库启动您的应用程序

    无论哪种情况,您首先需要将 crt 文件转换为 Java 使用的 jks 密钥库。你可以这样做:

    $ keytool -import -keystore mykeystore.jks -storepass horsestaple
    

    请注意,keytool 需要提供密码(上面的horsestaple)才能创建 jks 存储。你可以把任何东西放在那里;正如您所提到的,公共网站证书不需要密码保护,它们毕竟是公开的。

    如果您使用选项 1,请备份您的 cacerts 并提供 cacerts 文件而不是 mykeystore.jks。有关 cacerts 的位置,请参阅下面的链接。对于这个选项,一切就绪,您的应用程序应该无需任何额外配置即可通过 HTTPS 连接到 Web 服务,因为 Java 默认加载 cacerts。

    如果您正在执行选项 2,这可能至少在测试阶段是首选,您需要使用此参数运行您的应用程序:

    -Djavax.net.ssl.trustStore=mykeystore.jks
    

    这是一个 JVM 参数,因此请适当地提供它。这取决于您运行应用程序的方式。

    请注意,在这种情况下您将只有导入的证书,因此您的其他 HTTPS 连接将无法正常工作。您可以通过首先将标准 cacerts 复制到临时位置,将密钥导入其中并在上面的命令中使用它来避免这种情况。这将为您提供所有标准证书,以及您需要的证书。

    选项 2 的一个小缺点是,如果添加或撤销新证书,您的特定于应用程序的密钥库将不会更新。如果这是一个问题,您可以即时合并密钥库,例如:

    在任何一种情况下,您现在都应该能够执行标准的 URL 获取,例如此处给出的示例:

    即:

    final URL url = new URL("https://example.com");
    try(final InputStream in = url.openStream()){
      //…
    }
    

    更多信息在这里:

    • Keytool 和一般证书信息

    https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html

    • 证书位置

    http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html

    • 导入自签名证书的更多选项

    How to properly import a selfsigned certificate into Java keystore that is available to all Java applications by default?

    希望这会有所帮助。

    【讨论】:

    • 第三种选择是直接将信任库与客户端 API 一起使用,这就是 OP 看起来正在做的事情。这比您最初提出的两个选项中的任何一个都好,因为它不会干扰信任存储的任何其他使用。
    • @ChristopherSchultz 同意,如果 OP 想使用原始解决方案,当然可以。我想这取决于用例,如果分布在多个应用程序中,更新全局 cacerts 可能会更容易。
    【解决方案2】:

    除非他们为您提供他们的 .crt 文件,该文件应该用于验证 连接他们,这表明他们正在使用自签名证书,他们没有为您提供任何有用的东西。如果需要一个证书作为客户端,那么您首先需要的是一个公钥/私钥对,您可以从中生成一个CSR,然后对它进行签名。除了签名证书之外,没有其他人可以安全地提供任何内容。

    如果他们提供了自己的(自签名?)证书,您需要通过带有-trustcacerts 选项的keytool 将其加载到信任库,而不是密钥库中,然后告诉 Java 使用该信任库,或者通过 javax.net.ssl.trustStore 系统属性或通过构建您自己的 TrustManager 并将其提供给自定义 SSLContext,如 JSSE 参考指南中所述。

    【讨论】:

      猜你喜欢
      • 2017-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-10
      • 1970-01-01
      相关资源
      最近更新 更多