Java.io.IOException: имя хоста не проверено
Я пытаюсь подключиться к URL-адресу из моего приложения Android в Andorid Version 4.1.1, и я получаю сообщение об ошибке, указанное в заголовке моего вопроса, но когда я попытался подключить тот же URL-адрес от Andorid версии 4.0. 4 или 3.1, все работает нормально.
Фрагмент кода:
try {
.
.
.
URL url = new URL(urlStr);
Log.i(TAG,"[ URL ] " + urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int size = conn.getContentLength();
int responsecode = conn.getResponseCode();
Log.d(TAG, "Responsecode: " + responsecode);
.
.
.
} catch (Exception e) {
e.printStackTrace();
}
private static void trustAllHosts() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
System.out.println("IOException : HTTPSRequest::trustAllHosts");
e.printStackTrace();
}
}
Но здесь я понимаю одно: "Возможно, сертификат - это самозаверяющие сертификаты и не включает их в KeyStore.
Я не понимаю, почему это исключение происходит только в ОС Android Verison 4.1.1
Спасибо.
FULL STACK TRACE
01-31 10:26:08.348: W/System.err(3158): java.io.IOException: Hostname <URL> was not verified
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:289)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:239)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:273)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpURLConnectionImpl.getHeaderField(HttpURLConnectionImpl.java:130)
01-31 10:26:08.348: W/System.err(3158): at java.net.URLConnection.getHeaderFieldInt(URLConnection.java:544)
01-31 10:26:08.348: W/System.err(3158): at java.net.URLConnection.getContentLength(URLConnection.java:316)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpsURLConnectionImpl.getContentLength(HttpsURLConnectionImpl.java:191)
01-31 10:26:08.348: W/System.err(3158): at com.ih.util.HelpVideoServices$downloadTask.run(HelpVideoServices.java:172)
Ответы
Ответ 1
Если вы работаете с сертификатами, которые ничего не значат, и вы хотите обойти их, вам также нужно добавить нулевой верификатор имени хоста, чтобы этот код работал
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new NullX509TrustManager()}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
И код для хоста:
import javax.net.ssl.HostnameVerifier ;
import javax.net.ssl.SSLSession;
public class NullHostNameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
Log.i("RestUtilImpl", "Approving certificate for " + hostname);
return true;
}
}
Это нужно запустить один раз, но если вы вносите изменения в объект подключения, вам может потребоваться запустить его снова.
Ответ 2
В дополнение к ответу @Noam это полный пример:
/**
* Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
* aid testing on a local box, not for use on production.
*/
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
// not implemented
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
// not implemented
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
Надеюсь, что это поможет
Ответ 3
Это может произойти из-за того, что CN (общее имя), которое вы объявили на своем SSL, не обрабатывает фактический URL-адрес, по которому вы также отправляете свой HTTP-запрос.
Если это так, создайте новый SSL и введите currect CN. Это должно решить проблему.
Ответ 4
Я столкнулся с этой проблемой в 4.1.1 и 4.1.2, используя HTTPSUrlConnection.
После некоторого разговора я обнаружил, что это связано с тем, что сервер Apache, с которым я имею дело, имеет несколько виртуальных хостов, обслуживающих трафик https, в результате чего
Ответ 5
Android не может настроить SSL-соединение, я полагаю. Возможно, ваш сертификат для другого имени хоста, а не тот, с которым вы устанавливаете соединение. Прочитайте docs здесь и здесь.
Ответ 6
возможно, что ваша проблема заключается в том, что ваш URL был разрешен с помощью "https". вы должны преобразовать все строковые URL-адреса в "http", и он будет работать.
EDIT:
SchemeRegistry schemeRegistry = new SchemeRegistry ();
schemeRegistry.register (new Scheme ("http",
PlainSocketFactory.getSocketFactory (), 80));
schemeRegistry.register (new Scheme ("https",
new CustomSSLSocketFactory (), 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager (
params, schemeRegistry);
return new DefaultHttpClient (cm, params);
CustomSSLSocketFactory:
public class CustomSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory
{
private SSLSocketFactory FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory ();
public CustomSSLSocketFactory ()
{
super(null);
try
{
SSLContext context = SSLContext.getInstance ("TLS");
TrustManager[] tm = new TrustManager[] { new FullX509TrustManager () };
context.init (null, tm, new SecureRandom ());
FACTORY = context.getSocketFactory ();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public Socket createSocket() throws IOException
{
return FACTORY.createSocket();
}
// TODO: add other methods like createSocket() and getDefaultCipherSuites().
// Hint: they all just make a call to member FACTORY
}
FullX509TrustManager - это класс, который реализует javax.net.ssl.X509TrustManager, но ни один из методов не выполняет какую-либо работу, получите образец [здесь] [1].
Удачи!
Ответ 7
Из документации Amazon:
Ограничения в ковше
"При использовании виртуальных хостинговых ведер с SSL, сертификат Wild Card SSL соответствует только кодам, которые не содержат периодов. Чтобы обойти это, используйте HTTP или напишите собственную логику проверки сертификата.
Самый простой способ - создать уникальное имя ведра без периодов:
Вместо "bucketname.mycompany.com", что-то вроде "bucketnamemycompany" или любого другого DNS-совместимого имени ведра.