gpt4 book ai didi

sockets - 使用 STARTTLS 发送电子邮件

转载 作者:太空宇宙 更新时间:2023-11-03 13:53:05 26 4
gpt4 key购买 nike

我正在尝试使用 STARTTLS 命令发送电子邮件。我在 Gmail 中设置了一个测试帐户,并将其设置为仅接受具有 TLS 连接的入站电子邮件。

由于我不想深入的原因,我无法使用 JavaMail 或其他电子邮件库。

我已经能够使用 openssl 向这个测试帐户发送电子邮件。所以我知道该帐户已正确设置。

有效的示例:openssl s_client -starttls smtp -crlf -connect aspmx.l.google.com:25

我还能够使用包含 TLS 的 .Net 应用程序向该电子邮件帐户发送电子邮件。

我知道我的示例(下方)不是发送电子邮件的正确方式,因为我没有对服务器的响应使用react,但我认为这是创建示例来演示问题的好/简短方法。

我已经尝试了一段时间让这个工作。我尝试连接不同的端口(465、587、25),结果相似。我得到的错误是关于命令“AUTH LOGIN”的,但是我在之前的命令“EHLO aspmx.l.google.com”中已经没有从服务器得到任何响应。

我收到的错误是:“错误:软件导致连接中止:套接字写入错误”。

我是否在协商 TLS 连接以传输电子邮件的正确路径上,或者我是否遗漏了一些明显的东西?

任何帮助将不胜感激。

示例:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.bind.DatatypeConverter;

