Как обрабатывать недействительные сертификаты SSL с Apache HttpClient? - PullRequest
116 голосов
/ 01 декабря 2009

Я знаю, есть много разных вопросов и так много ответов об этой проблеме ... Но я не могу понять ...

У меня: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 установлена ​​"как есть" с выключенного компьютера. по донесению Мне нужно подключиться к некоторому сайту через HTTPS. Для этого я использую Apache HttpClient.

Из учебника я прочитал:

"После правильной установки JSSE безопасная HTTP-связь по SSL должна выглядеть как
просто как простое HTTP-соединение. "И некоторые примеры:

HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/"); 
try { 
  httpclient.executeMethod(httpget);
  System.out.println(httpget.getStatusLine());
} finally {
  httpget.releaseConnection();
}

К настоящему времени я пишу это:

HttpClient client = new HttpClient();

HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);

try {
    int status = client.executeMethod(get);
    System.out.println(status);

    BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
    int r=0;byte[] buf = new byte[10];
    while((r = is.read(buf)) > 0) {
        System.out.write(buf,0,r);
    }

} catch(Exception ex) {
    ex.printStackTrace();
}

В результате у меня есть набор ошибок:

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:1627)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
        at simpleapachehttp.Main.main(Main.java:41)
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:302)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
        at sun.security.validator.Validator.validate(Validator.java:235)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
        ... 17 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:191)
        at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
        ... 23 more

Что я должен сделать, чтобы создать простейшее соединение SSL? (Вероятно, без KeyManager, Trust Manager и т. Д. Пока).

Ответы [ 18 ]

152 голосов
/ 01 декабря 2009

https://mms.nw.ru использует самозаверяющий сертификат, который явно не содержится в наборе доверенных менеджеров по умолчанию.

Вам понадобится одно из следующих:

  • Настройте SSLContext с TrustManager, который принимает любой сертификат (см. Ниже)

  • Настройте SSLContext с соответствующим хранилищем доверия, включающим ваш сертификат

  • Добавьте сертификат для этого сайта в хранилище доверенных сертификатов Java по умолчанию.

Вот пример программы, которая создает (в основном бесполезный) контекст SSL, который принимает любой сертификат:

import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SSLTest {

    public static void main(String [] args) throws Exception {
        // configure the SSLContext with a TrustManager
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        URL url = new URL("https://mms.nw.ru");
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        });
        System.out.println(conn.getResponseCode());
        conn.disconnect();
    }

    private static class DefaultTrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
}
44 голосов
/ 01 декабря 2009

https://mms.nw.ru вероятно использует сертификат, не выданный органом по сертификации. Следовательно, вам нужно добавить сертификат в ваше доверенное хранилище ключей Java, как описано в , не удалось найти действительный путь сертификации для запрошенной цели :

При работе на клиенте, который работает с запущенным сервером SSL протокол https, вы можете получить ошибку не может найти действительное свидетельство путь к запрошенной цели, если сертификат сервера не выдается центр сертификации, но сам подписано или выдано частной CMS.

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

% java InstallCert _web_site_hostname_

Эта программа открыла соединение с указанный хост и запустил SSL рукопожатие. Это напечатало исключение трассировка стека произошедшей ошибки и показывает вам сертификаты, используемые сервер. Теперь он предлагает добавить сертификат вашего доверенного хранилища ключей.

Если вы передумали, введите «Д». Если вы действительно хотите добавить сертификат, введите «1» или другой номера для добавления других сертификатов, даже сертификат CA, но вы обычно не хочу этого делать Когда у вас есть сделал свой выбор, программа будет отобразить полный сертификат и затем добавил его в хранилище ключей Java с именем 'jssecacerts' в текущем каталог.

Чтобы использовать его в своей программе, либо настроить JSSE для использования в качестве доверия сохранить или скопировать его в свой Каталог $ JAVA_HOME / jre / lib / security. Если вы хотите, чтобы все приложения Java признать сертификат доверенным и не только JSSE, вы могли бы также переписать файл Cacerts в этом каталог.

После всего этого JSSE сможет завершить рукопожатие с хозяином, который вы можете проверить, запустив запрограммируйте снова.

Чтобы получить более подробную информацию, вы можете проверить Блог Leeland Больше не найден действительный путь сертификации к запрашиваемому цель '

23 голосов
/ 02 мая 2012

В дополнение к правильному ответу Pascal Thivent, другим способом является сохранение сертификата из Firefox (Просмотр сертификата -> Сведения -> экспорт) или openssl s_client и импорт его в хранилище доверия.

Это следует делать только в том случае, если у вас есть способ проверить этот сертификат. В противном случае, сделайте это при первом подключении, оно, по крайней мере, выдаст ошибку, если сертификат неожиданно изменится при последующих подключениях.

Чтобы импортировать его в доверенное хранилище, используйте:

keytool -importcert -keystore truststore.jks -file servercert.pem

