aceptar conexiones HTTPS con certificados autofirmados

Resuelto Morten asked hace 54 años • 13 respuestas

Estoy intentando realizar conexiones HTTPS usando HttpClientlib, pero el problema es que, dado que el certificado no está firmado por una autoridad de certificación (CA) reconocida como Verisign , GlobalSIgn , etc., que figura en el conjunto de certificados de confianza de Android, Sigo recibiendo javax.net.ssl.SSLException: Not trusted server certificate.

He visto soluciones en las que simplemente se aceptan todos los certificados, pero ¿qué pasa si quiero preguntarle al usuario?

Quiero obtener un cuadro de diálogo similar al del navegador, permitiendo al usuario decidir si continúa o no. Preferiblemente me gustaría utilizar el mismo almacén de certificados que el navegador. ¿Algunas ideas?

Morten avatar Jan 01 '70 08:01 Morten
Aceptado

Lo primero que debe hacer es establecer el nivel de verificación. No hay tantos niveles de este tipo:

  • ALLOW_ALL_HOSTNAME_VERIFIER
  • BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
  • STRICT_HOSTNAME_VERIFIER

Aunque el método setHostnameVerifier() está obsoleto para la nueva biblioteca Apache, para la versión en el SDK de Android es normal. Y entonces lo tomamos ALLOW_ALL_HOSTNAME_VERIFIERy lo configuramos en la fábrica de métodos SSLSocketFactory.setHostnameVerifier().

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

Entonces necesitas crear un DefaultHttpClientwith SingleClientConnManager. También en el código a continuación puede ver que, de forma predeterminada, también usará nuestra bandera ( ALLOW_ALL_HOSTNAME_VERIFIER) mediante el métodoHttpsURLConnection.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);
Nikolay Moskvin avatar Oct 11 '2010 08:10 Nikolay Moskvin

Se requieren los siguientes pasos principales para lograr una conexión segura de las autoridades de certificación que la plataforma Android no considera confiables.

Como lo solicitaron muchos usuarios, he reflejado aquí las partes más importantes del artículo de mi blog :

  1. Obtenga todos los certificados necesarios (raíz y cualquier CA intermedia)
  2. Cree un almacén de claves con keytool y el proveedor BouncyCastle e importe los certificados
  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 eficaz)

Consigue los certificados

Debe obtener todos los certificados que crean una cadena desde el certificado del punto final hasta la CA raíz. Esto significa cualquier certificado de CA intermedia (si está presente) y también el certificado de CA raíz. No es necesario obtener el certificado de punto final.

Crear el almacén de claves

Descargue el proveedor BouncyCastle y guárdelo en una ubicación conocida. También asegúrese de poder invocar el comando keytool (normalmente ubicado en la carpeta bin de su instalación de JRE).

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

No lo probé, pero creo que el orden de importación de los certificados es importante. Esto significa que primero importe 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 aún no está presente) con la contraseña mysecret y se importará el certificado de CA intermedia. También definí el proveedor BouncyCastle, donde se puede encontrar en mi sistema de archivos y el formato del almacén de claves. Ejecute este comando para cada certificado de 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 al 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 generar 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 puedes copiar el almacén de claves como un recurso sin procesar en tu aplicación de Android enres/raw/

Utilice el almacén de claves en su aplicación

En primer lugar tenemos que crear un Apache HttpClient personalizado que utilice nuestro almacén de claves para conexiones HTTPS:

import org.apache.http.*

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 podemos usarlo para conexiones seguras. Por ejemplo cuando realizamos 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 todo ;)

saxos avatar Oct 22 '2010 15:10 saxos