首页 文章

将证书导入Java密钥库,JVM会忽略新的证书

提问于
浏览
18

我正在尝试在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"

当我启动Tomcat并启用SSL调试时,根据日志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使用了原始文件 .

4 回答

  • 5

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

  • 2

    检查是否存在具有相同CN信息但具有不同别名的密钥 .

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

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

  • 0

    你所描述的正是我在使用 cmd.exe 和普通用户时所获得的,尽管是Windows Server上的管理组成员 . 您必须在管理模式下启动 cmd.exe 才能将更改应用于cacerts文件 . 至少在Win2k8 OS上 .

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

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

  • 16

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

    我最终编写了一个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);
            }
        }
    }
    

相关问题