0

STARTTLS コマンドでメールを送信しようとしています。Gmail でテスト アカウントをセットアップし、TLS 接続による受信メールのみを受け入れるようにセットアップしました。

入りたくない理由により、私は JavaMail やその他の電子メール ライブラリを使用できません。

openssl を使用して、このテスト アカウントにメールを送信できました。これで、アカウントが正しく設定されていることがわかりました。

うまくいった例: openssl s_client -starttls smtp -crlf -connect aspmx.l.google.com:25

また、TLS を組み込んだ .Net アプリケーションを使用して、このメール アカウントにメールを送信することもできました。

サーバーの応答に反応していないため、私の例 (以下) が電子メールを送信する適切な方法ではないことはわかっていますが、これは問題を示す例を作成するための良い/短い方法だと思いました.

私はこれを機能させるためにしばらく努力してきました。異なるポート (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
4

2 に答える 2

3

電子メールを送信するために TLS 接続をネゴシエートするための正しい道を進んでいますか、それとも明らかな何かが欠けていますか?

重要な手順がありません。

ほとんどの SMTP サーバーSTARTTLSはポート 587 にのみ実装していますが、一部のサーバーはポート 25 にも実装しています (Gmail はそうしています)。が許可EHLOされているかどうかを確認するには、サーバーの応答を解析する必要があります。STARTTLS

正常な応答を受信しSTARTTLSたら、SSL/TLS ハンドシェイクを開始して完了する必要があります。その後、さらに SMTP コマンドを送信します。そのステップを実行していません (への呼び出しをコメントアウトしましたSSLSocket.startHandshake())。サーバーはあなたからのハンドシェイク ハローを期待していますが、EHLO代わりに新しいコマンドを送信しています。サーバーはこれを不適切なハンドシェイクと解釈し、接続を閉じますAUTH LOGIN。コマンドを送信すると、そのことが報告されます。

また、ポート 25 に接続していますが、SSLSocketFactory代わりにポート 587 に接続していることを伝えています。一貫性を保つ必要があります。

Socketまた、SSL/TLS セッションを確立すると、元のセッションを読み取り/送信に使用できなくなります。暗号化されていないデータをサーバーに直接送信し、サーバーの生の暗号化されたデータを読み戻します。代わりにを使用する必要があります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");
  }
}
于 2015-12-11T03:38:01.043 に答える
0

上記の回答を実際のバージョンに調整しました。

これを下に挿入するので、他の誰かに役立つかもしれません。ご指導いただきました Remy Lebeau 氏に感謝いたします。

package com.mailprimer.smtp.sender;

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("XXXXXXXXXX".getBytes());

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

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

   int responseCode = sendCommand("EHLO aspmx.l.google.com", 250);

   if ( responseCode == 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("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 sendCommand(String s, int expectedRespCode) throws Exception
  {
      sendLine(s);

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

  // Need to wait a little longer until the other response is finished.
  Thread.sleep(100);

  int respCode = Integer.parseInt(line.substring(0, 3));

  if(expectedRespCode > 0)
  {
      while ((line.length() > 3) && ((line.charAt(3) == '-') || respCode != expectedRespCode))
      {
          line = out.readLine();
          System.out.println("SERVER: " + line);   

          respCode = Integer.parseInt(line.substring(0, 3));

          // Need to wait a little longer until the other response is finished.
          Thread.sleep(100);
      }
  }

  return respCode;


}



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

  checkResponse(respCode, expectedRespCode);

  return respCode;


}

  private static int sendCmd(String s, int[] expectedRespCodes) throws Exception
  {
      int respCode = sendCommand(s, 0);

  checkResponse(respCode, expectedRespCodes);

  return respCode;


}

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



private static void checkResponse(int actualRespCode, int[] expectedRespCodes) throws Exception
  {
      for (int i = 0; i < expectedRespCodes.length; ++i)
      {
          int expectedRespCode = expectedRespCodes[i];

          if (actualRespCode == expectedRespCode)
              return;
      }

      throw new Exception("command failed");
  }
}
于 2015-12-14T02:14:17.013 に答える