Безопасное исправление: javax.net.ssl.SSLPeerUnverifiedException: нет однорангового сертификата
Есть несколько сообщений об этой проблеме (javax.net.ssl.SSLPeerUnverifiedException: no peer certificate), но я не нашел ничего, что сработало бы для меня.
Многие сообщения (например this и this) "разрешают" это, разрешая всем сертификатам но, конечно, это нехорошее решение для чего угодно, кроме тестирования.
Другие кажутся довольно локализованными и не работают для меня. Я действительно надеюсь, что у кого-то есть понимание, которое мне не хватает.
Итак, моя проблема: я тестирую на сервере, доступном только через локальную сеть, подключаясь через HTTPS. Выполнение вызова, которое мне нужно через браузер, отлично работает. Не жалуйтесь на сертификаты, и если вы проверяете сертификаты, все выглядит хорошо.
Когда я пытаюсь использовать Android-устройство, я получаю javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
Здесь код, который его вызывает:
StringBuilder builder = new StringBuilder();
builder.append( /* stuff goes here*/ );
httpGet.setEntity(new UrlEncodedFormEntity(nameValuePairs));
ResponseHandler<String> responseHandler = new BasicResponseHandler();
// Execute HTTP Post Request. Response body returned as a string
HttpClient httpClient = MyActivity.getHttpClient();
HttpGet httpGet = new HttpGet(builder.toString());
String jsonResponse = httpClient.execute(httpGet, responseHandler); //Line causing the Exception
Мой код для MyActivity.getHttpClient()
:
protected synchronized static HttpClient getHttpClient(){
if (httpClient != null)
return httpClient;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, TIMEOUT_CONNECTION);
HttpConnectionParams.setSoTimeout(httpParameters, TIMEOUT_SOCKET);
HttpProtocolParams.setVersion(httpParameters, HttpVersion.HTTP_1_1);
//Thread safe in case various AsyncTasks try to access it concurrently
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(httpParameters, schemeRegistry);
httpClient = new DefaultHttpClient(cm, httpParameters);
CookieStore cookieStore = httpClient.getCookieStore();
HttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
return httpClient;
}
Любая помощь будет высоко оценена.
Edit
Также просто упомянуть, что у меня были другие проблемы с SSL с другим приложением, но добавление части SchemeRegistry
исправлено для меня раньше.
Изменить 2
До сих пор я тестировал только на Android 3.1, но мне нужно, чтобы он работал на Android 2.2+ независимо. Я только что протестировал в браузере на вкладке Android (Android 3.1), и он жалуется на сертификат. Это нормально в моем браузере ПК, но не в браузере Android или в моем приложении.
Изменить 3
Оказывается, браузер iOS также жалуется на это. Я начинаю думать, что проблема с цепочкой сертификатов, описанная здесь (SSL-сертификат не доверяет - только на мобильных устройствах)
Ответы
Ответ 1
Оказывается, мой код был в порядке, и проблема заключалась в том, что сервер не возвращал полную цепочку сертификатов. Для получения дополнительной информации см. Этот пост SO и этот пост суперпользователя:
Сертификат SSL не поддерживается - только на мобильных устройствах
https://superuser.com/questions/347588/how-do-ssl-chains-work
Ответ 2
Попробуйте ввести код: -
BasicHttpResponse httpResponse = null;
HttpPost httppost = new HttpPost(URL);
HttpParams httpParameters = new BasicHttpParams();
// Set the timeout in milliseconds until a connection is
// established.
int timeoutConnection = ConstantLib.CONNECTION_TIMEOUT;
HttpConnectionParams.setConnectionTimeout(httpParameters,
timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = ConstantLib.CONNECTION_TIMEOUT;
HttpConnectionParams
.setSoTimeout(httpParameters, timeoutSocket);
DefaultHttpClient httpClient = new DefaultHttpClient(
httpParameters);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair(ConstantLib.locale,
locale));
Ответ 3
В моем случае все работало нормально. Внезапно через 2 дня у моего устройства не было никаких ссылок на сайт https или ссылки на изображение.
После некоторого расследования выясняется, что настройки My time не были обновлены на устройстве.
Я правильно изменил настройки времени и работал.
Ответ 4
У меня было это исключение, когда я использовал самоподписанный сертификат + ip-адрес. Просто добавьте эти строки
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
и ваше HttpURLConnection будет работать.
Этот трюк не связан с проверкой CA! Поэтому, если я укажу неправильный CA и использую этот трюк, я получу другое исключение. Таким образом, хост остается доверенным
На всякий случай, я оставлю код для указания вашего собственного ЦС здесь:
String certStr = context.getString(R.string.caApi);
X509Certificate ca = SecurityHelper.readCert(certStr);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("caCert", ca);
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
Не забудьте использовать классные функции buildTypes и поместить различные CA для отладки/выпуска в папке res.