You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用TLS与服务器身份校验时Jakarta Mail发送邮件失败求助

解决Jakarta Mail(Angus)开启TLS服务器身份校验时的模块访问异常

问题场景

使用Jakarta Mail(依赖angus-mail-2.0.2)在JDK 11/17环境下发送邮件,启用TLS并开启mail.smtp.ssl.checkserveridentity=true时,抛出以下异常:

jakarta.mail.MessagingException: Could not convert socket to TLS; nested exception is: java.io.IOException: Can't verify identity of server: XXXXXXXXXXX.com at org.eclipse.angus.mail@2.0.2/org.eclipse.angus.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2173)

深层异常为模块访问权限问题:

java.lang.IllegalAccessException: class org.eclipse.angus.mail.util.SocketFetcher (in module org.eclipse.angus.mail) cannot access class sun.security.util.HostnameChecker (in module java.base) because module java.base does not export sun.security.util to module org.eclipse.angus.mail

关闭mail.smtp.ssl.checkserveridentity=false时邮件可正常发送,且服务器证书(Let's Encrypt)已在默认信任库中,排除证书信任问题。

根因分析

angus-mail-2.0.2版本的SocketFetcher类直接依赖了JDK内部API sun.security.util.HostnameChecker,而Java 9+的模块系统默认禁止导出内部API给第三方模块,导致权限访问异常。

解决方案

1. 升级Angus Mail到修复版本(推荐)

angus-mail在2.0.3及后续版本中已修复该问题,替换了对JDK内部API的依赖,改用标准公开API实现服务器身份校验。

  • 调整Maven/Gradle依赖,将angus-mail版本从2.0.2升级至2.0.3或更高:
    Maven示例:
    <dependency>
        <groupId>org.eclipse.angus</groupId>
        <artifactId>angus-mail</artifactId>
        <version>2.0.3</version>
    </dependency>
    
  • 保持jakarta.mail-api(2.1.2)和angus-activation版本的兼容性即可。

2. 临时JVM参数 workaround(不推荐生产环境)

如果暂时无法升级依赖,可通过JVM启动参数允许JDK导出内部API给Angus Mail模块:

--add-exports java.base/sun.security.util=org.eclipse.angus.mail

若为模块化应用,需额外添加:

--add-opens java.base/sun.security.util=org.eclipse.angus.mail

注意:该方案依赖JDK内部API,未来JDK版本可能移除该类,存在兼容性风险。

3. 自定义服务器身份校验逻辑

通过自定义SSLSocketFactory或HostnameVerifier替代默认校验逻辑,避免Angus Mail调用内部API:
修改代码中的Properties配置,添加自定义SocketFactory:

// 自定义SSLSocketFactory实现,使用标准HostnameVerifier
properties.setProperty("mail.smtp.ssl.socketFactory.class", "com.yourcompany.mail.CustomTLSSocketFactory");

自定义CustomTLSSocketFactory核心示例:

public class CustomTLSSocketFactory extends SSLSocketFactory {
    private final SSLSocketFactory delegate;
    private final HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();

    public CustomTLSSocketFactory() {
        this.delegate = (SSLSocketFactory) SSLSocketFactory.getDefault();
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        Socket sslSocket = delegate.createSocket(socket, host, port, autoClose);
        // 手动校验服务器身份
        if (!hostnameVerifier.verify(host, ((SSLSocket) sslSocket).getSession())) {
            throw new IOException("Server identity verification failed for host: " + host);
        }
        return sslSocket;
    }

    // 其他SSLSocketFactory抽象方法直接委托给delegate实现
    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
        return delegate.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
        return delegate.createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return delegate.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return delegate.createSocket(address, port, localAddress, localPort);
    }
}

添加该配置后,即使开启mail.smtp.ssl.checkserveridentity=true,也会使用自定义的校验逻辑,绕过Angus Mail的内部API调用。


内容的提问来源于stack exchange,提问作者Peter Müller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:47:35