【问题标题】:Unsupported or unrecognized ssl-message exception at startHandshake after issuing STARTTLS command in a java program在 java 程序中发出 STARTTLS 命令后 startHandshake 出现不受支持或无法识别的 ssl 消息异常
【发布时间】:2018-01-09 07:38:08
【问题描述】:

我在没有使用 JavaMail API 的情况下用 java 编写了一个简单的电子邮件客户端,正如我从文档中看到的那样,似乎有必要将消息拆分为头部和正文,并在单个 MIME 部分中发送会使我的应用程序更加复杂。 SSL 端口 (465) 一切正常,但现在我尝试将客户端与仅支持端口 587 上的 STARTTLS 的不同提供程序一起使用。以下代码是我编写的用于测试对服务器的访问的类。

private static DataOutputStream dos; //Streams to communicate with the server
private static DataInputStream dis;

public static void main(String[] args) {
    try {
        Socket socket = new Socket("mail.arcor.de", 587);
        dos = new DataOutputStream(socket.getOutputStream());
        dis = new DataInputStream(socket.getInputStream());

        new Thread(() -> {readInputRun();}).start(); //see below, every input from the server is printed to the standard output.

        dos.write(("EHLO " + InetAddress.getLocalHost().getHostAddress() + "\r\n").getBytes());

        dos.write("STARTTLS\r\n".getBytes());

        SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(socket, "mail.arcor.de", 587, true);
        dos = new DataOutputStream(sslSocket.getOutputStream());
        dis = new DataInputStream(sslSocket.getInputStream());

        sslSocket.startHandshake();

        dos.write(("EHLO " + InetAddress.getLocalHost().getHostAddress() + "\r\n").getBytes());

        dos.write("QUIT\r\n".getBytes());

        Thread.sleep(5000);
        dis.close();
        dos.close();
        socket.close();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

private static void readInputRun() {
    try {
        int input;
        while((input = dis.read()) >= 0) {
            System.out.print((char) input);
        }
    } catch(Exception e) {
        e.printStackTrace();
    }
}

每次执行时,都会出现以下异常:

javax.net.ssl.SSLException: Unsupported or unrecognized SSL message
at java.base/sun.security.ssl.SSLSocketInputRecord.handleUnknownRecord(SSLSocketInputRecord.java:416)
at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:173)
at java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1031)
at java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
at java.base/sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1402)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1429)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at accessTest.AccessTestSTARTTLS.main(AccessTestSTARTTLS.java:31)

它在第 31 行被抛出,即 'sslSocket.startHandshake();',所以不知何故握手没有成功。我是否在错误的时间点或以错误的方式启动它?

提前感谢您的帮助。

编辑:正如评论中所建议的,我启用了日志并发现以下内容: httpslog。在第五行但最后一行有一个不可读的字符,显然是作为客户端问候的一部分,所以我的计算机发送了它。为什么会出现这种情况,我该如何阻止这种情况?

【问题讨论】:

  • 启用 https 日志并查看第一步会发生什么。见Debugging SSL/TLS Connections
  • 在进行 SSL./TLS 握手之前读取对 EHLO 和 STARTTLS 的响应。
  • 我已经在调试模式下逐行运行代码,并等待 readInputRun() 方法在启动握手之前完全读取服务器输入。没有改变任何东西。
  • 这不是重新发明轮子,而是要了解用于电子邮件传输的协议是如何工作的。
  • 好的,您的日志显示:您的readInputRun 线程不仅正在读取对 EHLO 和 STARTTLS 的(文本模式)响应,而且还在读取 ServerHello 消息的第一个字节(也就是十六进制 16 control-V) 在 JSSE 获取之前;由于 JSSE 得到一个乱码且无法识别的 ServerHello,它无法继续。我修改:阅读对 EHLO 和 STARTTLS 的响应并且只有那些响应不是 SSL/TLS 握手的任何部分

标签: java ssl starttls


【解决方案1】:

需要明确的是,此答​​案背后的主要目的是将问题标记为已回答,因为实际上可以在问题的 cmets 中找到解决方案。问题在于 readInputRun() 读取了部分 ServerHello,所以让读取线程在握手之前中断自己,并在解决后重新启动它:

dos.write("STARTTLS\r\n".getBytes());

SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(socket, "mail.arcor.de", 587, true);
dos = new DataOutputStream(sslSocket.getOutputStream());
dis = new DataInputStream(sslSocket.getInputStream());

sslSocket.startHandshake();

new Thread(() -> {readInputRun();}).start();

dos.write(("EHLO " + InetAddress.getLocalHost().getHostAddress() + "\r\n").getBytes());

dos.write("QUIT\r\n".getBytes());

和:

private static void readInputRun() {
    try {
        int input;
        String allInput = "";
        while((input = dis.read()) >= 0) {
            allInput += (char) input;
            System.out.print(allInput.charAt(allInput.length() - 1));

            if(allInput.endsWith("Start TLS\r\n")) {
                break;
            }
        }
    } catch(Exception e) {
        e.printStackTrace();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    相关资源
    最近更新 更多