【问题标题】:Imported certificate to Java keystore, JVM ignores the new cert将证书导入 Java 密钥库,JVM 忽略新证书
【发布时间】:2018-09-07 00:42:35
【问题描述】:

我正在尝试让一个在 Tomcat 6 上运行的应用程序通过 SSL 连接到 LDAP 服务器。

我使用以下方法将服务器证书导入密钥库:

C:\Program Files\Java\jdk1.6.0_32\jre\lib\security>keytool -importcert -trustcacerts -file mycert -alias ca_alias -keystore "c:\Program Files\Java\jdk1.6.0_32\jre\lib\security\cacerts"

当我在 SSL 调试打开的情况下启动 Tomcat 时,根据日志 Tomcat 正在使用正确的证书文件:

trustStore is: C:\Program Files\Java\jdk1.6.0_32\jre\lib\security\cacerts

但是,Tomcat 并没有添加我刚刚导入的证书 - cacerts 文件中的所有其他证书都打印到日志中 - 并且连接失败:

handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

重新启动 Tomcat 没有帮助。我已使用 keytool -list 命令验证文件中确实存在新证书。

为什么 Tomcat 一直忽略我的新证书?

编辑:

问题似乎是由 Windows 7 VirtualStore 引起的。 Keytool 创建了 cacert 文件的新副本,Tomcat 使用了原始文件。

【问题讨论】:

  • 文件mycert 是否包含整个证书链? Java 希望整个信任路径都在存储中。
  • 只是一个简单的建议,转到您的C:\Users\YourAccountName,文件名.keystore 将在那里,打开它并从中删除您以前的文件,然后再次执行您所做的操作。希望这会为你解决问题:-)
  • @Romain 不,可信证书与链无关。任何作为受信任证书导入的证书都被视为受信任的根证书。
  • @tputkonen :我的错误,我的建议没有奏效,似乎我没有想法 :( 尝试,再试一次,这就是我能说的,你很快就会成功: -) 我在此向您致以最美好的祝愿:-)
  • @emboss (抱歉此评论断章取义,我将删除它。)对此有什么想法吗? meta.stackexchange.com/q/131414/148833

标签: java security tomcat ssl-certificate keytool


【解决方案1】:

将证书导入密钥库后,JVM 需要重新启动。

