raíz not navegador los gratis found for crear certification certificados certificado causarán autofirmados autofirmado advertencias android ssl https httpclient ca

android - not - aceptar conexiones HTTPS con certificados autofirmados



ssl android (12)

A continuación, le mostramos cómo puede agregar certificados adicionales a su KeyStore para evitar este problema: Confiar en todos los certificados que usan HttpClient a través de HTTPS

No le preguntará al usuario como usted lo pide, pero hará que sea menos probable que el usuario se encuentre con un error de "Certificado de servidor no confiable".

Estoy tratando de hacer conexiones HTTPS, usando HttpClient lib, pero el problema es que, dado que el certificado no está firmado por una autoridad certificadora (CA) reconocida como Verisign , GlobalSIgn , etc., listada en el conjunto de certificados de confianza de Android , Sigo recibiendo javax.net.ssl.SSLException: Not trusted server certificate .

He visto soluciones donde simplemente acepta todos los certificados, pero ¿qué sucede si quiero preguntarle al usuario?

Quiero obtener un diálogo similar al del navegador, permitiendo que el usuario decida continuar o no. Preferiblemente, me gustaría usar la misma tienda de certificados que el navegador. ¿Algunas ideas?


Escribí la pequeña biblioteca ssl-utils-android para confiar en certificados particulares en Android.

Simplemente puede cargar cualquier certificado dando el nombre de archivo del directorio de activos.

Uso:

OkHttpClient client = new OkHttpClient(); SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer"); client.setSslSocketFactory(sslContext.getSocketFactory());


Estaba frustrado tratando de conectar mi aplicación de Android a mi servicio RESTful utilizando https. También estaba un poco molesto con todas las respuestas que sugerían desactivar la verificación de certificados por completo. Si lo haces, ¿cuál es el punto de https?

Luego de buscar en Google el tema por un tiempo, finalmente encontré this solución donde no se necesitan jar externos, solo API de Android. Gracias a Andrew Smith, quien lo publicó en julio de 2014

/** * Set up a connection to myservice.domain using HTTPS. An entire function * is needed to do this because myservice.domain has a self-signed certificate. * * The caller of the function would do something like: * HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca"); * InputStream in = urlConnection.getInputStream(); * And read from that "in" as usual in Java * * Based on code from: * https://developer.android.com/training/articles/security-ssl.html#SelfSigned */ public static HttpsURLConnection setUpHttpsConnection(String urlString) { try { // Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) CertificateFactory cf = CertificateFactory.getInstance("X.509"); // My CRT file that I put in the assets folder // I got this file by following these steps: // * Go to https://littlesvr.ca using Firefox // * Click the padlock/More/Security/View Certificate/Details/Export // * Saved the file as littlesvr.crt (type X.509 Certificate (PEM)) // The MainActivity.context is declared as: // public static Context context; // And initialized in MainActivity.onCreate() as: // MainActivity.context = getApplicationContext(); InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt")); Certificate ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); // Tell the URLConnection to use a SocketFactory from our SSLContext URL url = new URL(urlString); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); return urlConnection; } catch (Exception ex) { Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString()); return null; } }

Funcionó bien para mi aplicación de maqueta.


Este es un problema que resulta de la falta de compatibilidad con SNI (Server Name Identification) en A, ndroid 2.x. Estuve luchando con este problema durante una semana hasta que me encontré con la siguiente pregunta, que no solo brinda una buena base del problema, sino que también proporciona una solución efectiva y efectiva sin agujeros de seguridad.

Error ''No peer certificate'' en Android 2.3 pero NO en 4


Google recomienda el uso de Android Volley para conexiones HTTP / HTTPS , ya que HttpClient está en desuso. Entonces, sabes la opción correcta :).

Y también, NUNCA NUKE certificados SSL (¡NUNCA!).

Deshabilitar los certificados SSL es totalmente contrario al propósito de SSL, que es promover la seguridad . No tiene sentido usar SSL, si planea bombardear todos los certificados SSL que lleguen. Una mejor solución sería, no usar SSL, o una mejor solución, crear un TrustManager personalizado en su App + usando Android Volley para conexiones HTTP / HTTPS.

Aquí hay un Gist que creé, con una aplicación de inicio de sesión básica, realizando conexiones HTTPS, utilizando un certificado autofirmado en el lado del servidor, aceptado en la aplicación.

Aquí también hay otro Gist que puede ayudar, para crear certificados SSL autofirmados para configurar en su servidor y también usar el certificado en su aplicación. Muy importante: debe copiar el archivo .crt que fue generado por el script de arriba, al directorio "raw" de su proyecto de Android.


La respuesta principal no funcionó para mí. Después de investigar, encontré la información requerida en "Desarrollador de Android": https://developer.android.com/training/articles/security-ssl.html#SelfSigned

Crear una implementación vacía de X509TrustManager hizo el truco:

private static class MyTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } ... HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); try { // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); TrustManager[] tmlist = {new MyTrustManager()}; context.init(null, tmlist, null); conn.setSSLSocketFactory(context.getSocketFactory()); } catch (NoSuchAlgorithmException e) { throw new IOException(e); } catch (KeyManagementException e) { throw new IOException(e); } conn.setRequestMethod("GET"); int rcode = conn.getResponseCode();

Tenga en cuenta que esta implementación vacía de TustManager es solo un ejemplo y su uso en un entorno productivo causaría una grave amenaza de seguridad.


Lo primero que debe hacer es establecer el nivel de verificación. Tales niveles no son tanto:

