Ошибка «Нет равных сертификатов» в Android 2.3, но НЕ в 4

Получение "javax.net.ssl.SSLPeerUnverifiedException: No peer certificate error" в эмуляторе под управлением Android 2.3, но НЕ в 4. В 4 он работает отлично. Я пытаюсь подключиться к серверу через https. Он использует действительный сертификат Thawte, отлично работает во всех браузерах и Android 3 и 4.

Если у кого-нибудь есть помощь по коду, ПОЖАЛУЙСТА и спасибо. Кроме того, если у кого-то есть предложения по безопасному обходному пути, я был бы признателен. Я все еще участвую, и я занимаюсь этой проблемой неделю. Это должно закончиться, поэтому я могу продолжать работать и учиться. Urgh.

Вот код HttpCLient, любезно предоставленный Антуаном Хауком ( http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/ ):

  import java.io.InputStream; import java.security.KeyStore; import java.security.cert.CertificateException; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.security.cert.X509Certificate; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.SingleClientConnManager; import android.content.Context; public class MyHttpClient extends DefaultHttpClient { final Context context; public MyHttpClient(Context context) { this.context = context; } @Override protected ClientConnectionManager createClientConnectionManager() { SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); // Register for port 443 our SSLSocketFactory with our keystore // to the ConnectionManager registry.register(new Scheme("https", newSslSocketFactory(), 443)); return new SingleClientConnManager(getParams(), registry); } private SSLSocketFactory newSslSocketFactory() { try { // Get an instance of the Bouncy Castle KeyStore format KeyStore trusted = KeyStore.getInstance("BKS"); // Get the raw resource, which contains the keystore with // your trusted certificates (root and any intermediate certs) InputStream in = context.getResources().openRawResource(R.raw.my_cert); try { // Initialize the keystore with the provided trusted certificates // Also provide the password of the keystore trusted.load(in, "my_pass".toCharArray()); } finally { in.close(); } // Pass the keystore to the SSLSocketFactory. The factory is responsible // for the verification of the server certificate. SSLSocketFactory sf = new SSLSocketFactory(trusted); // Hostname verification from certificate // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); return sf; } catch (Exception e) { throw new AssertionError(e); } } } 

И вот код, который его создает:

 DefaultHttpClient client = new MyHttpClient(getApplicationContext()); HttpPost post = new HttpPost(server_login_url); List  parameters = new ArrayList (); parameters.add(new BasicNameValuePair("username", user)); parameters.add(new BasicNameValuePair("password", pass)); try { post.setEntity(new UrlEncodedFormEntity(parameters, HTTP.UTF_8)); } catch (UnsupportedEncodingException e2) { // TODO Auto-generated catch block Log.d(DEBUG_TAG, "in UnsupportedEncodingException - " + e2.getMessage()); e2.printStackTrace(); } // Execute the GET call and obtain the response HttpResponse getResponse = null; try { getResponse = client.execute(post); } catch (ClientProtocolException e) { // TODO Auto-generated catch block // Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show(); Log.d(DEBUG_TAG, "in ClientProtocolException - " + e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block // Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show(); Log.d(DEBUG_TAG, "in client.execute IOException - " + e.getMessage()); e.printStackTrace(); } 

Ошибка попадает в блок IOException. Вот стек:

 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:258) org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93) org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:381) org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:164) org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:359) org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) org.ffb.tools.SplashActivity$LoginTask.makeConnection(SplashActivity.java:506) org.ffb.tools.SplashActivity$LoginTask.doLogin(SplashActivity.java:451) org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.java:439) org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.java:1) android.os.AsyncTask$2.call(AsyncTask.java:185) java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306) java.util.concurrent.FutureTask.run(FutureTask.java:138) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) java.lang.Thread.run(Thread.java:1019) 

Вот порядок цепочек (из команды openssl):

Я думаю, цепочка выглядит неплохо.

  i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA 1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA 2 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/[email protected] 

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

Итоговый контрольный список HTTPS / SSL для Android 2.3:

  • Если ваш CA находится в списке 2.3 доверенных ЦС Android, а Thawte – нет необходимости включать сертификат в приложение.
  • Android 2.3 не поддерживает идентификацию имени сервера, поэтому, если ваш сервер полагается на нее для подтверждения SSL, Android может не получать сертификаты, которые вы ожидаете.
  • У вас есть цепочка сертификатов на установленном сервере и правильно ли она заказана? Большинство браузеров обрабатывают цепочки сертификатов вне очереди, но Android 2.3 этого не делает. Ответ bdc в упомянутом выше streamе описывает, как проверить правильность вашего SSL-сертификата и цепочки с помощью «openssl s_client -connect yourserver.com:443».
  • Когда вы выкапываете это старое устройство 2.3, которое у вас есть в нижнем ящике, убедитесь, что его дата и время установлены правильно после слишком долгого бессилия.

У меня была такая же проблема, как и вы. Все отлично работало с android> 3.X, но когда я попытался с некоторыми (но не все!) Устройствами 2.3.X, я получил это знаменитое исключение «Отсутствие ошибки сертификата».

