JavaMail中的错误:PKIX路径构建失败,无法找到请求目标的有效认证路径。

36

我正在尝试在Android上构建一款电子邮件客户端应用,目前我想配置JavaMail部分。

我正在尝试与IMAP服务器建立连接,但我的代码似乎有问题... 这是我的代码:

package mailpackage;

import java.util.Properties;

import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;

public class Connection implements Runnable
{
    boolean done;

    public Connection()
    {
        this.done=false;
    }

    @Override
    public void run()
    {
        System.out.println("Hello from Connection Thread!");
        while(!done)
        {
            String host = "myhost";// change accordingly
            String mailStoreType = "imap";
            String username = "myusername";// change accordingly
            String password = "mypasswd";// change accordingly

            check(host, mailStoreType, username, password);

        }
    }

    public static void receiveEmail(String host, String storeType,  String username, String password)
{
    try
    {
        Properties properties = new Properties();  
        properties.put("mail.imap.com", host);  
        properties.put("mail.imap.starttls.enable","true");
        properties.put("mail.imap.auth", "true");  // If you need to authenticate

        // Use the following if you need SSL
        properties.put("mail.imap.socketFactory.port", 993);
        properties.put("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        properties.put("mail.imap.socketFactory.fallback", "false");

        Session emailSession = Session.getDefaultInstance(properties);  
        emailSession.setDebug(true);

        //2) create the IMAP store object and connect with the Imap server  
        IMAPStore emailStore = (IMAPStore) emailSession.getStore(storeType);

        emailStore.connect(host, username, password);  

        //3) create the folder object and open it  
        Folder emailFolder = emailStore.getFolder("INBOX");  
        emailFolder.open(Folder.READ_ONLY);  

        //4) retrieve the messages from the folder in an array and print it  
        Message[] messages = emailFolder.getMessages();  
        for (int i = 0; i <messages.length; i++) 
        {
            Message message = messages[i];  
            MimeMessage m = new MimeMessage(emailSession);
            m.setContent(((MimeMessage)messages[i]).getContent() , "text/plain; charset=UTF-8");
            System.out.println("---------------------------------");  
            System.out.println("Email Number " + (i + 1));  
            System.out.println("Subject: " + message.getSubject());  
            System.out.println("From: " + message.getFrom()[0]);  
            System.out.println("Text: " + message.getContent().toString());  
            m.writeTo(System.out);
        }  

        //5) close the store and folder objects  
        emailFolder.close(false);  
        emailStore.close();  

    } 
    catch (NoSuchProviderException e) {e.printStackTrace();}   
    catch (MessagingException e) {e.printStackTrace();}  
    catch (IOException e) {e.printStackTrace();}

}

    public void stopThread()
    {
        this.done=true;
    }
}

我这样从另一个类中调用线程:

connec=new Connection();
 (new Thread(connec)).start();

我遇到了以下错误:

javax.mail.MessagingException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target;
  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
    at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:571)
    at javax.mail.Service.connect(Service.java:288)
    at javax.mail.Service.connect(Service.java:169)
    at mailpackage.Connection.check(Connection.java:63)
    at mailpackage.Connection.run(Connection.java:33)
    at java.lang.Thread.run(Thread.java:744)
Caused by: 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
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:804)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:882)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:102)
    at com.sun.mail.util.TraceInputStream.read(TraceInputStream.java:110)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:254)
    at com.sun.mail.iap.ResponseInputStream.readResponse(ResponseInputStream.java:98)
    at com.sun.mail.iap.Response.<init>(Response.java:96)
    at com.sun.mail.imap.protocol.IMAPResponse.<init>(IMAPResponse.java:61)
    at com.sun.mail.imap.protocol.IMAPResponse.readResponse(IMAPResponse.java:135)
    at com.sun.mail.imap.protocol.IMAPProtocol.readResponse(IMAPProtocol.java:261)
    at com.sun.mail.iap.Protocol.<init>(Protocol.java:114)
    at com.sun.mail.imap.protocol.IMAPProtocol.<init>(IMAPProtocol.java:104)
    at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:538)
    ... 5 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1323)
    ... 23 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
    ... 29 more

我读到了有关PKIX路径错误的内容,其中提到将证书添加到Java存储中作为受信任证书,但我不确定这是否是解决方案,如果是的话,我也不知道如何操作。

// 我无法访问邮件服务器

有什么建议吗?谢谢!


4
我安装了Avast杀毒软件后遇到了这个问题。我找到了两种解决方案:1.在avast中禁用SSL扫描,2.将您的主机添加到Java邮件配置中的"mail.smtp.ssl.trust"。我确定这些都不是推荐的方法,但它们肯定是最简单的选项。 - pstanton
问题在于Sun/Oracle实现了自己的证书处理方式,没有办法指定不想使用它们,而是想使用例如openSSL或操作系统的默认实现。这是一个设计问题 - 或者你甚至可以认为它是一个错误。 - Wolfgang Fahl
7个回答

48

问题已解决!

解决方案如下:

首先通过openssl从邮件服务器获取自签名证书:

echo | openssl s_client -connect yoursever:port 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > yourcert.pem

将 yourcert.pem 文件保存到此路径 /Library/Java/Home/lib/security (在 macOSX 上),并按以下方式将证书文件放入 cacerts

keytool -keystore cacerts -importcert -alias youralias -file yourcert.pem

默认的密钥库密码为changeit。

您可以使用以下命令查看您所做的更改,该命令显示证书指纹。

keytool -list -keystore cacerts

之后,您应该将这些参数传递给VM。

