yeskery

Could not connect to SMTP host,PKIX path building failed,unable to find valid certification path...

Java 发邮件时遇到的问题

公司最近将邮件系统进行了升级,升级之后的邮件必须采用邮件加密传输通道。也就是 ssl 登录。

之前写的普通登录也将无法使用。今天在修改程序的过程中出现了一些问题,最终都顺利解决了,记录一下。

登录方式

普通登录

  1. <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  2. <property name="host" value="${mail.smtp.host}" />
  3. <property name="javaMailProperties">
  4. <props>
  5. <prop key="mail.smtp.auth">${mail.smtp.auth}</prop>
  6. <prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop>
  7. </props>
  8. </property>
  9. <property name="username" value="${mail.smtp.username}" />
  10. <property name="password" value="${mail.smtp.password}" />
  11. </bean>

ssl 登录

  1. <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  2. <property name="host" value="${mail.smtp.host}" />
  3. <property name="port" value="${mail.smtp.port}" />
  4. <property name="username" value="${mail.smtp.username}" />
  5. <property name="password" value="${mail.smtp.password}" />
  6. <property name="javaMailProperties">
  7. <props>
  8. <prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop>
  9. <prop key="mail.smtp.auth">${mail.smtp.auth}</prop>
  10. <prop key="mail.smtp.starttls.enable">${mail.smtp.starttls.enable}</prop>
  11. <prop key="mail.smtp.socketFactory.port">${mail.smtp.port}</prop>
  12. <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
  13. <prop key="mail.smtp.socketFactory.fallback">false</prop>
  14. </props>
  15. </property>
  16. </bean>

配置文件

  1. #------------------------------------------------------------------------------------
  2. # 邮件服务的配置
  3. #------------------------------------------------------------------------------------
  4. #服务器
  5. mail.smtp.host=smtp.abc.com
  6. #端口号,QQ邮箱需要使用SSL,端口号465或587,其他邮箱默认的端口号:25
  7. mail.smtp.port=465
  8. #STARTTLS是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL)
  9. mail.smtp.starttls.enable=true
  10. #是否需要验证密码
  11. mail.smtp.auth=true
  12. #超时时间
  13. mail.smtp.timeout=50000
  14. #------------------------------------------------------------------------------------
  15. # 加解密密钥 不能给出
  16. # 对用户名和密码都进行了加密 加解密工具在DESUtil中
  17. #------------------------------------------------------------------------------------
  18. #登陆用户名 我是随便写的
  19. mail.smtp.username=CBoch2LlydMW2432VYReDe4tjvJRstAmCoE123111
  20. #密码 qq为授权码 111111我是随便写的
  21. mail.smtp.password=avq5JeBiMuNr5F4wclLZZA==

改为 ssl 后遇到的问题

  1. Mail server connection failed; nested exception is javax.mail.MessagingException: Could not connect to SMTP host: smtp.abc.com, port: 465;
  2. nested exception is:
  3. 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;
  4. nested exception is:
  5. 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

错误为:

  1. Could not connect to SMTP host: smtp.abc.com, port: 465
  2. PKIX path building failed
  3. unable to find valid certification path to requested target

其中 1 和 2 是由 3 引起的。

现在问题就是解决。

unable to find valid certification path to requested target

网上有两种解决方案

  1. 安装数字证书
  2. 生成证书

我把公司的数字证书安装后没有解决问题,采用第二种方法解决

参考文献:http://www.tuicool.com/articles/zUjiIb

其中这篇文章中有一个地方在邮件系统中需要注意,url 需要加上端口号

java_mail_cert_create

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

java_mail_cert_create_success

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

java_mail_send