【讨论】:

    【解决方案2】:

    查看是否存在具有相同CN信息但别名不同的键。

    我之前尝试导入较新版本的证书但将旧版本留在密钥库中时遇到过类似问题。我的 Java 程序会简单地在密钥库中找到第一个匹配的 CN 密钥(这是旧的过期密钥)并尝试使用它,即使有一个新的也与 CN 匹配。

    还要确保密钥库中存在验证根证书(和中间证书,如果适用)。如果您要针对 Verisign 或 Globalsign 等主要安全提供商之一进行身份验证,他们通常会为您提供根证书和中间证书。如果这些证书已经存在于密钥库中,请确保它们仍然有效。您需要拥有从您的个人证书一直到身份验证链的所有证书,一直到根,存在于您的密钥库中,以便它了解如何验证您的凭据。

    【讨论】:

    • 这似乎不是问题,但感谢您的建议
    • 我已经编辑了我的答案。您是否检查过您的证书颁发机构的根证书是否存在于密钥库中?
    • 您不需要在信任库中已经存在的证书之上添加证书。商店中的任何证书都是可信的,无需进一步验证。但是别名的好建议!
    【解决方案3】:

    您所描述的正是我在使用cmd.exe 和普通用户(尽管是 Windows Server 上的管理组成员)时所得到的。您必须在管理模式下启动 cmd.exe 才能将更改应用到 cacerts 文件。至少在 Win2k8 操作系统上。

    如果您不这样做,插入符号将在keytool.exe -list 视图中显示新添加的证书,但 Tomcat 不会看到它们。不知道为什么会这样。但是,当您使用以管理员身份启动的 cmd.exe 添加它时,Tomcat 可以使用新添加的证书。

    您也可以使用Djavax.net.debug="ssl,handshake" 查看 Tomcat 从 cacerts 文件中读取的内容。

    【讨论】:

    【解决方案4】:

    在我的情况下,我查看了很多东西,然后才弄清楚出了什么问题......我将证书添加到不同的密钥库中,我添加了链中的所有证书(顺便说一句毫无意义),我再次下载了证书我自己的理智并检查了序列号,甚至检查了下载的证书以确保它具有所有正确的信息。

    我最终编写了一个 TLS 验证客户端应用程序来调试问题。我连接的远程服务器不仅只支持 TLS 1.2(在我的 Java 7 版本中默认禁用),而且该服务器也不支持在我的客户端中启用的任何密码。事实证明,Java 7 启用了不到一半的受支持密码,其中许多确实不安全,并且一些最安全的密码被禁用。

    经过一些交叉检查,我得出了以下 TLS 1.2 支持的安全密码的有序列表:

    new String[] {
        "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",
        "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
        "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
        "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
        "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
        "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
        "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
        "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
        "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
        "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
        "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
        "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256",
        "TLS_DHE_RSA_WITH_AES_256_CCM",
        "TLS_DHE_RSA_WITH_AES_128_CCM",
        "TLS_DHE_PSK_WITH_AES_256_CCM",
        "TLS_DHE_PSK_WITH_AES_128_CCM",
        "TLS_CHACHA20_POLY1305_SHA256",
        "TLS_AES_256_GCM_SHA384",
        "TLS_AES_128_GCM_SHA256",
        "TLS_AES_128_CCM_SHA256"
    }
    

    如果周围有任何加密专家,请随时更新此列表。我使用Qualys SSL Labsthis Information Security SE answerIANA 作为我的来源。

    对于那些想要我使用的源代码示例的人,请参见下文。我使用的是 Apache Commons HttpClient 3.0,因此您可能需要下载以下二进制文件:

    TLS12SocketFactory.java

    import java.io.*;
    import java.net.*;
    import java.util.*;
    import org.apache.commons.httpclient.params.HttpConnectionParams;
    import org.apache.commons.httpclient.protocol.*;
    import org.apache.commons.lang.StringUtils;
    
    public class TLS12SocketFactory implements SecureProtocolSocketFactory {
    
            private final SecureProtocolSocketFactory base;
            public TLS12SocketFactory()
            {
                this.base = (SecureProtocolSocketFactory)Protocol.getProtocol("https").getSocketFactory();
            }
            private Socket acceptOnlyTLS12(Socket socket)
            {
                if(socket instanceof javax.net.ssl.SSLSocket) {
                    final javax.net.ssl.SSLSocket s = (javax.net.ssl.SSLSocket)socket;
    
                    // Set TLS 1.2
                    s.setEnabledProtocols(new String[]{ "TLSv1.2" });
    
                    // Using recommended ciphers from https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4
                    List<String> recommended = new ArrayList(Arrays.asList(new String[]{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_CCM", "TLS_DHE_RSA_WITH_AES_128_CCM", "TLS_DHE_PSK_WITH_AES_256_CCM", "TLS_DHE_PSK_WITH_AES_128_CCM", "TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_SHA256" }));
                    recommended.retainAll(Arrays.asList(s.getSupportedCipherSuites()));
                    if(recommended.size() == 0) {
                        System.err.println("No supported modern ciphers. Update crypto policy or install JCE Unlimited Strength Jurisdiction Policy files." + System.lineSeparator());
                    } else if(recommended.size() < 3) {
                        System.out.println("Few supported modern ciphers. It's recommended to update crypto policy or install JCE Unlimited Strength Jurisdiction Policy files." + System.lineSeparator());
                    }
                    s.setEnabledCipherSuites(recommended.toArray(new String[0]));
    
                    // Log matched cipher and cert
                    s.addHandshakeCompletedListener(new javax.net.ssl.HandshakeCompletedListener() {
                        @Override
                        public void handshakeCompleted(javax.net.ssl.HandshakeCompletedEvent hce) {
                            String print = s.getInetAddress().getHostName() + System.lineSeparator() + hce.getCipherSuite() + System.lineSeparator();
                            try {
                                for(java.security.cert.Certificate cert : hce.getPeerCertificates()) {
                                    List<String> certStrings = Arrays.asList(cert.toString().split("\r?\n"));
                                    for(int line = 0; line < certStrings.size(); line++) {
                                        if(certStrings.get(line).startsWith("Certificate Extensions:")) {
                                            print += System.lineSeparator() + StringUtils.join(certStrings.subList(2, line-1), System.lineSeparator()) + System.lineSeparator();
                                            break;
                                        }
                                    }
                                }
                            } catch (javax.net.ssl.SSLPeerUnverifiedException ex) {
                                print += "Non-certificate based cipher used" + System.lineSeparator();
                            }
                            System.out.println(print);
                        }
                    });
                }
                return socket;
            }
    
            @Override
            public Socket createSocket(String host, int port) throws IOException
            {
                return acceptOnlyTLS12(base.createSocket(host, port));
            }
            @Override
            public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException
            {
                return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort));
            }
            @Override
            public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException
            {
                return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort, params));
            }
            @Override
            public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException
            {
                return acceptOnlyTLS12(base.createSocket(socket, host, port, autoClose));
            }
    }
    

    Main.java

    import java.io.*;
    import java.security.*;
    import java.security.cert.*;
    import java.util.*;
    import org.apache.commons.httpclient.protocol.Protocol;
    import org.apache.commons.httpclient.*;
    import org.apache.commons.httpclient.cookie.CookiePolicy;
    import org.apache.commons.httpclient.methods.*;
    import org.apache.commons.httpclient.params.HttpClientParams;
    
    public class Main {
        public static void main(String[] args) {
            List<java.net.URI> locations = new ArrayList<>();
            for(String arg : args) {
                java.net.URI location = java.net.URI.create(arg);
                if(location.isAbsolute() && location.getScheme().equals("https")) {
                    locations.add(location);
                } else {
                    System.out.println("Skipping invalid URL: " + arg);
                }
            }
            System.out.println("Connecting to URL's");
            System.out.println();
            System.out.println("-------------------------");
    
            TLS12SocketFactory tls12factory = new TLS12SocketFactory();
            Protocol.registerProtocol("httpss", new Protocol("httpss", tls12factory, 443));
            for(java.net.URI location : locations) {
                System.out.println();
                try {
                    // Form request
                    String tls12url = location.toString().replaceFirst("^https:", "httpss:");
                    HttpMethod method = new HeadMethod(tls12url);
    
                    // Send request
                    HttpClientParams params = new HttpClientParams();
                    params.setParameter(HttpClientParams.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
                    new HttpClient(params).executeMethod(method);
                    method.setFollowRedirects(true);
    
                    // Print response
                    System.out.println(location.toString());
                    System.out.println(method.getStatusLine().toString());
                } catch (javax.net.ssl.SSLHandshakeException ex) {
                    System.out.println("There was an error making a secure connection to " + location.getHost());
                    ex.printStackTrace(System.out);
                } catch (HttpException ex) {
                    System.out.println("There was an error with the external webpage");
                    ex.printStackTrace(System.out);
                } catch (Exception ex) {
                    System.out.println("Could not complete request");
                    ex.printStackTrace(System.out);
                }
            }
            System.out.println();
            System.out.println("-------------------------");
            System.out.println();
            try {
                // Load supported SSL Ciphers
                System.out.println("Supported ciphers");
                System.out.println();
                System.out.println("-------------------------");
                System.out.println();
                javax.net.ssl.SSLSocket socket = (javax.net.ssl.SSLSocket)tls12factory.createSocket("www.google.com", 443);
                for(String cipher : socket.getSupportedCipherSuites()) {
                    System.out.println(cipher);
                }
                System.out.println();
                System.out.println("-------------------------");
                System.out.println();
    
                // Load enabled SSL Ciphers
                System.out.println("Enabled ciphers");
                System.out.println();
                System.out.println("-------------------------");
                System.out.println();
                for(String cipher : socket.getEnabledCipherSuites()) {
                    System.out.println(cipher);
                }
                System.out.println();
                System.out.println("-------------------------");
                System.out.println();
    
                // Load the JDK's cacerts keystore file
                String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
                System.out.println("Loading keystore");
                System.out.println(filename);
                System.out.println();
                System.out.println("-------------------------");
                System.out.println();
                FileInputStream is = new FileInputStream(filename);
                KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
                String password = "changeit";
                keystore.load(is, password.toCharArray());
    
                // This class retrieves the most-trusted CAs from the keystore
                PKIXParameters params = new PKIXParameters(keystore);
    
                // Get the set of trust anchors, which contain the most-trusted CA certificates
                for (TrustAnchor ta : params.getTrustAnchors()) {
                    // Print certificate
                    System.out.println(ta.getTrustedCert());
                }
            } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | IOException ex) {
                System.out.println("Could not load keystore");
                ex.printStackTrace(System.out);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-02-12
      • 1970-01-01
      • 2013-01-02
      • 2016-10-26
      • 1970-01-01
      • 2014-03-01
      • 2011-02-06
      • 2011-05-18
      • 1970-01-01
      相关资源
      最近更新 更多