Ошибка в JavaMail: при создании пути PKIX не удалось найти действительный путь сертификации к запрошенной цели

Я пытаюсь создать клиентское приложение для электронной почты в 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 в качестве доверенного сертификата, но я не знаю, является ли это решением для этого, и если это так, я не знаю, как это сделать.

// У меня нет доступа к почтовому серверу

Какие-либо предложения? Спасибо!

6 ответов

Решение

Хорошо, проблема решена!

Решение заключается в следующем:

Сначала получите самоподписанный сертификат с почтового сервера через 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

После этого вы должны передать эти аргументы в ВМ

(для 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

Вы можете попробовать обновить библиотеку 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);

Эта запись FAQ по JavaMail должна помочь.

Цитируемый текст со ссылочного сайта:

В: При подключении к моему почтовому серверу через SSL я получаю исключение, например "невозможно найти действительный путь сертификации для запрашиваемой цели".

A: Ваш сервер, вероятно, использует тестовый сертификат или самозаверяющий сертификат вместо сертификата, подписанного коммерческим центром сертификации. Вам нужно будет установить сертификат сервера в хранилище доверенных сертификатов. Программа InstallCert поможет.

Кроме того, вы можете установить в свойстве mail.protocol.ssl.trust имя хоста вашего почтового сервера. Посмотрите javadocs для пакетов поставщика протокола для деталей.

Другие распространенные причины этой проблемы:

  • Там брандмауэр или антивирусная программа перехватывает ваш запрос.
  • В вашей установке JDK что-то не так, что он не может найти сертификаты для доверенных центров сертификации.
  • Вы работаете на сервере приложений, который переопределил список доверенных центров сертификации JDK.

Простой способ решить эту проблему путем получения файла сертификата из Java 7

скопируйте файл "cacerts" из следующего каталога Java 7

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

и вставьте его в каталог Java 6

C:\Program Files\Java\jdk1.6.0\jre\lib\security

Я потерял так много дней в поисках решения, и этот пост мне помог. У меня такая же проблема. Я создал файл pem, как здесь, а затем, файл сертификата.pem, был инкрустирован в файл cacert (копия с именем TrustStore.jks) с помощью этой команды:

keytool.exe -import -noprompt -keystore TrustStore.jks -storepass changeit ^ -alias ДОМЕННОЕ ИМЯ -файл MYCERTFILE.pem

(DOMAINNAME необходимо заменить именем хоста - этот трюк очень важен, а MYCERTFILE - файлом недавнего создания...)

Я надеюсь, что это решение может кому-нибудь помочь.

Я также столкнулся с этой проблемой при общении с почтовым сервером. Однако основной причиной было то, что к серверу (Exchange 2013) был применен как реальный сертификат, так и самозаверяющий. Подходящим способом было удалить самоподписанный на сервере, потому что он имел приоритет и блокировал настоящий сертификат.

Я должен был заменить:

props.setProperty("mail.smtp.starttls.enable", "true");
props.setProperty("mail.smtp.auth", "true");

с:

props.put("mail.smtp.starttls.enable", true);
props.put("mail.smtp.auth", true);

избавиться от ошибки.

Обратите внимание, что я использую Jetty 9, который включает в себя древнюю версию javamail.

Я отправил это, потому что ошибка не должна означать, что с сертификатом что-то не так.

Другие вопросы по тегам