public class SendEmailWithTLSConnectionTest {

private static DataOutputStream dos;
private static BufferedReader out = null;

public static void main(String[] args) throws Exception
{
try
{
int delay = 1000;

String username = DatatypeConverter.printBase64Binary("leo@tls.calcium.co.nz".getBytes());
String password = DatatypeConverter.printBase64Binary("2wsxZAQ!".getBytes());

Socket sock = new Socket("aspmx.l.google.com", 25);

out = new BufferedReader(new InputStreamReader(sock.getInputStream()));

(new Thread(new Runnable()
{
public void run()
{
while(true)
{
try
{
if(out != null)
{
String line;

while((line = out.readLine()) != null)
{
System.out.println("SERVER: "+line);
}
}
}
catch (IOException e)
{
System.out.println("IOException SERVER! Error: " + e);

try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
})).start();

dos = new DataOutputStream(sock.getOutputStream());

send("EHLO aspmx.l.google.com\r\n");
Thread.sleep(delay * 5);

send("STARTTLS\r\n");
Thread.sleep(delay * 5);

SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(
sock,
sock.getInetAddress().getHostAddress(),
587,
true);

sslSocket.setUseClientMode(true);
sslSocket.setEnableSessionCreation(true);

// Thread.sleep(delay * 5);
// sslSocket.startHandshake();

send("EHLO aspmx.l.google.com\r\n");
Thread.sleep(delay * 5);

send("AUTH LOGIN\r\n");
Thread.sleep(delay * 5);

send(username + "\r\n");
Thread.sleep(delay * 5);

send(password + "\r\n");
Thread.sleep(delay * 5);

send("MAIL FROM: <leo@tls.calcium.co.nz>\r\n");
Thread.sleep(delay * 5);

send("RCPT TO: <leo@tls.calcium.co.nz>\r\n");
Thread.sleep(delay * 5);

send("DATA\r\n");
Thread.sleep(delay * 5);

send("Test 1 2 3");
Thread.sleep(delay * 5);

send("\r\n.\r\n");
Thread.sleep(delay * 5);

send("QUIT\r\n");
}
catch(Exception ex)
{
System.out.println("Exception when sending out test. Error: " + ex.getMessage());
}
}

private static void send(String s) throws Exception
{
dos.writeBytes(s);

System.out.println("CLIENT: "+s);
}
}

输出:

SERVER: 220 mx.google.com ESMTP on10si24036122pac.132 - gsmtp
CLIENT: EHLO aspmx.l.google.com
SERVER: 250-mx.google.com at your service, [103.23.17.19]
SERVER: 250-SIZE 35882577
SERVER: 250-8BITMIME
SERVER: 250-STARTTLS
SERVER: 250-ENHANCEDSTATUSCODES
SERVER: 250-PIPELINING
SERVER: 250-CHUNKING
SERVER: 250 SMTPUTF8
CLIENT: STARTTLS
SERVER: 220 2.0.0 Ready to start TLS
CLIENT: EHLO aspmx.l.google.com
Exception when sending out test. Error: Software caused connection abort: socket write error

最佳答案

Am I on the right path to negotiating a TLS connection to transmit an email or am I missing something obvious?

您缺少重要的步骤。

大多数 SMTP 服务器仅在端口 587 上实现 STARTTLS,但有些服务器也在端口 25 上实现它(Gmail 也是)。您必须解析服务器的 EHLO 响应以了解是否允许 STARTTLS

收到成功的 STARTTLS 响应后,您必须启动并完成 SSL/TLS 握手,然后再发送任何进一步的 SMTP 命令。您没有执行该步骤(您注释掉了对 SSLSocket.startHandshake() 的调用)。服务器期待你的握手问候,但你发送了一个新的 EHLO 命令,服务器将其解释为错误的握手并关闭连接,当你发送 AUTH LOGIN 命令。

此外,您正在连接到端口 25,但随后告诉 SSLSocketFactory 您连接到端口 587。您需要保持一致。

此外,一旦建立了 SSL/TLS session ,就不能再使用原来的 Socket 进行读取/发送。您会将未加密的数据直接发送到服务器,并读回服务器的原始加密数据。您必须改用 SSLSocket,这样它可以加密您发送的任何内容并解密您读取的任何内容。因此,您将不得不相应地重新初始化您的输入/输出流(并完全摆脱您的读取线程,因为它不属于此代码。SMTP 是同步的 - 发送命令,读取响应,发送命令,读取响应等)。

你需要更多类似的东西:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.bind.DatatypeConverter;

public class SendEmailWithTLSConnectionTest {

private static DataOutputStream dos;
private static BufferedReader out = null;

public static void main(String[] args) throws Exception
{
try
{
String username = DatatypeConverter.printBase64Binary("leo@tls.calcium.co.nz".getBytes());
String password = DatatypeConverter.printBase64Binary("2wsxZAQ!".getBytes());

Socket sock = new Socket("aspmx.l.google.com", 587);

out = new BufferedReader(new InputStreamReader(sock.getInputStream()));
dos = new DataOutputStream(sock.getOutputStream());

if (sendCmd("EHLO aspmx.l.google.com") == 250)
{
// TODO: parse response
if (true/*response contains STARTTLS capability*/)
{
sendCmd("STARTTLS", 220);

SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(
sock,
sock.getInetAddress().getHostAddress(),
sock.getPort(),
true);

sslSocket.setUseClientMode(true);
sslSocket.setEnableSessionCreation(true);

System.out.println("CLIENT: securing connection");
sslSocket.startHandshake();
// on an initial handshake, startHandshake() blocks the calling
// thread until the handshake is finished...
System.out.println("CLIENT: secured");

sock = sslSocket;
out = new BufferedReader(new InputStreamReader(sock.getInputStream()));
dos = new DataOutputStream(sock.getOutputStream());

sendCmd("EHLO aspmx.l.google.com", 250);
}
}
else
sendCmd("HELO aspmx.l.google.com", 250);

sendCmd("AUTH LOGIN", 334);
if (sendCmd(username, new int[]{235, 334}) == 334)
sendCmd(password, 235);

sendCmd("MAIL FROM: <leo@tls.calcium.co.nz>", 250);
sendCmd("RCPT TO: <leo@tls.calcium.co.nz>", new int[]{250, 251});
sendCmd("DATA", 354);

sendLine("From: <leo@tls.calcium.co.nz>");
sendLine("To: <leo@tls.calcium.co.nz>");
sendLine("Subject: test");
sendLine("");
sendLine("Test 1 2 3");
sendCmd(".", 250);

sendCmd("QUIT", 221);
}
catch(Exception ex)
{
System.out.println("Exception when sending out test. Error: " + ex.getMessage());
}
}

private static void sendLine(String s) throws Exception
{
dos.writeBytes(s + "\r\n");
System.out.println("CLIENT: " + s);
}

private static int sendCmd(String s) throws Exception
{
sendLine(s);

String line = out.readLine();
System.out.println("SERVER: " + line);

int respCode = Integer.parseInt(line.substring(0, 3));
while ((line.length() > 3) && (line.charAt(3) == '-'))
{
line = out.readLine();
System.out.println("SERVER: " + line);
}

return respCode;
}

private static int sendCmd(String s, int expectedRespCode) throws Exception
{
int respCode = sendCmd(s);
checkResponse(respCode, expectedRespCode);
return respCode;
}

private static int sendCmd(String s, int[] expectedRespCodes) throws Exception
{
int respCode = sendCmd(s);
checkResponse(respCode, expectedRespCodes);
return respCode;
}

private static void checkResponse(int actualRespCode, int expectedRespCode)
{
if (actualRespCode != expectedRespCode)
throw new Exception("command failed");
}

private static void checkResponse(int actualRespCode, int[] expectedRespCodes)
{
for (int i = 0; i < expectedRespCodes.length; ++i)
{
if (actualRespCode == expectedRespCode)
return;
}
throw new Exception("command failed");
}
}

关于sockets - 使用 STARTTLS 发送电子邮件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34215231/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com