SMTP邮件发送失败:TLS套接字转换及PKIX证书路径错误
I'm trying to send emails via SMTP using port 578 (note: my Spring config shows port 587) and getting the following error:
Could not convert socket to TLS; nested exception is: 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
Relevant Code & Configuration
EmailController.java
package com.jcg.spring.mvc.email; import java.io.IOException; import java.io.InputStream; import javax.mail.internet.MimeMessage; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamSource; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessagePreparator; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.commons.CommonsMultipartFile; import org.springframework.web.servlet.ModelAndView; @Controller public class EmailController { static String emailToRecipient, emailSubject, emailMessage; static final String emailFromRecipient = "srinivasaraojella@gmail.com"; static ModelAndView modelViewObj; @Autowired private JavaMailSender mailSenderObj; @RequestMapping(value = {"/", "emailForm"}, method = RequestMethod.GET) public ModelAndView showEmailForm(ModelMap model) { modelViewObj = new ModelAndView("emailForm"); return modelViewObj; } // This Method Is Used To Prepare The Email Message And Send It To The Client @RequestMapping(value = "sendEmail", method = RequestMethod.POST) public ModelAndView sendEmailToClient(HttpServletRequest request, final @RequestParam CommonsMultipartFile attachFileObj) { // Reading Email Form Input Parameters emailSubject = request.getParameter("subject"); emailMessage = request.getParameter("message"); emailToRecipient = request.getParameter("mailTo"); // Logging The Email Form Parameters For Debugging Purpose System.out.println("\nReceipient?= " + emailToRecipient + ", Subject?= " + emailSubject + ", Message?= " + emailMessage + "\n"); mailSenderObj.send(new MimeMessagePreparator() { public void prepare(MimeMessage mimeMessage) throws Exception { MimeMessageHelper mimeMsgHelperObj = new MimeMessageHelper(mimeMessage, true, "UTF-8"); mimeMsgHelperObj.setTo(emailToRecipient); mimeMsgHelperObj.setFrom(emailFromRecipient); mimeMsgHelperObj.setText(emailMessage); mimeMsgHelperObj.setSubject(emailSubject); // Determine If There Is An File Upload. If Yes, Attach It To The Client Email if ((attachFileObj != null) && (attachFileObj.getSize() > 0) && (!attachFileObj.equals(""))) { System.out.println("\nAttachment Name?= " + attachFileObj.getOriginalFilename() + "\n"); mimeMsgHelperObj.addAttachment(attachFileObj.getOriginalFilename(), new InputStreamSource() { public InputStream getInputStream() throws IOException { return attachFileObj.getInputStream(); } }); } else { System.out.println("\nNo Attachment Is Selected By The User. Sending Text Email!\n"); } } }); System.out.println("\nMessage Send Successfully.... Hurrey!\n"); modelViewObj = new ModelAndView("success","messageObj","Thank You! Your Email Has Been Sent!"); return modelViewObj; } }
spring-servlet.xml (truncated)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.jcg.spring.mvc.email" /> <!-- Spring Email Sender Bean Configuration --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.gmail.com" /> <property name="port" value="587" /> <property name="username" value="srinivasaraojella@gmail.com" /> <property name="password" value="srinu877$@" /> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> <prop key="mail.debug">true</prop> <prop key="mail.transport.protocol">smtp</prop> <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> <prop key="mail.smtp.socketFactory.port">465</prop> <prop key="mail.smtp.starttls.enable">true</prop> </props> </property> </bean> </beans>
Let's fix this issue step by step. The error has two main root causes: conflicting SMTP configuration settings, and missing root certificates in Java's trust store.
1. Fix Conflicting SMTP Configuration
You're using port 587 (STARTTLS mode) for Gmail SMTP, but your config includes settings for 465 (SSL direct mode), which causes a conflict. Here's how to correct it:
Update the javaMailProperties section in your spring-servlet.xml to remove the socket factory settings (they're only for port 465):
<property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> <prop key="mail.debug">true</prop> <prop key="mail.transport.protocol">smtp</prop> <prop key="mail.smtp.starttls.enable">true</prop> <!-- Optional: Force TLS connection (recommended) --> <prop key="mail.smtp.starttls.required">true</prop> </props> </property>
Also, double-check the port: you mentioned using 578, but Gmail's standard SMTP ports are 587 (STARTTLS) and 465 (SSL). Use 587 unless your email provider specifies otherwise.
2. Resolve PKIX Certificate Trust Issue
The PKIX path building failed error means Java can't verify Gmail's SSL certificate because it doesn't have the necessary root certificate in its default trust store (cacerts). Here are three solutions, ordered by security:
Option 1: Import Gmail's Root Certificate (Production-Grade)
This is the safest method for production environments:
Get Gmail's SMTP certificate:
Run this OpenSSL command to fetch the certificate:openssl s_client -connect smtp.gmail.com:587 -starttls smtpCopy everything from
-----BEGIN CERTIFICATE-----to-----END CERTIFICATE-----and save it asgmail.crt.Import the certificate into Java's trust store:
Locate your Javacacertsfile (usually inJAVA_HOME/jre/lib/security/cacertsfor JDK 8, orJAVA_HOME/lib/security/cacertsfor JDK 9+). Run this keytool command (default password ischangeit):keytool -import -alias gmail-smtp -keystore /path/to/cacerts -file gmail.crtConfirm the import when prompted.
Option 2: Bypass Certificate Validation (Only for Development)
Never use this in production—it disables SSL security. But it's useful for quick testing:
Replace your mailSender bean with a custom implementation that skips certificate checks:
@Bean public JavaMailSender mailSender() { JavaMailSenderImpl sender = new JavaMailSenderImpl(); sender.setHost("smtp.gmail.com"); sender.setPort(587); sender.setUsername("srinivasaraojella@gmail.com"); // If you have 2FA enabled on Gmail, use an App Password instead of your regular password sender.setPassword("srinu877$@"); Properties props = sender.getJavaMailProperties(); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.debug", "true"); // Bypass SSL certificate validation try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {} public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); props.put("mail.smtp.ssl.socketFactory", sc.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } return sender; }
Option 3: Update Java to the Latest Version
Older Java versions may not include the latest root certificates. Updating to the most recent JDK/JRE can automatically resolve the trust issue, as Oracle regularly updates the cacerts store.
3. Additional Gmail-Specific Fix
If you're still having trouble sending emails:
- Enable App Passwords: If you have 2-Step Verification enabled on your Gmail account, you can't use your regular password. Generate an App Password (requires 2FA) and use that in your configuration.
- Check Less Secure Apps: Google has phased out this option, but if you don't have 2FA enabled, you might need to enable "Less secure app access" (not recommended for long-term use).
内容的提问来源于stack exchange,提问作者Jalla Srinivasaraojella