По умолчанию хранилище доверия по умолчанию должно быть lib/security/cacerts, а его пароль - changeit, подробности см. Справочное руководство JSSE .

Если вы не хотите разрешить этот сертификат глобально, но только для этих соединений, для него можно создать SSLContext:

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../truststore.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();

tmf.init(ks);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

Затем вам нужно настроить его для Apache HTTP Client 3.x, внедрив его, если его SecureProtocolSocketFactory использовать для этого SSLContext. (Есть примеры здесь ).

Apache HTTP Client 4.x (кроме самой ранней версии) имеет прямую поддержку для передачи SSLContext.

11 голосов
/ 07 августа 2015

Apache HttpClient 4.5 way:

org.apache.http.ssl.SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
sslContextBuilder.loadTrustMaterial(new org.apache.http.conn.ssl.TrustSelfSignedStrategy());
SSLContext sslContext = sslContextBuilder.build();
org.apache.http.conn.ssl.SSLConnectionSocketFactory sslSocketFactory =
        new SSLConnectionSocketFactory(sslContext, new org.apache.http.conn.ssl.DefaultHostnameVerifier());

HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSocketFactory);
httpClient = httpClientBuilder.build();

ПРИМЕЧАНИЕ: org.apache.http.conn.ssl.SSLContextBuilder равно устарело и org.apache.http.ssl.SSLContextBuilder является новым (уведомление conn отсутствует в имени пакета последнего).

10 голосов
/ 01 декабря 2009

С http://hc.apache.org/httpclient-3.x/sslguide.html:

Protocol.registerProtocol("https", 
new Protocol("https", new MySSLSocketFactory(), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
try {
  httpclient.executeMethod(httpget);
      System.out.println(httpget.getStatusLine());
} finally {
      httpget.releaseConnection();
}

Где можно найти пример MySSLSocketFactory здесь . Он ссылается на TrustManager, который вы можете изменить, чтобы доверять всему (хотя вы должны учитывать это!)

6 голосов
/ 17 августа 2017

Для Apache HttpClient 4.5+ и Java8:

SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial((chain, authType) -> true).build();

SSLConnectionSocketFactory sslConnectionSocketFactory =
        new SSLConnectionSocketFactory(sslContext, new String[]
        {"SSLv2Hello", "SSLv3", "TLSv1","TLSv1.1", "TLSv1.2" }, null,
        NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom()
        .setSSLSocketFactory(sslConnectionSocketFactory)
        .build();

Но если ваш HttpClient использует ConnectionManager для поиска соединения, например, как это:

 PoolingHttpClientConnectionManager connectionManager = new 
         PoolingHttpClientConnectionManager();

 CloseableHttpClient client = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();

HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory) не действует , проблема не решена.

Поскольку HttpClient использует указанный connectionManager для поиска соединения, а указанный connectionManager не зарегистрировал наш настроенный SSLConnectionSocketFactory. Для решения этой проблемы следует зарегистрировать настроенный SSLConnectionSocketFactory в ConnectionManager. Правильный код должен выглядеть так:

PoolingHttpClientConnectionManager connectionManager = new 
    PoolingHttpClientConnectionManager(RegistryBuilder.
                <ConnectionSocketFactory>create()
      .register("http",PlainConnectionSocketFactory.getSocketFactory())
      .register("https", sslConnectionSocketFactory).build());

CloseableHttpClient client = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();
4 голосов
/ 02 декабря 2009

Когда у вас есть хранилище сертификатов Java (с помощью созданного выше класса great InstallCert), вы можете заставить его использовать Java, передав параметр "javax.net.ssl.trustStore" при запуске Java. ,

Ex:

java -Djavax.net.ssl.trustStore=/path/to/jssecacerts MyClassName
3 голосов
/ 25 августа 2011

Еще одна проблема, с которой вы можете столкнуться с самозаверяющими тестовыми сертификатами:

java.io.IOException: неверное имя хоста HTTPS: должно быть ...

Эта ошибка возникает при попытке доступа к URL-адресу HTTPS. Возможно, вы уже установили сертификат сервера в хранилище ключей вашего JRE. Но эта ошибка означает, что имя сертификата сервера не совпадает с фактическим доменным именем сервера, указанным в URL-адресе. Обычно это происходит, когда вы используете сертификат, не выданный центром сертификации.

В этом примере показано, как написать HttpsURLConnection DefaultHostnameVerifier, игнорирующий имя сервера сертификатов:

http://www.java -samples.com / showtutorial.php? Tutorialid = 211

2 голосов
/ 15 февраля 2019

хочу вставить ответ сюда:

в Apache HttpClient 4.5.5

Как обработать недействительный сертификат SSL с клиентом Apache 4.5.5?

HttpClient httpClient = HttpClients
            .custom()
            .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
            .build();
2 голосов
/ 30 января 2012

Чтобы легко добавить хосты, которым вы доверяете, без выполнения всех проверок, попробуйте код здесь: http://code.google.com/p/self-signed-cert-trust-manager/.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...