110 votos

Aceptando un certificado para HTTPs en Android

Estoy tratando de hacer las conexiones Https en los móviles Android, utilizando HttpClient. Problema es que dado que el certificado no está firmado sigo recibiendo "javax.neto.ssl.SSLException: No se confía en el certificado de servidor".

Ahora he visto un montón de soluciones en la que sólo deberá aceptar todos los certificados, pero lo que si me quiere preguntar al usuario? Quiero obtener un cuadro de diálogo similar a la del navegador, de forma que el usuario puede decidir continuar o no.

Preferiblemente me gustaría utilizar el mismo certificatestore como navegador. Alguna idea?

134voto

Nikolay Moskvin Puntos 772

La primera cosa que usted necesita hacer es establecer el nivel de verificación. A estos niveles no es 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 de Android SDK es normal. Y así tomamos ALLOW_ALL_HOSTNAME_VERIFIER y la puso en el método de fábrica SSLSocketFactory.setHostnameVerifier().

A continuación, necesita establecer nuestra fábrica para que el protocolo https. Para ello, simplemente llame al SchemeRegistry.register() método.

Entonces usted necesita para crear un DefaultHttpClient con SingleClientConnManager. También en el código siguiente se puede ver que en defecto también hará uso de nuestra bandera (ALLOW_ALL_HOSTNAME_VERIFIER) por el método HttpsURLConnection.setDefaultHostnameVerifier()

A continuación el código me funciona:

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

110voto

saxos Puntos 1494

Los siguientes pasos son necesarios para lograr una conexión segura de las Autoridades de Certificación que no sean considerados como de confianza para la plataforma android.

Como solicitado por muchos usuarios, he reflejado las partes más importantes de mi artículo en el blog aquí:

  1. Agarra todos los certificados necesarios (de la raíz y el intermedio, de la CA)
  2. Crear un almacén de claves con keytool y la BouncyCastle proveedor de importación y los certificados
  3. Cargar el almacén de claves en su aplicación para android y su uso para conexiones seguras (recomiendo utilizar el Apache HttpClient en lugar de la estándar java.net.ssl.HttpsURLConnection (más fácil de entender, más eficiente)

Agarra los certs

Usted tiene que obtener todos los certificados que construir una cadena, desde el extremo de certificado de todo el camino hasta la CA Raíz. Esto significa, cualquier (si está presente) de la CA Intermedia certs y también la Raíz CA cert. Usted no necesita obtener el extremo en el certificado.

Crear el almacén de claves

Descargar el BouncyCastle Proveedor y guárdelo en una ubicación conocida. También asegúrese de que usted puede invocar el comando keytool (normalmente se encuentra bajo la carpeta bin de la instalación de JRE).

Ahora importa el obtenido certs (no importar el extremo de cert) en un BouncyCastle formato de almacén de claves.

No la he probado, pero creo que el fin de la importación de los certificados es importante. Esto significa, de importación de la parte inferior de certificado CA Intermedia primero y luego todo el camino hasta el certificado de entidad emisora Raíz.

Con el comando siguiente en el almacén de claves nuevo (si no está ya presente) con la contraseña mysecret será creada y el certificado de la CA Intermedia que va a ser importado. Yo también define la BouncyCastle proveedor, donde se puede encontrar en mi sistema de archivo y el formato de 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

Verificar si los certificados se han importado 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

Debe salida de la totalidad de 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 usted puede copiar el almacén de claves como materia prima de los recursos en su aplicación para android de bajo res/raw/

Utilizar el almacén de claves en la aplicación

Primero de todo tenemos que crear un custom Apache HttpClient que utiliza nuestro almacén de claves para las 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 propio HttpClient, ahora sólo se puede utilizar para conexiones seguras. Por ejemplo, cuando hacemos una llamada llega a un RESTO de recursos.

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

4voto

emmby Puntos 35359

He aquí cómo usted puede añadir certificados adicionales a su almacén de claves para evitar este problema: Confiar en todos los certificados que utilizan HttpClient través de HTTPS

No le pedirá al usuario como usted lo pide, pero hará que sea menos probable que el usuario se encontrará con un error de "certificado de servidor no seguro".

3voto

Markus Lenger Puntos 119

La respuesta no trabajo para mí. Después de investigar un poco he encontrado la información requerida en el "Android Developer": https://developer.android.com/training/articles/security-ssl.html#SelfSigned

Creando un vacío de implementación 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();

Por favor, ser conscientes de que este vacío de implementación de TustManager es sólo un ejemplo y su uso en un entorno productivo podría causar una grave amenaza para la seguridad!

-1voto

EmP Puntos 33

Tal vez esto le ayuda... funciona en java clientes con certificados autofirmados (no hay ninguna comprobación de que el certificado). Tenga cuidado y se utiliza sólo para el desarrollo de los casos debido a que no es seguro en absoluto!!

Apache HttpClient 4.0 Ignorar Errores de Certificado SSL

Esperamos que te funciona en Android sólo tienes que añadir HttpClient biblioteca... buena suerte!!

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: