Spring javamailsender 发送 SSL 电子邮件
Posted
技术标签:
【中文标题】Spring javamailsender 发送 SSL 电子邮件【英文标题】:Spring javamailsender sending SSL emails 【发布时间】:2014-07-25 18:39:18 【问题描述】:在我的应用程序中,org.springframework.mail.javamail.JavaMailSenderImpl 未配置为发送 SSL 电子邮件(https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html 的默认值为 false)。
话虽如此,我通过这个 JavaMail API 发送了两种类型的电子邮件,一种是多部分/相对的,另一种是纯文本的。当部署在我们的开发服务器上时,这两封电子邮件都已成功发送。但多部分电子邮件无法在另一台开发服务器(A 阶段服务器)上发送。
应用部署在Tomcat6上,运行在JDK 1.6上。
我调试了为 tomcat 服务器配置的密钥库中可用的所有私钥和证书(Web 应用程序在 HTTPS 上运行)。这看起来不错(我也探索了替换密钥库的选项)。
现在,当我发送电子邮件时,失败的邮件具有来自 java 邮件 API 的以下调试
DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to host "8570-5cb30915t8.tb.bbossyyy.com", port 25, isSSL false
220 8570-5CB30915T8.TB.bbossyyy.com ESMTP SubEthaSMTP null
DEBUG SMTP: connected to host "8570-5cb30915t8.tb.bbossyyy.com", port: 25
EHLO hqarchs01
250-8570-5CB30915T8.TB.bbossyyy.com
250-8BITMIME
250-AUTH LOGIN
250 Ok
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN"
DEBUG SMTP: Found extension "Ok", arg ""
DEBUG SMTP: use8bit false
MAIL FROM:<dfn@bbossyyy.com>
250 Ok
DEBUG SMTP: sendPartial set
RCPT TO:<rp@bbossyyy.com>
250 Ok
RCPT TO:<rp@bbossyyy.com>
250 Ok
RCPT TO:<archo@bbossyyy.com>
250 Ok
DEBUG SMTP: Verified Addresses
DEBUG SMTP: RP <rp@bbossyyy.com>
DEBUG SMTP: RP <rp@bbossyyy.com>
DEBUG SMTP: archo@bbossyyy.com
DATA
354 End data with <CR><LF>.<CR><LF>
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
现在发送成功,下面是调试语句
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc]
DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to host "8570-5cb30915t8.tb.bbossyyy.com", port 25, isSSL false
220 8570-5CB30915T8.TB.bbossyyy.com ESMTP SubEthaSMTP null
DEBUG SMTP: connected to host "8570-5cb30915t8.tb.bbossyyy.com", port: 25
EHLO hqarchs01
250-8570-5CB30915T8.TB.bbossyyy.com
250-8BITMIME
250-AUTH LOGIN
250 Ok
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN"
DEBUG SMTP: Found extension "Ok", arg ""
DEBUG SMTP: use8bit false
MAIL FROM:<archo@bbossyyy.com>
250 Ok
DEBUG SMTP: sendPartial set
RCPT TO:<jmi@bbossyyy.com>
250 Ok
DEBUG SMTP: Verified Addresses
DEBUG SMTP: John Mirabile <jmirabile@bbossyyy.com>
DATA
354 End data with <CR><LF>.<CR><LF>
Date: Wed, 4 Jun 2014 18:06:15 -0400 (EDT)
From: ArchO <archo@bbossyyy.com>
To: JM <jm@bbossyyy.com>
Message-ID: <1680495420.11401919575021.JavaMail.svcaoadmin@hqarchs01>
Subject: PO # 88636 Approval
MIME-Version: 1.0
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit
<html>
<head>
<title>PO Approval</title>
<style>
*
font-family:Tahoma,Arial,Verdana,Helvetica,sans-serif;
font-size:10pt;
</style>
</head>
<body>
------------------------------
</body>
</html>
.
250 Ok
QUIT
221 Bye
此外,抛出的异常是 org.springframework.mail.MailSendException,嵌套原因是通常的 SSL 握手异常
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source)
at java.net.URL.openStream(Unknown Source)
at javax.activation.URLDataSource.getInputStream(Unknown Source)
at javax.activation.DataHandler.writeTo(Unknown Source)
at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1350)
at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:845)
at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:361)
at com.sun.mail.handlers.multipart_mixed.writeTo(multipart_mixed.java:85)
at javax.activation.ObjectDataContentHandler.writeTo(Unknown Source)
at javax.activation.DataHandler.writeTo(Unknown Source)
at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1350)
at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1683)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:585)
... 77 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 101 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 107 more
我还从 sun.security.provider.certpath.SunCertPathBuilderException 中检索了 AdjacencyList 以检索 sun.security.provider.certpath.BuildStep 并使用 getCertificate() 方法打印证书详细信息。根据使用 Apache Tomcat 服务器配置的密钥库,该证书是有效的。以下是调试中的一些细节
DEBUG :BS getFullString :Certificate to be tried.
Issuer: CN=bbossyyyIssuingCA, DC=bbossyyy, DC=bbosstest, DC=com
Subject:
SerialNum: 152a754f000000001177
Expires: Fri Apr 22 11:23:39 EDT 2016
SubjKeyID: KeyIdentifier [
0000: 6D E0 32 B3 9E 7C 14 C3 14 B6 63 55 7F C7 4C CB m.2.......cU..L.
0010: 2C 48 F6 DC ,H..
]
AuthKeyID: KeyIdentifier [
0000: B8 21 48 97 A2 7B 0A A2 03 6F 35 4C 9D DB 38 AE .!H......o5L..8.
0010: 6E 3A 6B B6 n:k.
]
Exception: null
Index: 1
DEBUG :BS getIssuerName : CN=bbossyyyIssuingCA, DC=bbossyyy, DC=bbosstest, DC=com
DEBUG :BS verboseToString : Certificate to be tried.
Certificate contains:
Issuer: CN=bbossyyyIssuingCA, DC=bbossyyy, DC=bbosstest, DC=com
Subject:
SerialNum: 152a754f000000001177
Expires: Fri Apr 22 11:23:39 EDT 2016
SubjKeyID: KeyIdentifier [
0000: 6D E0 32 B3 9E 7C 14 C3 14 B6 63 55 7F C7 4C CB m.2.......cU..L.
0010: 2C 48 F6 DC ,H..
]
AuthKeyID: KeyIdentifier [
0000: B8 21 48 97 A2 7B 0A A2 03 6F 35 4C 9D DB 38 AE .!H......o5L..8.
0010: 6E 3A 6B B6 n:k.
]
DEBUG :BS getSigAlgName : SHA256withRSA
DEBUG :BS getSigAlgOID : 1.2.840.113549.1.1.11
DEBUG :BS getType : X.509
DEBUG :BS getIssuerAlternativeNames : null
DEBUG :BS getNonCriticalExtensionOIDs : [2.5.29.14, 1.3.6.1.4.1.311.21.10, 1.3.6.1.4.1.311.21.7, 1.3.6.1.5.5.7.1.1, 2.5.29.31, 2.5.29.37, 2.5.29.35]
DEBUG :BS getNotAfter : Fri Apr 22 11:23:39 EDT 2016
DEBUG :BS getNotBefore : Wed Jun 04 16:34:29 EDT 2014
DEBUG :BS getVersion : 3
我需要您的帮助来解决这个问题。询问您对此的看法。
编辑........
由于进一步学习和cmets,转贴如下 我试图从配置为 HTTPS 的 Apache Tomcat 服务器 6.0 发送带有内联图像的电子邮件。目标 SMTP 服务器没有来自受信任 CA 的有效证书。
现在,我可以将不带附件的电子邮件成功发送到服务器,并且在 HTTP 上运行时,我还可以发送带附件的电子邮件(不安全)。但是,我无法从 tomcat 的 HTTPS 配置发送电子邮件到 smtp 服务器。
我正在使用 Spring 的 JavaMailSenderImpl 发送电子邮件。 Mailsender的bean配置如下
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host"><value>$smtp.server</value></property>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.sendpartial">true</prop>
<prop key="mail.debug">true</prop>
</props>
</property>
</bean>
如下所示,一旦客户端开始向服务器发送数据,就会创建异常
DEBUG: JavaMail version 1.4ea
DEBUG: java.io.FileNotFoundException: C:\Program Files\Java\jre6\lib\javamail.providers (The system cannot find the file specified)
DEBUG: !anyLoaded
DEBUG: not loading resource: /META-INF/javamail.providers
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Sun Microsystems, Inc], com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Sun Microsystems, Inc], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Sun Microsystems, Inc], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Sun Microsystems, Inc], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Sun Microsystems, Inc]
DEBUG: Providers Listed By Protocol: imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Sun Microsystems, Inc], imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Sun Microsystems, Inc], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Sun Microsystems, Inc], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Sun Microsystems, Inc], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Sun Microsystems, Inc], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc]
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: !anyLoaded
DEBUG: not loading resource: /META-INF/javamail.address.map
DEBUG: java.io.FileNotFoundException: C:\Program Files\Java\jre6\lib\javamail.address.map (The system cannot find the file specified)
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc]
DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to host "appsmtp.bobbsincyyy.com", port 25, isSSL false
220 hqxhubp15.fb.bobbsincyyy.com Microsoft ESMTP MAIL Service ready at Thu, 5 Jun 2014 13:47:10 -0400
DEBUG SMTP: connected to host "appsmtp.bobbsincyyy.com", port: 25
EHLO hqarchd01
250-hqxhubp15.fb.bobbsincyyy.com Hello [172.25.14.13]
250-SIZE 219901952
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-AUTH
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250-XEXCH50
250 XSHADOW
DEBUG SMTP: Found extension "SIZE", arg "219901952"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "AUTH", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "XEXCH50", arg ""
DEBUG SMTP: Found extension "XSHADOW", arg ""
DEBUG SMTP: use8bit false
MAIL FROM:<df@bobbsincyyy.com>
250 2.1.0 Sender OK
DEBUG SMTP: sendPartial set
RCPT TO:<rp@bobbsincyyy.com>
250 2.1.5 Recipient OK
RCPT TO:<rp@bobbsincyyy.com>
250 2.1.5 Recipient OK
RCPT TO:<archo@bobbsincyyy.com>
250 2.1.5 Recipient OK
DEBUG SMTP: Verified Addresses
DEBUG SMTP: RB <rp@bobbsincyyy.com>
DEBUG SMTP: RB <rp@bobbsincyyy.com>
DEBUG SMTP: archo@bobbsincyyy.com
DATA
354 Start mail input; end with <CRLF>.<CRLF>
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
http://springinpractice.com/2012/04/29/fixing-pkix-path-building-issues-when-using-javamail-and-smtp 中有一个很好的描述。但是,这也不起作用(我不确定来自 smtp 服务器的自签名证书是否会导致问题)。
我还尝试使用 Truststore 配置在 javamail 属性上启用 STARTTLS。那也没用。
SMTP 服务器是内网服务器,不对外暴露。因此,即使使用自签名证书,基本上我也可以信任此 SMTP。
如何做到这一点?
【问题讨论】:
STARTTLS 在哪里?您正在连接到端口 25,该端口通常不支持加密(但可能支持通过 STARTTLS 从纯文本升级到 TLS)。通常 SSL 连接在端口 465 上,可升级(通过 STARTTLS)连接在 587 上。端口 25 支持加密的情况很少见。 【参考方案1】:我强烈怀疑这是因为您在端口 25 上连接,而服务器不支持该端口上的 TLS。请注意,服务器在连接时不会通告 STARTTLS 支持(250 条连接消息)。
您可能应该使用 465 (SSL) 或 587 (STARTTLS)。
【讨论】:
我在调试消息中没有看到任何 STARTTLS 命令。可能调试没有这么说?我不知道。此外,我仍然无法理解为什么一种方式不加密而另一种方式加密。在互联网上跌跌撞撞并不支持 MIME 上的附件需要 SSL 的理论。【参考方案2】:在没有发出 STARTTLS 命令的情况下,我不明白您为什么曾经收到 SSL 异常。
是否有可能发生故障的服务器正在运行某种防火墙或防病毒软件来拦截您的请求,并且当您的消息内容不是纯文本时可能表现不同?
【讨论】:
【参考方案3】:添加图像时,我使用 URLDataSource 链接到 HTTPS 站点(位于服务器的图像文件夹中的图像)。
由于服务器不信任自己,因此创建了 SSLHandShakeException。改用 FileDataSource 解决了这个问题。
问题从来不在于 SMTP。
我的愚蠢学习:如果有HandShake异常,先分析堆栈跟踪!
【讨论】:
以上是关于Spring javamailsender 发送 SSL 电子邮件的主要内容,如果未能解决你的问题,请参考以下文章
spring-boot实战12:Spring Boot中使用JavaMailSender发送邮件
Spring Boot使用JavaMailSender发送邮件