附上 InstallCert.java

  1. /*
  2. * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * - Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. *
  11. * - Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * - Neither the name of Sun Microsystems nor the names of its
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. import java.io.BufferedReader;
  32. import java.io.File;
  33. import java.io.FileInputStream;
  34. import java.io.FileOutputStream;
  35. import java.io.InputStream;
  36. import java.io.InputStreamReader;
  37. import java.io.OutputStream;
  38. import java.security.KeyStore;
  39. import java.security.MessageDigest;
  40. import java.security.cert.CertificateException;
  41. import java.security.cert.X509Certificate;
  42. import javax.net.ssl.SSLContext;
  43. import javax.net.ssl.SSLException;
  44. import javax.net.ssl.SSLSocket;
  45. import javax.net.ssl.SSLSocketFactory;
  46. import javax.net.ssl.TrustManager;
  47. import javax.net.ssl.TrustManagerFactory;
  48. import javax.net.ssl.X509TrustManager;
  49. public class InstallCert {
  50. public static void main(String[] args) throws Exception {
  51. String host;
  52. int port;
  53. char[] passphrase;
  54. if ((args.length == 1) || (args.length == 2)) {
  55. String[] c = args[0].split(":");
  56. host = c[0];
  57. port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
  58. String p = (args.length == 1) ? "changeit" : args[1];
  59. passphrase = p.toCharArray();
  60. } else {
  61. System.out
  62. .println("Usage: java InstallCert <host>[:port] [passphrase]");
  63. return;
  64. }
  65. File file = new File("jssecacerts");
  66. if (file.isFile() == false) {
  67. char SEP = File.separatorChar;
  68. File dir = new File(System.getProperty("java.home") + SEP + "lib"
  69. + SEP + "security");
  70. file = new File(dir, "jssecacerts");
  71. if (file.isFile() == false) {
  72. file = new File(dir, "cacerts");
  73. }
  74. }
  75. System.out.println("Loading KeyStore " + file + "...");
  76. InputStream in = new FileInputStream(file);
  77. KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
  78. ks.load(in, passphrase);
  79. in.close();
  80. SSLContext context = SSLContext.getInstance("TLS");
  81. TrustManagerFactory tmf = TrustManagerFactory
  82. .getInstance(TrustManagerFactory.getDefaultAlgorithm());
  83. tmf.init(ks);
  84. X509TrustManager defaultTrustManager = (X509TrustManager) tmf
  85. .getTrustManagers()[0];
  86. SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
  87. context.init(null, new TrustManager[] { tm }, null);
  88. SSLSocketFactory factory = context.getSocketFactory();
  89. System.out
  90. .println("Opening connection to " + host + ":" + port + "...");
  91. SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
  92. socket.setSoTimeout(10000);
  93. try {
  94. System.out.println("Starting SSL handshake...");
  95. socket.startHandshake();
  96. socket.close();
  97. System.out.println();
  98. System.out.println("No errors, certificate is already trusted");
  99. } catch (SSLException e) {
  100. System.out.println();
  101. e.printStackTrace(System.out);
  102. }
  103. X509Certificate[] chain = tm.chain;
  104. if (chain == null) {
  105. System.out.println("Could not obtain server certificate chain");
  106. return;
  107. }
  108. BufferedReader reader = new BufferedReader(new InputStreamReader(
  109. System.in));
  110. System.out.println();
  111. System.out.println("Server sent " + chain.length + " certificate(s):");
  112. System.out.println();
  113. MessageDigest sha1 = MessageDigest.getInstance("SHA1");
  114. MessageDigest md5 = MessageDigest.getInstance("MD5");
  115. for (int i = 0; i < chain.length; i++) {
  116. X509Certificate cert = chain[i];
  117. System.out.println(" " + (i + 1) + " Subject "
  118. + cert.getSubjectDN());
  119. System.out.println(" Issuer " + cert.getIssuerDN());
  120. sha1.update(cert.getEncoded());
  121. System.out.println(" sha1 " + toHexString(sha1.digest()));
  122. md5.update(cert.getEncoded());
  123. System.out.println(" md5 " + toHexString(md5.digest()));
  124. System.out.println();
  125. }
  126. System.out
  127. .println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
  128. String line = reader.readLine().trim();
  129. int k;
  130. try {
  131. k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
  132. } catch (NumberFormatException e) {
  133. System.out.println("KeyStore not changed");
  134. return;
  135. }
  136. X509Certificate cert = chain[k];
  137. String alias = host + "-" + (k + 1);
  138. ks.setCertificateEntry(alias, cert);
  139. OutputStream out = new FileOutputStream("jssecacerts");
  140. ks.store(out, passphrase);
  141. out.close();
  142. System.out.println();
  143. System.out.println(cert);
  144. System.out.println();
  145. System.out
  146. .println("Added certificate to keystore 'jssecacerts' using alias '"
  147. + alias + "'");
  148. }
  149. private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
  150. private static String toHexString(byte[] bytes) {
  151. StringBuilder sb = new StringBuilder(bytes.length * 3);
  152. for (int b : bytes) {
  153. b &= 0xff;
  154. sb.append(HEXDIGITS[b >> 4]);
  155. sb.append(HEXDIGITS[b & 15]);
  156. sb.append(' ');
  157. }
  158. return sb.toString();
  159. }
  160. private static class SavingTrustManager implements X509TrustManager {
  161. private final X509TrustManager tm;
  162. private X509Certificate[] chain;
  163. SavingTrustManager(X509TrustManager tm) {
  164. this.tm = tm;
  165. }
  166. public X509Certificate[] getAcceptedIssuers() {
  167. throw new UnsupportedOperationException();
  168. }
  169. public void checkClientTrusted(X509Certificate[] chain, String authType)
  170. throws CertificateException {
  171. throw new UnsupportedOperationException();
  172. }
  173. public void checkServerTrusted(X509Certificate[] chain, String authType)
  174. throws CertificateException {
  175. this.chain = chain;
  176. tm.checkServerTrusted(chain, authType);
  177. }
  178. }
  179. }

注意

不能将在自己电脑上生成的 jssecacerts 文件拷贝到服务器上去,如果拷贝上去不会起作用,仍然会报错。
服务器必须编译 InstallCert.java,去生成 jssecacerts 文件。

参考文献

  1. 解决PKIX问题:unable to find valid certification path to requested target
  2. Java发邮件-QQ服务器认证(A secure connection is requiered(such as ssl))

本文转载自:https://blog.csdn.net/frankcheng5143/article/details/52022289

评论

发表评论 点击刷新验证码

提示

该功能暂未开放