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,导致握手过程中证书验证失败。
解决步骤
检查目标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验证证书库有效性。
- 找到目标JDK的
修复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); }显式指定证书库路径(可选)
若无法修改JDK的cacerts,可在启动参数中指定自定义证书库:-Djavax.net.ssl.trustStore=/path/to/valid/cacerts -Djavax.net.ssl.trustStorePassword=changeit验证邮件服务器证书
使用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
相关产品推荐
相关产品推荐