  • ALLOW_ALL_HOSTNAME_VERIFIER
  • BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
  • STRICT_HOSTNAME_VERIFIER

Aunque el método setHostnameVerifier () es obsoleto para la nueva biblioteca de Apache, pero para la versión en Android SDK es normal. Y entonces tomamos ALLOW_ALL_HOSTNAME_VERIFIER y lo establecemos en el método factory SSLSocketFactory.setHostnameVerifier() .

A continuación, debe configurar nuestra fábrica para el protocolo a https. Para hacer esto, simplemente llame al método SchemeRegistry.register() .

Luego debe crear un DefaultHttpClient con SingleClientConnManager . También en el código siguiente puede ver que en el valor predeterminado también usará nuestro indicador ( ALLOW_ALL_HOSTNAME_VERIFIER ) por el método HttpsURLConnection.setDefaultHostnameVerifier()

El siguiente código funciona para mí:

HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; DefaultHttpClient client = new DefaultHttpClient(); SchemeRegistry registry = new SchemeRegistry(); SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory(); socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier); registry.register(new Scheme("https", socketFactory, 443)); SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry); DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams()); // Set verifier HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); // Example send http request final String url = "https://encrypted.google.com/"; HttpPost httpPost = new HttpPost(url); HttpResponse response = httpClient.execute(httpPost);


Los siguientes pasos principales son necesarios para lograr una conexión segura entre las Autoridades de Certificación que la plataforma de Android no considera confiables.

