重新定义默认SSL上下文时,找不到Java密钥库

时间:2014-04-21 20:18:42

标签: java ssl jsse

假设我想要在我的Java应用程序中通过SSL进行所有连接时询问用户权限,以及遇到不受信任或过期的服务器证书(就像大多数Web浏览器那样)。

看来,最自然的方法是在应用程序启动时替换默认的SSL上下文。

以下是最小的工作示例:

public class ClientAuthentication {
    public static final String SERVER_URL = "https://my.test.server";

    public static void main(String[] args) throws Exception {
        SSLContext defaultContext = SSLContext.getInstance("TLS");
        defaultContext.init(null, new TrustManager[]{new MyX509TrustManager()}, null);
        // defaultContext.init(null, null, null);
        SSLContext.setDefault(defaultContext);
        HttpURLConnection connection = (HttpURLConnection) new URL(SERVER_URL).openConnection();
        System.out.println(connection.getResponseCode());
    }

    private static class MyX509TrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            throw new UnsupportedOperationException("Won't be called by client");
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException {
            if (userAcceptsCertificate(chain)) {
                System.out.format("Certificate '%s' accepted%n", chain[0].getIssuerX500Principal().getName());
            }
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }

        private boolean userAcceptsCertificate(X509Certificate[] x509Certificates) {
            // ...
            return true;
        }
    }
}

不幸的是,当服务器需要客户端身份验证时,它不起作用。甚至,当通过-Djavax.net.ssl.keyStore VM选项正确指定包含客户端证书的密钥库时,连接失败 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure,因为根本没有发送客户端证书(在Wireshark中跟踪)。

这种行为有点令人惊讶,因为JavaDoc for SSLContext#init(KeyManager[], TrustManager[], SecureRandom)表示传递null而不是密钥管理器或信任管理器意味着,将执行默认查找过程。

Сontradientily,如果我将null而不是数组与我的自定义信任管理器作为第二个参数传递给init并指定包含服务器证书的JKS信任存储,使用
-Djavax.net.ssl.trustStore VM参数,服务器证书将在那里找到并成功接受

那么,您能否建议一种重新定义默认SSL上下文的方法,仅替换信任管理器并保留关键管理器搜索的默认行为?或者可能是找到通过VM选项指定的密钥管理器的一种强大方法。

备注

  1. 如果我根本没有自定义默认SSL上下文,则连接成功,即VM选项中的所有路径,密码和商店类型都是正确的。

  2. 我无法以任何方式致电SSLContext.getDefault()SSLContext.getInstance("Default"),因为默认情境只能初始化一次。

1 个答案:

答案 0 :(得分:1)

您已使用空键管理器初始化了上下文。这意味着没有密钥管理器。它并不意味着有一个默认密钥管理器。因此,当请求密钥管理器的功能时,没有任何反应。这些功能包括提供客户端证书。

Javadoc错了。 IBM JSSE的行为与此处描述的相同,但Sun / Oracle JSSE没有,并且从未有过。