Fijación segura: javax.net.ssl.SSLPeerUnverifiedException: sin certificado de par

Hay docenas de publicaciones sobre este tema (javax.net.ssl.SSLPeerUnverifiedException: sin certificado de pares) pero no he encontrado nada que me funcione.

Muchas publicaciones (como esta , y esta ) “resuelven” esto al permitir que se acepten todos los certificados, pero, por supuesto, esta no es una buena solución para otra cosa que no sean las pruebas.

Otros parecen bastante localizados y no funcionan para mí. Realmente espero que alguien tenga una idea que me falte.

Entonces, mi problema: estoy probando en un servidor accesible solo a través de la red local, conectándose a través de HTTPS. Hacer la llamada que necesito a través del navegador funciona bien. No se queja de los certificados y si revisa los certificados, todo se ve bien.

Cuando pruebo en mi dispositivo Android, obtengo javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

Aquí está el código que lo llama:

 StringBuilder builder = new StringBuilder(); builder.append( /* stuff goes here*/ ); httpGet.setEntity(new UrlEncodedFormEntity(nameValuePairs)); ResponseHandler 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 

Mi código para 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; } 

Cualquier ayuda sería muy apreciada.

Editar

También por mencionar que tuve otros problemas de SSL con otra aplicación, pero al agregar la porción de SchemeRegistry se me arregló antes.

Editar 2

Hasta ahora solo he probado en Android 3.1, pero necesito que esto funcione en Android 2.2+ independientemente. Acabo de probar en el navegador en la pestaña de Android (Android 3.1) y se queja del certificado. Está bien en mi navegador de PC, pero no en el navegador de Android o en mi aplicación.

Editar 3

Resulta que el navegador iOS también se queja de eso. Estoy empezando a pensar que es un problema de la cadena de certificados que se describe aquí (el certificado SSL no es de confianza, solo en el móvil )

Resultó que mi código estaba bien y el problema era que el servidor no devolvía la cadena de certificados completa. Para obtener más información, consulte esta publicación de SO y esta publicación de superusuario:

El certificado SSL no es de confianza, solo en dispositivos móviles

https://superuser.com/questions/347588/how-do-ssl-chains-work

Pruebe el siguiente código: –

  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 nameValuePairs = new ArrayList(); nameValuePairs.add(new BasicNameValuePair(ConstantLib.locale, locale)); 

En mi caso, todo solía funcionar bien. De repente, después de 2 días, mi dispositivo no mostró ningún sitio https o enlace de imagen.

Después de algunas investigaciones, resultó que Mi configuración de tiempo no estaba actualizada en el dispositivo.

Cambié mi configuración de tiempo correctamente y funcionó.

Tuve esta excepción cuando utilicé el certificado autofirmado + dirección IP. Solo agregue estas líneas

 HostnameVerifier allHostsValid = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); 

y su HttpURLConnection funcionará. ¡Ese truco no está relacionado con la validación contra CA! Entonces, si especifico una CA incorrecta y uso ese truco, obtendré otra excepción. Entonces el host sigue siendo de confianza

Por las dudas, dejaré el código para especificar su propia CA aquí:

 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()); 

No te olvides de usar la función cool buildTypes y coloca diferentes CA para depurar / liberar en la carpeta res.