(对于Windows和Linux,请在" "之间键入yourpath)

-Djavax.net.ssl.trustStore="/Library/Java/Home/lib/security/cacerts"

-Djavax.net.ssl.trustStorePassword="changeit"

用于调试:

-Djava.security.debug=certpath

-Djavax.net.debug=trustmanager


1
注意,在将VM参数放入eclipse.ini中时,请不要使用引号。 - bcoughlan
1
在此之后,您应该将这些参数传递给虚拟机。+1 最终这个问题被我解决了。 - Amani Kilumanga
在尝试运行命令> keytool -keystore cacerts -importcert -alias youralias -file yourcert.pem时,使用MacOS出现了java.lang.Exception:输入不是X.509证书。 - Tarun

42
你可以尝试升级javax.mail.jar库,网址为https://java.net/projects/javamail/pages/Home(当前版本为1.5.5),并添加以下代码:
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true); 
properties.put("mail.imap.ssl.trust", "*");
properties.put("mail.imap.ssl.socketFactory", sf);

5
应该给这个点赞,简单并避免了在密钥库中存储证书的所有麻烦。 - Pradyut Bhattacharya
7
为使它工作,我不得不将属性值中的"imap"替换为"imaps",但你得到了我的点赞。然而,这只应该用于测试!让用户决定是否信任该服务器,然后将该服务器添加到受信任主机列表中(目前还不知道如何实现此操作。一旦我知道了,我会发布答案)。 - MauganRa
3
这样做会避开最重要的安全功能。不要这么做。 - Monkey Supersonic
3
似乎需要将mail.imap替换为mail.smtp以适用于SMTP。 - Adir D
试图通过在客户端的信任存储中加载邮件服务器的信任证书来解决此问题是过度的。想想看,例如,如果您可以使用用户名和密码在gmail.com上登录,那么为什么在以编程方式访问邮件时需要将gmail的证书存储在客户端的信任存储中呢?两者都是程序。因此,以下是Bill Shannon发布的正确解决方案。只需将mail.protocol.ssl.trust添加到邮件服务器的DNS中,问题就解决了。 - undefined
显示剩余3条评论

7

这个JavaMail FAQ条目应该会有所帮助。

引用链接网站的内容:

问:当我通过SSL连接到我的邮件服务器时,我会收到一个异常,如“无法找到请求目标的有效认证路径”。

答:您的服务器可能使用测试证书或自签名证书,而不是由商业证书颁发机构签名的证书。 您需要将服务器的证书安装到您的信任存储中。 InstallCert程序将有所帮助。

或者,您可以将“mail.protocol.ssl.trust”属性设置为您的邮件服务器的主机名。 有关详细信息,请参阅协议提供程序包的javadocs。

此问题的其他常见原因包括:

  • 有防火墙或反病毒程序拦截您的请求。
  • JDK安装中出现了一些问题,导致它无法找到受信任的证书颁发机构的证书。
  • 您正在运行覆盖JDK受信任证书颁发机构列表的应用程序服务器。

谢谢,这很有帮助!但我仍然遇到相同的错误。我通过openssl成功获取了邮件服务器的证书,将其导入cacert文件中,但当我再次运行应用程序时,仍然出现相同的错误。 - fnkbz
我只能说你一定做错了什么。如果没有更多具体的细节,很难猜测你可能做错了什么。 - Bill Shannon
我使用MacOSX Mavericks。我使用openssl工具通过终端连接到邮件服务器,使用以下命令:echo | openssl s_client -connect server:port 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.pem并将文件保存为cert.pem。然后将cert.pem文件保存在/Library/Java/Home/lib/security路径下,并将cert文件放入cacerts中,如下所示:keytool -keystore cacerts -importcert -alias myalias -file cert.pem然后运行应用程序,但仍然收到相同的错误。您有什么想法吗? - fnkbz
keytool是否显示证书已存在?应用程序是否在同一台MacOS机器上运行?您在该机器上安装了多个JDK或JRE版本吗?应用程序是使用“java”命令从命令行运行,还是在应用服务器中运行?如果按照此处所述打开额外的调试输出,是否提供任何线索?它是否显示正在使用您安装的证书?您可能需要尝试FAQ中的InstallCert程序,以防它有所不同。 - Bill Shannon
我做了更改,但没有运气! - fnkbz
显示剩余5条评论

4

通过从Java 7获取证书文件的简单方法来解决此问题

从以下Java 7目录中复制“cacerts”文件即可。

C:\Program Files\Java\jdk1.7.0_79\jre\lib\security

把它复制并粘贴到Java 6目录中。
C:\Program Files\Java\jdk1.6.0\jre\lib\security

1

我花了很多时间搜索解决方案,这篇文章对我非常有帮助。我遇到了同样的问题。我按照这里的方法创建了一个pem文件,然后使用以下命令将cert文件.pem嵌入cacert文件(复制为TrustStore.jks):

keytool.exe -import -noprompt -keystore TrustStore.jks -storepass changeit ^ -alias DOMAINNAME -file MYCERTFILE.pem

(DOMAINNAME必须替换为主机名 -这个技巧非常重要-,MYCERTFILE由最近创建的文件替换...)

我希望这个解决方案能帮助到其他人。


0
我也遇到过这个问题,当与邮件服务器通信时。然而,根本原因是该服务器(Exchange 2013)既应用了真实证书又应用了自签名证书。适当的做法是从服务器上删除自签名证书,因为它占据主导地位并阻止了真实证书的使用。

0
检查将此行添加到邮件属性中:
properties.put("mail.smtp.ssl.trust", "serveraddress");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接