Я много вырыл через stackoverflow и другие блоги, но я не нашел ничего, что работало на этих «мошеннических» устройствах (в моем случае: правильное использование truststore, не требуется sni, правильный порядок цепочки сертификатов на сервере и т. Д.) ,

Это похоже на то, что Apache HttpClient от Android просто не работает правильно на некоторых устройствах 2.3.X. Исключение “no peer Certificates” происходило слишком рано, чтобы даже получить собственный код верификатора имени хоста, поэтому решение вроде этого не работало для меня.

Вот мой код:

 KeyStore trustStore = KeyStore.getInstance("BKS"); InputStream is = this.getAssets().open("discretio.bks"); trustStore.load(is, "discretio".toCharArray()); is.close(); SSLSocketFactory sockfacto = new SSLSocketFactory(trustStore); sockfacto.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", sockfacto, 443)); SingleClientConnManager mgr = new SingleClientConnManager(httpParameters, schemeRegistry); HttpClient client = new DefaultHttpClient(mgr, httpParameters); HttpGet request = new HttpGet(url); HttpResponse response = client.execute(request); 

Поэтому я переписал все, используя javax.net.ssl.HttpsURLConnection, и теперь он работает на всех проверенных мной устройствах (от 2.3.3 до 4.X).

Вот мой новый код:

 KeyStore trustStore = KeyStore.getInstance("BKS"); InputStream is = this.getAssets().open("discretio.bks"); trustStore.load(is, "discretio".toCharArray()); is.close(); TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); tmf.init(trustStore); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); URL request = new URL(url); HttpsURLConnection urlConnection = (HttpsURLConnection) request.openConnection(); //ensure that we are using a StrictHostnameVerifier urlConnection.setHostnameVerifier(new StrictHostnameVerifier()); urlConnection.setSSLSocketFactory(context.getSocketFactory()); urlConnection.setConnectTimeout(15000); InputStream in = urlConnection.getInputStream(); //I don't want to change my function's return type (laziness) so I'm building an HttpResponse BasicHttpEntity res = new BasicHttpEntity(); res.setContent(in); HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, urlConnection.getResponseCode(), ""); resp.setEntity(res); 

Целая цепочка сертификатов и проверка имени хоста (я их тестировал). Если кто-то хочет лучше взглянуть на изменение, вот разница

Комментарии приветствуются, я надеюсь, что это поможет некоторым людям.

Другим источником этого сообщения может быть неправильная установка даты и времени, например, при использовании устройства, которое прошло несколько месяцев без питания. Довольно тривиально, но его трудно обнаружить.

Какие сертификаты вы загружаете из R.raw.my_cert ? Эта ошибка говорит о наличии неверно сконфигурированного сервера – не устанавливая первичные и вторичные средние ЦС Thawte – или вы не загружаете и не доверяете правильной цепочке сертификатов.

Проверка сертификата (или, точнее, цепочки) логики в (по крайней мере) Android 2.3 неисправна.

Вот что я заметил:

  • Если сервер TLS в своей цепочке сертификатов предоставляет только сертификат сервера (не самонастраиваемый или самонастраиваемый), вы можете поместить сертификат сервера в хранилище ключей, и проверка будет успешной.

  • Если сервер TLS в своей цепочке сертификатов предоставляет также промежуточный сертификат CA, тогда в хранилище ключей вы должны поместить только корневой сертификат ЦС и убедиться, что хранилище ключей НЕ содержит сертификаты сервера и промежуточного ЦС (в противном случае проверка не будет выполнена случайным образом).

  • Если сервер TLS в своей цепочке сертификатов предоставляет промежуточные и корневые сертификаты CA в правильном порядке, вам нужно только убедиться, что корневой сертификат CA находится в хранилище ключей (неважно, имеются ли сертификаты сервера и промежуточного центра сертификации).

Таким образом, «правильный / надежный» способ справиться с этим заключается в том, чтобы включить в хранилище ключей только корневые сертификаты ЦС и конфигурацию визового сервера для «Нет равноправного сертификата» – в случае, если цепочка сертификатов сервера не предоставляет промежуточные сертификаты ЦС или сертификаты находятся в неправильном порядке , Вы можете протестировать сервер, используя https://www.ssllabs.com/ssltest/ .

  • Присвоение имени домена локальному хосту для среды разработки
  • Запрос Python, бросающий SSLError
  • Как использовать ssh / scp между непубличными машинами
  • Java7 Отказ от доверия сертификату в хранилище доверия
  • Как реализовать идентификацию имени сервера (SNI)
  • Ошибка SSL: certificate_verify_failed при очистке https://www.thenewboston.com/
  • Почему запросы Python игнорируют параметр проверки?
  • Как подключиться к защищенному сайту с использованием SSL в Java с файлом pkcs12?
  • Java: загрузка SSL Keystore через ресурс
  • Последствия безопасности отключения CURLOPT_SSL_VERIFYHOST (libcurl / openssl)
  • Как извлечь CN из X509Certificate в Java?
  • Давайте будем гением компьютера.