【问题标题】:SSLSocket ignores domain mismatchSSLSocket 忽略域不匹配
【发布时间】:2013-07-31 14:05:32
【问题描述】:

我正在从 SSL 套接字读取,但主机与证书不匹配(例如,host = "localhost")。我预计会出现异常,但以下代码可以愉快地与远程服务器通信,没有任何问题。

try (
    final Socket socket = SSLSocketFactory.getDefault().createSocket(host, port);
    final OutputStream os = socket.getOutputStream();
    final InputStream is = socket.getInputStream()) {

    os.write(("HEAD / HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n").getBytes());
    os.flush();

    final byte[] bytes = new byte[1024];
    int n;
    while ((n = is.read(bytes)) != -1) {
        System.out.print(new String(bytes, 0, n));
    }
    System.out.println();
} catch (final IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

因此我尝试了另一种方法:

try {
    final HttpURLConnection conn = (HttpURLConnection) new URL("https://" + host + ":" + port + "/").openConnection();

    try (InputStream is = conn.getInputStream()) {
        IOUtils.copy(is, System.out);
    } catch (final IOException e1) {
        try (InputStream es = conn.getErrorStream()) {
            if (es != null) {
                IOUtils.copy(es, System.out);
            }
        }
    }
} catch (final IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

不幸的是,我仍然没有收到 SSL 异常,只是在日志中发出警告: 2013-07-31 16:02:27,182 WARN nio - javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

如果证书不匹配,如何获取 SSL 异常?

【问题讨论】:

    标签: java ssl


    【解决方案1】:

    SSL/TLS 协议规范是模块化的,并且与用于验证远程主机的规范分离。这些其他规范分为两类:验证证书本身是否可信(RFC 3280/5280)和验证证书中的身份(RFC 6125,或 RFC 2818 用于 HTTPS)。

    JSSE 在SSLSocket(或SSLEngine)API 中集成了SSL 协议和证书验证,但不处理标识符的验证(这同样重要)。

    这主要是因为SSLSocket/SSLEngine可以适用于任何应用协议(例如HTTP、IMAP、SMTP、LDAP等),但验证标识符的规则不同规范(有小的变化),直到 RFC 6125(仍然是最近的)。

    HttpsURLConnection 处理两者,因为它还使用遵循 HTTPS 规范(RFC 2818,第 3.1 节)的HostnameVerifier。这与SSLSocket/SSLEngine API 分开完成。 对于其他协议,您可能需要实现协议规范所说的内容。

    话虽如此,从 Java 7 开始,作为SSLSocket/SSLEngine API 的一部分,有一种机制可以直接验证证书的身份。

    SSLParameters sslParams = new SSLParameters();
    sslParams.setEndpointIdentificationAlgorithm("HTTPS");
    sslSocket.setSSLParameters(sslParams);
    

    如果主机名不匹配,使用它应该会引发异常。

    HTTPS 与 RFC 6125 中更统一的规范之间没有重大区别(除了后者认为 IP 地址超出范围的事实)。即使您不使用 HTTPS,将其标识规范用于其他协议通常仍然有意义。 (也许“RFC 6125”端点识别算法可能会出现在 Java 的更高版本中。)

    【讨论】:

    • 感谢您提供详细信息。
    【解决方案2】:

    主机名匹配不是 SSL 的一部分,它是 HTTPS 的一部分。参见例如 javax.net.ssl.HostnameVerifier。

    您引用的日志表明 HttpsURLConnection 引发了 SSLException

    【讨论】:

    • 在追踪该日志后,我发现它记录在org.eclipse.jetty.io.nio.SelectChannelEndPoint 中(这个测试用例和服务器在同一个JVM上)。我还发现从 connection.getInputStream() 读取时抛出的IOException 实际上是javax.net.ssl.SSLHandshakeException,这正是我需要的:-)
    • 什么异常?你没有在你的问题中说什么。
    猜你喜欢
    • 2021-07-03
    • 2010-12-12
    • 2011-02-10
    • 2017-03-11
    • 2021-01-21
    • 1970-01-01
    • 2012-07-02
    • 2012-07-10
    • 2019-01-27
    相关资源
    最近更新 更多