Could not connect to SMTP host,PKIX path building failed,unable to find valid certification path...
Java 发邮件时遇到的问题
公司最近将邮件系统进行了升级,升级之后的邮件必须采用邮件加密传输通道。也就是 ssl 登录。
之前写的普通登录也将无法使用。今天在修改程序的过程中出现了一些问题,最终都顺利解决了,记录一下。
登录方式
普通登录
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"><property name="host" value="${mail.smtp.host}" /><property name="javaMailProperties"><props><prop key="mail.smtp.auth">${mail.smtp.auth}</prop><prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop></props></property><property name="username" value="${mail.smtp.username}" /><property name="password" value="${mail.smtp.password}" /></bean>
ssl 登录
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"><property name="host" value="${mail.smtp.host}" /><property name="port" value="${mail.smtp.port}" /><property name="username" value="${mail.smtp.username}" /><property name="password" value="${mail.smtp.password}" /><property name="javaMailProperties"><props><prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop><prop key="mail.smtp.auth">${mail.smtp.auth}</prop><prop key="mail.smtp.starttls.enable">${mail.smtp.starttls.enable}</prop><prop key="mail.smtp.socketFactory.port">${mail.smtp.port}</prop><prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop><prop key="mail.smtp.socketFactory.fallback">false</prop></props></property></bean>
配置文件
#------------------------------------------------------------------------------------# 邮件服务的配置#------------------------------------------------------------------------------------#服务器mail.smtp.host=smtp.abc.com#端口号,QQ邮箱需要使用SSL,端口号465或587,其他邮箱默认的端口号:25mail.smtp.port=465#STARTTLS是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL)mail.smtp.starttls.enable=true#是否需要验证密码mail.smtp.auth=true#超时时间mail.smtp.timeout=50000#------------------------------------------------------------------------------------# 加解密密钥 不能给出# 对用户名和密码都进行了加密 加解密工具在DESUtil中#------------------------------------------------------------------------------------#登陆用户名 我是随便写的mail.smtp.username=CBoch2LlydMW2432VYReDe4tjvJRstAmCoE123111#密码 qq为授权码 111111我是随便写的mail.smtp.password=avq5JeBiMuNr5F4wclLZZA==
改为 ssl 后遇到的问题
Mail server connection failed; nested exception is javax.mail.MessagingException: Could not connect to SMTP host: smtp.abc.com, port: 465;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. Failed messages: javax.mail.MessagingException: Could not connect to SMTP host: smtp.abc.com, port: 465;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
错误为:
- Could not connect to SMTP host: smtp.abc.com, port: 465
- PKIX path building failed
- unable to find valid certification path to requested target
其中 1 和 2 是由 3 引起的。
现在问题就是解决。
unable to find valid certification path to requested target
网上有两种解决方案
- 安装数字证书
- 生成证书
我把公司的数字证书安装后没有解决问题,采用第二种方法解决
参考文献:http://www.tuicool.com/articles/zUjiIb
其中这篇文章中有一个地方在邮件系统中需要注意,url 需要加上端口号。

看到如下信息表示成功地生成了证书:

将生成的 jssecacerts 文件拷贝到 %JAVA_HONME%\jre\lib\security\ 目录下就ok了。

附上 InstallCert.java
/** Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** - Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.** - Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.** - Neither the name of Sun Microsystems nor the names of its* contributors may be used to endorse or promote products derived* from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.security.KeyStore;import java.security.MessageDigest;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLException;import javax.net.ssl.SSLSocket;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.TrustManager;import javax.net.ssl.TrustManagerFactory;import javax.net.ssl.X509TrustManager;public class InstallCert {public static void main(String[] args) throws Exception {String host;int port;char[] passphrase;if ((args.length == 1) || (args.length == 2)) {String[] c = args[0].split(":");host = c[0];port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);String p = (args.length == 1) ? "changeit" : args[1];passphrase = p.toCharArray();} else {System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");return;}File file = new File("jssecacerts");if (file.isFile() == false) {char SEP = File.separatorChar;File dir = new File(System.getProperty("java.home") + SEP + "lib"+ SEP + "security");file = new File(dir, "jssecacerts");if (file.isFile() == false) {file = new File(dir, "cacerts");}}System.out.println("Loading KeyStore " + file + "...");InputStream in = new FileInputStream(file);KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());ks.load(in, passphrase);in.close();SSLContext context = SSLContext.getInstance("TLS");TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(ks);X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);context.init(null, new TrustManager[] { tm }, null);SSLSocketFactory factory = context.getSocketFactory();System.out.println("Opening connection to " + host + ":" + port + "...");SSLSocket socket = (SSLSocket) factory.createSocket(host, port);socket.setSoTimeout(10000);try {System.out.println("Starting SSL handshake...");socket.startHandshake();socket.close();System.out.println();System.out.println("No errors, certificate is already trusted");} catch (SSLException e) {System.out.println();e.printStackTrace(System.out);}X509Certificate[] chain = tm.chain;if (chain == null) {System.out.println("Could not obtain server certificate chain");return;}BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));System.out.println();System.out.println("Server sent " + chain.length + " certificate(s):");System.out.println();MessageDigest sha1 = MessageDigest.getInstance("SHA1");MessageDigest md5 = MessageDigest.getInstance("MD5");for (int i = 0; i < chain.length; i++) {X509Certificate cert = chain[i];System.out.println(" " + (i + 1) + " Subject "+ cert.getSubjectDN());System.out.println(" Issuer " + cert.getIssuerDN());sha1.update(cert.getEncoded());System.out.println(" sha1 " + toHexString(sha1.digest()));md5.update(cert.getEncoded());System.out.println(" md5 " + toHexString(md5.digest()));System.out.println();}System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");String line = reader.readLine().trim();int k;try {k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;} catch (NumberFormatException e) {System.out.println("KeyStore not changed");return;}X509Certificate cert = chain[k];String alias = host + "-" + (k + 1);ks.setCertificateEntry(alias, cert);OutputStream out = new FileOutputStream("jssecacerts");ks.store(out, passphrase);out.close();System.out.println();System.out.println(cert);System.out.println();System.out.println("Added certificate to keystore 'jssecacerts' using alias '"+ alias + "'");}private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();private static String toHexString(byte[] bytes) {StringBuilder sb = new StringBuilder(bytes.length * 3);for (int b : bytes) {b &= 0xff;sb.append(HEXDIGITS[b >> 4]);sb.append(HEXDIGITS[b & 15]);sb.append(' ');}return sb.toString();}private static class SavingTrustManager implements X509TrustManager {private final X509TrustManager tm;private X509Certificate[] chain;SavingTrustManager(X509TrustManager tm) {this.tm = tm;}public X509Certificate[] getAcceptedIssuers() {throw new UnsupportedOperationException();}public void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException {throw new UnsupportedOperationException();}public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {this.chain = chain;tm.checkServerTrusted(chain, authType);}}}
注意
不能将在自己电脑上生成的 jssecacerts 文件拷贝到服务器上去,如果拷贝上去不会起作用,仍然会报错。
服务器必须编译 InstallCert.java,去生成 jssecacerts 文件。
参考文献
- 解决PKIX问题:unable to find valid certification path to requested target
- Java发邮件-QQ服务器认证(A secure connection is requiered(such as ssl))
本文转载自:https://blog.csdn.net/frankcheng5143/article/details/52022289
评论