【发布时间】: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 握手的任何部分。