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

时间:2012-05-07 14:26:37

标签: java security tomcat ssl-certificate keytool

我正在尝试在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 个答案:

答案 0 :(得分:16)

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

答案 1 :(得分:5)

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

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

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

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:0)

在我的案例中,我仔细检查了很多事情,然后才发现出了什么问题……我将证书添加到了不同的密钥库中,在链中添加了所有证书(这毫无意义,顺便说一句),我再次为我本人的理智,检查了序列号,甚至检查了下载的证书,以确保它具有所有正确的信息。

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