Как обрабатывать недопустимые SSL-сертификаты с помощью Apache HttpClient?

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

У меня есть: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 установлен «как есть» от off. по донесению Мне нужно подключиться к некоторому сайту через 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 и т. Д.).

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; } } } 

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

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

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

 % java InstallCert _web_site_hostname_ 

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

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

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

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

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

В дополнение к правильному ответу Паскаля Тивента другим способом является сохранение сертификата из 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 его, если его SecureProtocolSocketFactory использовать этот SSLContext . (Здесь есть примеры).

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

С 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 , который вы можете изменить, чтобы доверять всему (хотя вы должны это учитывать!)

Путь Apache HttpClient 4.5:

 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 отсутствует в имени пакета).

После того, как у вас есть Java Cert Store (используя отличный class InstallCert, созданный выше), вы можете получить java, чтобы использовать его, передав параметр javax.net.ssl.trustStore при запуске java.

Пример:

 java -Djavax.net.ssl.trustStore=/path/to/jssecacerts MyClassName 

Для 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. create() .register("http",PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslConnectionSocketFactory).build()); CloseableHttpClient client = HttpClients.custom() .setConnectionManager(connectionManager) .build(); 

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

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

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

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

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

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

EasySSLProtocolSocketFactory давал мне проблемы, поэтому я закончил реализацию своего собственного протокола ProtocolSocketFactory.

Сначала вам необходимо зарегистрировать его:

 Protocol.registerProtocol("https", new Protocol("https", new TrustAllSSLSocketFactory(), 443)); HttpClient client = new HttpClient(); ... 

Затем выполните ProtocolSocketFactory:

 class TrustAllSSLSocketFactory implements ProtocolSocketFactory { public static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{ new X509TrustManager() { public void checkClientTrusted(final X509Certificate[] certs, final String authType) { } public void checkServerTrusted(final X509Certificate[] certs, final String authType) { } public X509Certificate[] getAcceptedIssuers() { return null; } } }; private TrustManager[] getTrustManager() { return TRUST_ALL_CERTS; } public Socket createSocket(final String host, final int port, final InetAddress clientHost, final int clientPort) throws IOException { return getSocketFactory().createSocket(host, port, clientHost, clientPort); } @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException { return createSocket(host, port); } public Socket createSocket(final String host, final int port) throws IOException { return getSocketFactory().createSocket(host, port); } private SocketFactory getSocketFactory() throws UnknownHostException { TrustManager[] trustAllCerts = getTrustManager(); try { SSLContext context = SSLContext.getInstance("SSL"); context.init(null, trustAllCerts, new SecureRandom()); final SSLSocketFactory socketFactory = context.getSocketFactory(); HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory); return socketFactory; } catch (NoSuchAlgorithmException | KeyManagementException exception) { throw new UnknownHostException(exception.getMessage()); } } } 

Примечание. Это с HttpClient 3.1 и Java 8

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

Примечание. Возможно, вам стоит дважды проверить, что вы делаете, это небезопасная операция.

Используя InstallCert для генерации файла jssecacerts и do -Djavax.net.ssl.trustStore=/path/to/jssecacerts поработали.

