Saltar al contenido

aceptar conexiones HTTPS con certificados autofirmados

Te damos la bienvenida a nuestra web, ahora hallarás la respuesta a lo que estás buscando.

Solución:

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 apache, pero para la versión en Android SDK es normal. Y entonces tomamos ALLOW_ALL_HOSTNAME_VERIFIER y configurarlo 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 DefaultHttpClient con SingleClientConnManager. También en el código a continuación puede ver que por defecto también usará nuestra bandera (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);

Se requieren los siguientes pasos principales para lograr una conexión segura de las Autoridades de Certificación que no son consideradas confiables por la plataforma Android.

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

  1. Obtenga todos los certificados requeridos (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 utilícelo 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)

Agarra los certificados

Debe obtener todos los certificados que construyen una cadena desde el certificado de 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.

Crea el almacén de claves

Descargue el proveedor de BouncyCastle y guárdelo en una ubicación conocida. También asegúrese de que puede invocar el comando keytool (generalmente ubicado en 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 primero importe el certificado de CA intermedia más bajo y luego hasta el certificado de CA raíz.

Con el siguiente comando, un nuevo almacén de claves (si aún no está presente) con la contraseña mi secreto se creará 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 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 un recurso sin procesar en su aplicación de Android en res/raw/

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

En primer lugar, tenemos que crear un Apache HttpClient personalizado que use 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 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 todo 😉

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 certificado *.crt presentar en /res/raw para que esté disponible en R.raw.*

Utilice la siguiente clase para obtener un HTTPClient o HttpsURLConnection que tendrá una fábrica de sockets usando 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 los objetos se generan a partir de .crt archivos.
  2. Un defecto KeyStore es creado.
  3. keyStore.setCertificateEntry("ca", cert) está agregando certificado al almacén de claves bajo el alias “ca”. Modifica el código para agregar más certificados (CA intermedia, etc.).
  4. El objetivo principal es generar una SSLSocketFactory que luego puede ser utilizado 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

valoraciones y reseñas

Recuerda que tienes la capacidad de explicar si te fue de ayuda.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *