javax.net.ssl.SSLHandshakeException:没有合适的协议。如何强制 Java 使用 TLSv1.2 发送邮件?
Posted
技术标签:
【中文标题】javax.net.ssl.SSLHandshakeException:没有合适的协议。如何强制 Java 使用 TLSv1.2 发送邮件?【英文标题】:javax.net.ssl.SSLHandshakeException:No appropriate protocol. How to force Java to use TLSv1.2 for sending mail? 【发布时间】:2021-10-21 17:36:25 【问题描述】:我正在尝试使用 JavaMailSender 在 Spring Boot 中发送邮件,但出现此错误:
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate). Failed messages: javax.mail.MessagingException: Could not convert socket to TLS;
nested exception is:
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
阅读后发现是因为我们使用的 TLSv1 或 TLSv1.1 已经过时了,我们应该使用 v1.2 或更高版本。我尝试在 application.yml 中添加值为 TLSv1.2 的 ssl.protocols 属性,但它似乎不起作用。这是我的 application.yml:
spring:
mail:
host: smtp.gmail.com
port: 587
username: ******@gmail.com
password: *******
protocol: smtp
tls: true
properties.mail.smtp:
auth: true
ssl.trust: smtp.gmail.com
starttls.required: true
starttls.enabled: true
ssl.protocols: TLSv1.2
这是发送邮件的方法:
public void sendEmail(String to, String subject, String body)
logger.info("Sending mail to : " + to);
SimpleMailMessage mail = new SimpleMailMessage();
mail.setTo(to);
mail.setSubject(subject);
mail.setText(body);
try
javaMailSender.send(mail);
logger.info("Mail sent to " + to);
catch (Exception e)
logger.error("Could not send mail to " + to + ". Exception : " + e.getMessage());
我更新了 JavaMailSender 版本,但没有解决问题。唯一可行的方法是按照this answer 中的建议从 jdk.tls.disabledAlgorithms 中删除 TLSv1 和 TLSv1.1,但这只是一个临时修复。如何让邮件发件人使用 TLSv1.2 或更高版本?我在 application.yml 中定义 ssl.protocols 属性的方式有什么问题吗?
这是我使用的java版本:
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.292-b10, mixed mode)
以及spring boot starter邮件版:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.5.3</version>
</dependency>
【问题讨论】:
是 starttls.enables: true 还是 starttls.enable: true 从 enable 中去掉 s 您可以添加 ssl.protocols : TLSv1.2 属性或 ssl.enable : false 我不相信 TLS 版本是问题所在。考虑“或密码套件不合适”。邮件服务器是否有证书和私钥?以及它支持哪些密码套件。 @Sagii 它是我的属性文件中的 starttls.enables,感谢您指出。我更正了它,并尝试提供 ssl.enable:false 而不是 ssl.protocols:TLSv1.2,但仍然是同样的错误。 @user207421 我正在使用 gmail 发送邮件。我现在检查了支持的密码,发现了这个support.google.com/a/answer/9795993?hl=en 【参考方案1】:我最近回答了一个几乎相同的question。
如您所料,您的问题将与 JDK 中引入的 a change 有关,以便默认禁用不安全的 TLS 协议版本:
安全库/javax.net.ssl ➜ 禁用 TLS 1.0 和 1.1
TLS 1.0 和 1.1 是 TLS 协议的版本,它们不再是 被认为是安全的,并已被更安全和现代的取代 版本(TLS 1.2 和 1.3)。
这些版本现已默认禁用。如果遇到 问题,您可以自担风险,通过删除重新启用版本
jdk.tls.disabledAlgorithms
中的“TLSv1”和/或“TLSv1.1”java.security
配置文件中的安全属性。
正如您在bug description 中看到的那样,更改已向后移植到不同的 JDK 版本,即您正在使用的版本。
为了解决这个问题,您可以编辑您的java.security
配置文件并从jdk.tls.disabledAlgorithms
安全属性中删除TLSv1
和/或TLSv1.1
。
此外,您可以在配置属性中明确包含所需的 TLS 协议:
props.put("mail.smtp.ssl.protocols", "TLSv1.2");
老实说,我不明白为什么 Spring Boot 不应用此配置,看来您定义正确。您可以在描述mail properties configuration 和不同的accepted mail properties 时查看库的源代码,以及Spring Boot documentation 中的描述。请尝试像这样定义属性:
spring:
mail:
properties:
# other properties you need
"mail.smtp.ssl.protocols": "TLSv1.2"
为了排除一些可能与 YAML 格式有关的问题,您可以尝试在 JavaMaiSenderImpl
中显式设置此属性,仅用于测试:
public void sendEmail(String to, String subject, String body)
logger.info("Sending mail to : " + to);
SimpleMailMessage mail = new SimpleMailMessage();
mail.setTo(to);
mail.setSubject(subject);
mail.setText(body);
try
// Just for validating the solution
JavaMailSenderImpl javaMailSenderImpl = (JavaMailSenderImpl)javaMailSender;
// Please, put a breakpoint here and debug the configured
// properties, it will provide you a valuable information
// about the actual properties that have been applied
javaMailSenderImpl.getJavaMailProperties().put("mail.smtp.ssl.protocols", "TLSv1.2");
javaMailSender.send(mail);
logger.info("Mail sent to " + to);
catch (Exception e)
logger.error("Could not send mail to " + to + ". Exception : " + e.getMessage());
【讨论】:
很抱歉这么晚才回复。我们正在更新我们的 java 版本,不知何故我们的集成测试都搞砸了,所以我无法验证这一点。我想在我们修复测试后回复,但我认为它不会很快修复。幸运的是,我在 Java 11 中没有遇到这个错误。无论如何我都会奖励你@jccampanero 嗨@VishnuVS。请,没有必要道歉。非常感谢你给我赏金。无论如何,我希望您能真正解决问题:当您修复测试时,请随时与我联系,如果可以,我将很乐意提供帮助。此外,请注意,该更改已向后移植到多个 JDK 版本,可能也移植到您将用于 Java 11 的那个版本。正如我告诉你的,如果你认为我可以提供帮助,请再次与我联系。 太棒了!这个问题几个月来一直困扰着我们在一台使用最新版本的 open-jdk 的服务器上,但在另一台使用非常旧版本的服务器上却没有。只需添加: props.put("mail.smtp.ssl.protocols", "TLSv1.2");到处都修好了。谢谢! 这很棒@balm。我很高兴听到这个答案很有帮助并且您能够解决您的问题。【参考方案2】:我在使用 application.properties 时遇到了类似的问题 我得到的错误是:
-dev.miku.r2dbc.mysql.client.MySqlConnectionClosedException:连接意外关闭 -d.m.r.mysql.client.ReactorNettyClient:错误:没有合适的协议(协议被禁用或密码套件不合适) -12-16 14:48:30.287 WARN 27232 --- [actor-tcp-nio-2] d.m.r.mysql.client.ReactorNettyClient:连接已被对等方关闭 2021-12-16 14:48:30.288 错误 27232 --- [actor-tcp-nio-2] reactor.core.publisher.Operators :操作员称为默认 onErrorDropped
这是我的解决方案:
一行代码就解决了这个问题:
spring.r2dbc.properties.ssl= false
【讨论】:
您在哪里添加“一行”来解决问题?另外,如果我没记错的话,这似乎正在关闭 ssl。【参考方案3】:您可以通过设置System.setProperty("mail.smtp.ssl.protocols", protocols);
来实现。例如:
String protocols = String.join(" ",
SSLContext
.getDefault()
.getSupportedSSLParameters()
.getProtocols()
);
System.setProperty("mail.smtp.ssl.protocols", protocols);
无论如何,此配置无需任何配置即可工作
mail:
host: smtp.gmail.com
port: 587
username: ***@gmail.com
password: P4ssw0rd
properties:
mail:
smtp:
starttls:
enable: true
required: true
auth: true
【讨论】:
很难看出将mail.smtp.ssl.protocols
设置为支持的协议除了断言现有的默认值外,还能做什么。如果有的话,通过添加所有受支持但禁用的协议(例如匿名协议)会使情况变得更糟。以上是关于javax.net.ssl.SSLHandshakeException:没有合适的协议。如何强制 Java 使用 TLSv1.2 发送邮件?的主要内容,如果未能解决你的问题,请参考以下文章