Я использую httpclient 3.1.X, и это работает для меня

  try { SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManager trustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; sslContext.init(null, new TrustManager[]{trustManager}, null); SslContextSecureProtocolSocketFactory socketFactory = new SslContextSecureProtocolSocketFactory(sslContext,false); Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) socketFactory, 443));//同样会影响到HttpUtils } catch (Throwable e) { e.printStackTrace(); 

}

 public class SslContextSecureProtocolSocketFactory implements SecureProtocolSocketFactory { private SSLContext sslContext; private boolean verifyHostname; public SslContextSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) { this.verifyHostname = true; this.sslContext = sslContext; this.verifyHostname = verifyHostname; } public SslContextSecureProtocolSocketFactory(SSLContext sslContext) { this(sslContext, true); } public SslContextSecureProtocolSocketFactory(boolean verifyHostname) { this((SSLContext)null, verifyHostname); } public SslContextSecureProtocolSocketFactory() { this((SSLContext)null, true); } public synchronized void setHostnameVerification(boolean verifyHostname) { this.verifyHostname = verifyHostname; } public synchronized boolean getHostnameVerification() { return this.verifyHostname; } public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { SSLSocketFactory sf = this.getSslSocketFactory(); SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port, clientHost, clientPort); this.verifyHostname(sslSocket); return sslSocket; } public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { if(params == null) { throw new IllegalArgumentException("Parameters may not be null"); } else { int timeout = params.getConnectionTimeout(); Socket socket = null; SSLSocketFactory socketfactory = this.getSslSocketFactory(); if(timeout == 0) { socket = socketfactory.createSocket(host, port, localAddress, localPort); } else { socket = socketfactory.createSocket(); InetSocketAddress localaddr = new InetSocketAddress(localAddress, localPort); InetSocketAddress remoteaddr = new InetSocketAddress(host, port); socket.bind(localaddr); socket.connect(remoteaddr, timeout); } this.verifyHostname((SSLSocket)socket); return socket; } } public Socket createSocket(String host, int port) throws IOException, UnknownHostException { SSLSocketFactory sf = this.getSslSocketFactory(); SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port); this.verifyHostname(sslSocket); return sslSocket; } public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { SSLSocketFactory sf = this.getSslSocketFactory(); SSLSocket sslSocket = (SSLSocket)sf.createSocket(socket, host, port, autoClose); this.verifyHostname(sslSocket); return sslSocket; } private void verifyHostname(SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException { synchronized(this) { if(!this.verifyHostname) { return; } } SSLSession session = socket.getSession(); String hostname = session.getPeerHost(); try { InetAddress.getByName(hostname); } catch (UnknownHostException var10) { throw new UnknownHostException("Could not resolve SSL sessions server hostname: " + hostname); } X509Certificate[] certs = (X509Certificate[])((X509Certificate[])session.getPeerCertificates()); if(certs != null && certs.length != 0) { X500Principal subjectDN = certs[0].getSubjectX500Principal(); List cns = this.getCNs(subjectDN); boolean foundHostName = false; Iterator i$ = cns.iterator(); AntPathMatcher matcher = new AntPathMatcher(); while(i$.hasNext()) { String cn = (String)i$.next(); if(matcher.match(cn.toLowerCase(),hostname.toLowerCase())) { foundHostName = true; break; } } if(!foundHostName) { throw new SSLPeerUnverifiedException("HTTPS hostname invalid: expected \'" + hostname + "\', received \'" + cns + "\'"); } } else { throw new SSLPeerUnverifiedException("No server certificates found!"); } } private List getCNs(X500Principal subjectDN) { ArrayList cns = new ArrayList(); StringTokenizer st = new StringTokenizer(subjectDN.getName(), ","); while(st.hasMoreTokens()) { String cnField = st.nextToken(); if(cnField.startsWith("CN=")) { cns.add(cnField.substring(3)); } } return cns; } protected SSLSocketFactory getSslSocketFactory() { SSLSocketFactory sslSocketFactory = null; synchronized(this) { if(this.sslContext != null) { sslSocketFactory = this.sslContext.getSocketFactory(); } } if(sslSocketFactory == null) { sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); } return sslSocketFactory; } public synchronized void setSSLContext(SSLContext sslContext) { this.sslContext = sslContext; } 

}

Для HttpClient мы можем сделать это:

 SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom()); SSLContext.setDefault(ctx); String uri = new StringBuilder("url").toString(); HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { return true; } }; HttpClient client = HttpClientBuilder.create().setSSLContext(ctx) .setSSLHostnameVerifier(hostnameVerifier).build() 
  • Игнорирование сертификата SSL в Apache HttpClient 4.3
  • ASP.NET MVC RequireHttps только в производстве
  • Получение ошибки сертификата в IE и Chrome, но открывается в FireFox
  • Проблемы с использованием Maven и SSL за прокси-сервером
  • Сертификаты клиента Java через HTTPS / SSL
  • Присвоение имени домена локальному хосту для среды разработки
  • прием HTTPS-соединений с самозаверяющими сертификатами
  • Как найти версию SSL / TLS в Java
  • Delphi: idHttp + SSL
  • Как сохранить сертификат SSL удаленного сервера локально как файл
  • Android-шлюз, подписанный якорем HTTPS, для пути сертификации не найден
  • Interesting Posts

    Возможно ли увидеть все ссылки на объект во время выполнения?

    Как создать правило, которое перемещает электронное письмо в другой файл после его завершения?

    Как можно сделать брандмауэр Windows XP для блокировки определенного приложения?

    Java Swing – как показать панель поверх другой панели?

    Как изменить сочетание клавиш для переключения вкладок в Mac Terminal?

    Почему так долго требуется, чтобы компьютер ответил «неверным паролем», когда правильный пароль был близок к мгновенному?

    Spring-Batch без сохранения метаданных в базе данных?

    Что вы делаете, когда вам нужен поиск целых слов в Firefox?

    Может ли кто-нибудь объяснить мне Потенциальные Квантеры? (Обычные выражения)

    Замена ноутбука не увеличивает / уменьшает яркость экрана при смене источника питания

    c # datatable to csv

    Есть ли способ взломать пароль в проекте Excel VBA?

    Как читать весь stream в std :: string?

    Операция modulo на отрицательных числах в Python

    Как я могу сортировать ListBox, используя только XAML и без кода?

    Давайте будем гением компьютера.