ここに特有の問題があります。意図は、office365のSMTP経由でメールを送信することです。
ローカルのラップトップから一貫してメールを送信できました。
しかし、(ファイアウォールの背後にある)サーバーにデプロイした場合、成功しません。 注:サーバーでsmtp.office365.com
のポート587にアクセスして確認します。これは、ローカルコンピュータから正常に機能するためのプロパティです。
Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.connectiontimeout", MAIL_TIMEOUT);
props.put("mail.smtp.timeout", MAIL_TIMEOUT);
props.put("mail.debug", true);
this.session = Session.getInstance(props);
session.setDebug(true);
Transport transport = session.getTransport();
transport.connect("smtp.office365.com", 587, email, pass);
しかし、サーバーで失敗します。サーバーのデバッグログは次のとおりです。
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.Sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to Host "smtp.office365.com", port 587, isSSL false
220 PN1PR0101CA0017.Outlook.office365.com Microsoft ESMTP MAIL Service ready at Fri, 28 Jun 2019 06:39:41 +0000
DEBUG SMTP: connected to Host "smtp.office365.com", port: 587
EHLO appqa
250-PN1PR0101CA0017.Outlook.office365.com Hello [182.73.191.100]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "157286400"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 SMTP server ready
Exception in thread "main" javax.mail.MessagingException: Could not convert socket to TLS;
nested exception is:
Java.net.SocketTimeoutException: Read timed out
at com.Sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.Java:2155)
at com.Sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.Java:752)
at javax.mail.Service.connect(Service.Java:366)
at com.company.app.MailReader.getTransport(MailReader.Java:269)
at io.vavr.control.Try.of(Try.Java:75)
at com.company.app.MailReader.<init>(MailReader.Java:59)
at com.company.services.MailService.getNewMailReader(MailService.Java:82)
at com.company.services.MailService.start(MailService.Java:46)
at com.company.Main.main(Main.Java:34)
Caused by: Java.net.SocketTimeoutException: Read timed out
at Java.net.SocketInputStream.socketRead0(Native Method)
at Java.net.SocketInputStream.socketRead(SocketInputStream.Java:116)
at Java.net.SocketInputStream.read(SocketInputStream.Java:171)
at Java.net.SocketInputStream.read(SocketInputStream.Java:141)
at Sun.security.ssl.InputRecord.readFully(InputRecord.Java:465)
at Sun.security.ssl.InputRecord.readV3Record(InputRecord.Java:593)
at Sun.security.ssl.InputRecord.read(InputRecord.Java:529)
at Sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.Java:975)
at Sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.Java:1367)
at Sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.Java:1395)
at Sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.Java:1379)
at com.Sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.Java:626)
at com.Sun.mail.util.SocketFetcher.startTLS(SocketFetcher.Java:553)
at com.Sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.Java:2150)
... 8 more
問題はファイアウォールの特定のルールでした。
ファイアウォールでルールを削除すると、この問題が修正されました。機能させるために特定のコードを変更する必要はありませんでした。
サーバーにローカルコンピュータと同じ証明書のセットがあるかどうかを確認します。
サーバーからの220応答は、TLSセッションがすでに確立されていることを意味するのではなく、クライアントがそれをネゴシエートすることを意味します。
STARTTLSコマンドに対する220応答を受信した後、クライアントは他のSMTPコマンドを与える前にTLSネゴシエーションを開始する必要があります。 STARTTLSコマンドを発行した後、クライアントがなんらかのエラーにより実際にTLSハンドシェイクを開始できないことがわかった場合は、接続を中止する必要があります(SHOULD)。 (RFC 3207から)
この時点で、証明書が見つからないことが最も可能性の高い問題です。
サーバーでJREバージョンを確認し、ローカルコンピューターのバージョンと比較します。
同じコードが異なるマシンで異なる動作をするため、これは環境に関連する問題です。全体像がなければ、確実に答えることはできません。しかし、私はさらなる調査のためのいくつかの洞察を提供したいと思います。私の分析は次のとおりです:
_Caused by: Java.net.SocketTimeoutException: Read timed out
at Java.net.SocketInputStream.socketRead0(Native Method)
at Java.net.SocketInputStream.socketRead(SocketInputStream.Java:116)
...
at Sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.Java:1367)
...
_
つまり、ソケットは確立されましたが、ソケットをTLSに変換するハンドシェイクフェーズが失敗しました。証明書が有効でない場合、ハンドシェイク後に報告されます。SocketFetcher.Javaクラスのコードを見てみましょう。
_ /*
* Force the handshake to be done now so that we can report any
* errors (e.g., certificate errors) to the caller of the startTLS
* method.
*/
sslsocket.startHandshake();
/*
* Check server identity and trust.
*/
boolean idCheck = PropUtil.getBooleanProperty(props,
prefix + ".ssl.checkserveridentity", false);
if (idCheck)
checkServerIdentity(Host, sslsocket);
if (sf instanceof MailSSLSocketFactory) {
MailSSLSocketFactory msf = (MailSSLSocketFactory)sf;
if (!msf.isServerTrusted(Host, sslsocket)) {
throw cleanupAndThrow(sslsocket,
new IOException("Server is not trusted: " + Host));
}
}
}
_
ソケットは次の行でタイムアウトを検出しました:sslsocket.startHandshake()
、これは証明書の検証前です。
第二に、ファイアウォールが無効になっていることをすでに述べましたが、前のソケットが正しく確立され、telnetコマンドも正しく確立されているので、ファイアウォールの問題でもないと思います。
プロトコルの問題のようですが、これは主にハンドシェイクフェーズ中に発生したためです。それ以外の場合は、証明書エラーや接続タイムアウトなど、より明確で異なるエラーが表示されます。これはsocketReadタイムアウトで、クライアント(サーバー)がサーバー(office365)からの情報を期待しているが、サーバーが応答しないことを示します。これは、クライアントが一緒に話し合っていないようです。
コンパイルされたコードはここでは問題ではありませんが、このプロセスの一部は環境に関連しています。_SSLSocketImpl.class
_クラスは、コンパイルからではなく、JREからのものです。そして、これはプロトコルが実装されている正確なコード(逆コンパイル)です。
_private void performInitialHandshake() throws IOException {
Object var1 = this.handshakeLock;
synchronized(this.handshakeLock) {
if (this.getConnectionState() == 1) {
this.kickstartHandshake();
if (this.inrec == null) {
this.inrec = new InputRecord();
this.inrec.setHandshakeHash(this.input.r.getHandshakeHash());
this.inrec.setHelloVersion(this.input.r.getHelloVersion());
this.inrec.enableFormatChecks();
}
this.readRecord(this.inrec, false);
this.inrec = null;
}
}
}
_
上記のコードはJRE_1.8.0_181のものであり、コードまたはサーバーのコードは異なる場合があります。これは、サーバーのJREバージョンを確認する必要がある方法です。
これをプロパティに追加してみてください。うまくいくはずです。
props.getProperties().put("mail.smtp.ssl.trust", "smtp.office365.com");