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

commons-email 1.6.0仅Java18.0.2可发邮件,其他版本报EmailException求助

邮件发送异常排查:仅Java 18.0.2/NetBeans启动成功,其他Java版本失败

问题背景

使用commons-email 1.6.0实现邮件发送功能,仅在NetBeans启动应用或Java 18.0.2版本下能成功发送;切换至其他Java版本时触发EmailException,核心底层异常为java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty。

相关代码

public boolean sendMailHTML(String subject, String msgBody, String[] destinataires, String[] copies, String[] blindCopies) {

    boolean result = false;
    try {
        HtmlEmail email = new HtmlEmail();

        email.setHostName(Config.getHostName());
        email.setSmtpPort(Config.getSmtpPort());

        if (Config.isSslEnabled()) {
            email.setSSLOnConnect(true);
            email.setSslSmtpPort(String.valueOf(Config.getSmtpPort()));
        }

        if (Config.isTlsEnabled()) {
            email.setStartTLSEnabled(true);
            email.setStartTLSRequired(true);
        }

        if (Config.isDebugEnabled()) {
            email.setDebug(true);
            BaseLoggerNotifier.logInfo(ILogger.levelErreur.debug, "Mail Configuration Loaded: \n" + Config.toString());
        }

        String login = Config.getLogin();
        String password = Config.getPassword();
        if (login != null && !login.equals("")) {
            email.setAuthentication(login, password);
        }
        email.setFrom(Config.getFrom());
        for (String dest : destinataires) {
            if (!dest.equals("")) {
                email.addTo(dest);
            }
        }
        if (copies != null) {
            for (String copie : copies) {
                if (!copie.equals("")) {
                    email.addCc(copie);
                }
            }
        }
        if (blindCopies != null) {
            for (String bCopie : blindCopies) {
                if (!bCopie.equals("")) {
                    email.addBcc(bCopie);
                }
            }
        }
        email.setSubject(subject);
        if (msgBody != null && !msgBody.equals("")) {
            email.setHtmlMsg(msgBody);
        } else {
            email.setHtmlMsg("<HTML></HTML>");
        }
    
        email.send();

        result = true;
    } catch (EmailException ex) {
        BaseLoggerNotifier.logInfo(ILogger.levelErreur.erreur, getClass().getName(), ex, "sendMailHTML()");
    }
    return result;
}

Jakarta Mail调试日志

DEBUG: Jakarta Mail version 1.6.7
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle]}
DEBUG: Providers Listed By Protocol: {imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: need username and password for authentication
DEBUG SMTP: protocolConnect returning false, host=####, user=####, password=
DEBUG SMTP: useEhlo true, useAuth true
    DEBUG SMTP: trying to connect to host "#####", port 465, isSSL false
    2024-06-07 10:43:25,975 INFO  [exceptions] mailtransfert.MailSender -> (org.apache.commons.mail.EmailException) Sending the email to the following server failed : ######:465 -> sendMailHTML()
        2024-06-07 10:43:25,975 INFO  [exceptions] org.apache.commons.mail.Email.sendMimeMessage(Email.java:1298)
        2024-06-07 10:43:25,975 INFO  [exceptions] org.apache.commons.mail.Email.send(Email.java:1282)
        2024-06-07 10:43:25,975 INFO  [exceptions] mailtransfert.MailSender.sendMailHTML(MailSender.java:110)
        2024-06-07 10:43:25,976 INFO  [exceptions] calcul.Calcul_Alarmes.sendMailOuverture(Calcul_Alarmes.java:412)
        2024-06-07 10:43:25,976 INFO  [exceptions] calcul.Calcul_Alarmes.runAction(Calcul_Alarmes.java:367)
        2024-06-07 10:43:25,976 INFO  [exceptions] framework.prototypes.ICalcul.run(ICalcul.java:142)
        2024-06-07 10:43:25,976 INFO  [exceptions] java.lang.Thread.run(Unknown Source)

添加-Djavax.net.debug=all后的核心异常

***
calcul.Calcul_Alarmes, handling exception: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
%% Invalidated:  [Session-12, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256]
calcul.Calcul_Alarmes, SEND TLSv1.2 ALERT:  fatal, description = internal_error
calcul.Calcul_Alarmes, WRITE: TLSv1.2 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 03 00 02 02 50                               ......P
calcul.Calcul_Alarmes, called closeSocket()
2024-06-07 11:42:22,388 ERROR [general] mailtransfert.MailSender -> (org.apache.commons.mail.EmailException) Sending the email to the following server failed : #### -> sendMailHTML()

排查分析与解决方案

核心原因

trustAnchors parameter must be non-empty异常表明JVM无法找到有效的信任证书库(cacerts),或者证书库为空/损坏。不同Java版本表现差异的原因:

  • NetBeans启动场景:NetBeans可能默认使用自带JDK或配置了正确的证书库路径,而非系统环境变量中的JDK。
  • Java 18.0.2正常:该版本的cacerts文件完整有效,其他版本的cacerts可能被误删、损坏,或JVM未正确指向证书库路径。
  • 端口465的SSL配置冲突:调试日志显示trying to connect to host "#####", port 465, isSSL false——465是SMTP SSL的标准端口,但代码中同时开启SSL和TLS,导致握手过程中证书验证失败。

解决步骤

  1. 检查目标Java版本的cacerts文件

    • 找到目标JDK的jre/lib/security/cacerts(Java 9+为lib/security/cacerts),确认文件存在且大小正常(通常150KB以上)。
    • 若文件缺失/损坏,从Java 18.0.2的对应目录复制cacerts到目标JDK路径,或用keytool重新导入根证书。
    • 默认密码为changeit,可通过keytool -list -keystore cacerts验证证书库有效性。
  2. 修复SSL/TLS配置逻辑
    端口465对应smtps协议(SSL直连),无需开启STARTTLS,修改代码逻辑:

    if (Config.isSslEnabled()) {
        email.setSSLOnConnect(true);
        email.setSslSmtpPort(String.valueOf(Config.getSmtpPort()));
        // SSL直连模式下关闭STARTTLS
        email.setStartTLSEnabled(false);
        email.setStartTLSRequired(false);
    } else if (Config.isTlsEnabled()) {
        // STARTTLS对应587端口
        email.setStartTLSEnabled(true);
        email.setStartTLSRequired(true);
    }
    
  3. 显式指定证书库路径(可选)
    若无法修改JDK的cacerts,可在启动参数中指定自定义证书库:

    -Djavax.net.ssl.trustStore=/path/to/valid/cacerts
    -Djavax.net.ssl.trustStorePassword=changeit
    
  4. 验证邮件服务器证书
    使用openssl s_client -connect smtp.example.com:465检查服务器证书是否有效,若为自签名证书,需导入JVM的cacerts:

    keytool -import -alias smtp-server -file server-cert.crt -keystore cacerts
    

内容的提问来源于stack exchange,提问作者Charles Semaan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:15:54