Según lo solicitado por muchos usuarios, he reflejado las partes más importantes del artículo de mi blog aquí:

  1. Obtenga todos los certificados necesarios (raíz y cualquier CA intermedia)
  2. Crear un almacén de claves con keytool y el proveedor BouncyCastle e importar los certs
  3. Cargue el almacén de claves en su aplicación de Android y úselo para las conexiones seguras (recomiendo usar Apache HttpClient en lugar del estándar java.net.ssl.HttpsURLConnection (más fácil de entender, más eficiente)

Agarrar los certs

Debe obtener todos los certificados que forman una cadena desde el certificado de punto final hasta la CA raíz. Esto significa cualquier certificado de CA intermedia (si existe) y también el certificado de CA raíz. No necesita obtener el certificado de punto final.

Crea el almacén de claves

Descargue el proveedor de BouncyCastle y guárdelo en una ubicación conocida. Asegúrese también de que puede invocar el comando keytool (generalmente ubicado debajo de la carpeta bin de su instalación de JRE).

Ahora importe los certificados obtenidos (no importe el certificado de punto final) en un almacén de claves con formato BouncyCastle.

No lo probé, pero creo que el orden de importación de los certificados es importante. Esto significa que importe primero el certificado de CA intermedio más bajo y luego hasta el certificado de CA raíz.

Con el siguiente comando, se creará un nuevo almacén de claves (si no está presente) con la contraseña mysecret y se importará el certificado CA intermedio. También definí el proveedor BouncyCastle, donde se puede encontrar en mi sistema de archivos y en el formato del almacén de claves. Ejecute este comando para cada certificado en la cadena.

keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Verifique si los certificados se importaron correctamente en el almacén de claves:

keytool -list -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Debería dar salida a toda la cadena:

RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93 IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43

Ahora puede copiar el almacén de claves como recurso sin formato en su aplicación de Android en res/raw/

Usa el almacén de claves en tu aplicación

En primer lugar, debemos crear un Apache HttpClient personalizado que use nuestro almacén de claves para conexiones HTTPS:

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.mykeystore); try { // Initialize the keystore with the provided trusted certificates // Also provide the password of the keystore trusted.load(in, "mysecret".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); } } }

Hemos creado nuestro HttpClient personalizado, ahora solo podemos usarlo para conexiones seguras. Por ejemplo, cuando hacemos una llamada GET a un recurso REST.

// Instantiate the custom HttpClient DefaultHttpClient client = new MyHttpClient(getApplicationContext()); HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23"); // Execute the GET call and obtain the response HttpResponse getResponse = client.execute(get); HttpEntity responseEntity = getResponse.getEntity();

Eso es ;)


Ninguna de estas correcciones funcionó para mi plataforma de desarrollo dirigida a SDK 16, versión 4.1.2, así que encontré una solución alternativa.

Mi aplicación almacena datos en el servidor usando " http://www.example.com/page.php?data=somedata "

Recientemente page.php se movió a " https://www.secure-example.com/page.php " y sigo obteniendo "javax.net.ssl.SSLException: Certificado de servidor no confiable".

En lugar de aceptar todos los certificados para una sola página, comencé con esta guía y resolví mi problema escribiendo mi propia página.php publicada en " http://www.example.com/page.php "

<?php caronte ("https://www.secure-example.com/page.php"); function caronte($url) { // build curl request $ch = curl_init(); foreach ($_POST as $a => $b) { $post[htmlentities($a)]=htmlentities($b); } curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post)); // receive server response ... curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $server_output = curl_exec ($ch); curl_close ($ch); echo $server_output; } ?>


Si tiene un certificado personalizado / autofirmado en el servidor que no está en el dispositivo, puede usar la siguiente clase para cargarlo y usarlo en el lado del cliente en Android:

Coloque el archivo de certificado *.crt en /res/raw para que esté disponible desde R.raw.*

Utilice la clase inferior para obtener un HTTPClient o HttpsURLConnection que tendrá una fábrica de socket que use ese certificado:

package com.example.customssl; import android.content.Context; import org.apache.http.client.HttpClient; 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.AllowAllHostnameVerifier; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; public class CustomCAHttpsProvider { /** * Creates a {@link org.apache.http.client.HttpClient} which is configured to work with a custom authority * certificate. * * @param context Application Context * @param certRawResId R.raw.id of certificate file (*.crt). Should be stored in /res/raw. * @param allowAllHosts If true then client will not check server against host names of certificate. * @return Http Client. * @throws Exception If there is an error initializing the client. */ public static HttpClient getHttpClient(Context context, int certRawResId, boolean allowAllHosts) throws Exception { // build key store with ca certificate KeyStore keyStore = buildKeyStore(context, certRawResId); // init ssl socket factory with key store SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore); // skip hostname security check if specified if (allowAllHosts) { sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); } // basic http params for client HttpParams params = new BasicHttpParams(); // normal scheme registry with our ssl socket factory for "https" SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", sslSocketFactory, 443)); // create connection manager ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry); // create http client return new DefaultHttpClient(cm, params); } /** * Creates a {@link javax.net.ssl.HttpsURLConnection} which is configured to work with a custom authority * certificate. * * @param urlString remote url string. * @param context Application Context * @param certRawResId R.raw.id of certificate file (*.crt). Should be stored in /res/raw. * @param allowAllHosts If true then client will not check server against host names of certificate. * @return Http url connection. * @throws Exception If there is an error initializing the connection. */ public static HttpsURLConnection getHttpsUrlConnection(String urlString, Context context, int certRawResId, boolean allowAllHosts) throws Exception { // build key store with ca certificate KeyStore keyStore = buildKeyStore(context, certRawResId); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // Create a connection from url URL url = new URL(urlString); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); // skip hostname security check if specified if (allowAllHosts) { urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier()); } return urlConnection; } private static KeyStore buildKeyStore(Context context, int certRawResId) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { // init a default key store String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); // read and add certificate authority Certificate cert = readCert(context, certRawResId); keyStore.setCertificateEntry("ca", cert); return keyStore; } private static Certificate readCert(Context context, int certResourceId) throws CertificateException, IOException { // read certificate resource InputStream caInput = context.getResources().openRawResource(certResourceId); Certificate ca; try { // generate a certificate CertificateFactory cf = CertificateFactory.getInstance("X.509"); ca = cf.generateCertificate(caInput); } finally { caInput.close(); } return ca; } }

Puntos clave:

  1. Certificate objetos de Certificate se generan a partir de archivos .crt .
  2. Se KeyStore un KeyStore predeterminado.
  3. keyStore.setCertificateEntry("ca", cert) está agregando un certificado al almacén de claves bajo el alias "ca". Usted modifica el código para agregar más certificados (CA intermedia, etc.).
  4. El objetivo principal es generar una SSLSocketFactory que luego pueda ser utilizada por HTTPClient o HttpsURLConnection .
  5. SSLSocketFactory se puede configurar más, por ejemplo, para omitir la verificación del nombre de host, etc.

Más información en: http://developer.android.com/training/articles/security-ssl.html


Tal vez esto sea útil ... funciona en clientes java utilizando certificados autofirmados (no hay verificación del certificado). ¡Tenga cuidado y úselo solo para casos de desarrollo porque eso no es seguro en absoluto!

Cómo ignorar los errores del certificado SSL en Apache HttpClient 4.0

Espero que funcione en Android simplemente agregando la biblioteca HttpClient ... ¡buena suerte!


La forma más simple para crear un certificado SSL

Abre Firefox (supongo que también es posible con Chrome, pero es más fácil para mí con FF)

Visite su sitio de desarrollo con un certificado SSL autofirmado.

Haga clic en el certificado (al lado del nombre del sitio)

Haga clic en "Más información"

Haga clic en "Ver certificado"

Haga clic en "Detalles"

Haga clic en "Exportar ..."

Elija "Certificado X.509 con cadena (PEM)", seleccione la carpeta y el nombre para guardarlo y haga clic en "Guardar"

Vaya a la línea de comando, al directorio donde descargó el archivo pem y ejecute "openssl x509 -inform PEM -outform DM -in .pem -out .crt"

Copie el archivo .crt en la raíz de la carpeta / sdcard dentro de su dispositivo Android Dentro de su dispositivo Android, Configuración> Seguridad> Instalar desde el almacenamiento.

Debe detectar el certificado y dejar que lo agregue al dispositivo Navegue a su sitio de desarrollo.

La primera vez debería pedirte que confirmes la excepción de seguridad. Eso es todo.

El certificado debería funcionar con cualquier navegador instalado en tu Android (navegador, Chrome, Opera, Dolphin ...)

Recuerde que si está sirviendo sus archivos estáticos de un dominio diferente (todos somos perras de velocidad de página) también necesita agregar el certificado para ese dominio.