HTTPS y SSL en Android

| 2016-11-16 | No hay comentarios »

Para poder utilizar un servidor a través de HTPPS, el certificado digital SSL del servidor debe ser reconocido como un certificado “de confianza” por Android. Podemos comprobar los certificados reconocidos en una instalación de Android en las opciones de seguridad del dispositivo.

Si intentamos conectarnos a un servidor mediante https y Android no reconoce el certificado como confiable obtendremos la siguiente excepción:

 javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Para solucionar este problema y realizar la conexión tenemos tres alternativas:

  • Instalar “manualmente” el certificado en Android. Obligar al usuario a realizar esta instalación no parece una buena idea.
  • Aceptar cualquier certificado. Es la solución más simple pero supone un problema de seguridad ya que no podemos asegurar la identidad del servidor.
  • Incluir el certificado en la app y utilizarlo para validar el certificado del servidor.

En este artículo vamos a aplicar la tercera solución tomando como ejemplo el código de la documentación oficial. Los pasos son:

  1. Obtener el certificado del servidor en formato X.509. Eso podemos hacerlo con firefox:
  • Paso 1: Darle clic al candado verde y luego en la fecha “>” para luego hacer clic en la opción “Más información”.

certificado ssl

  • Paso 2: Darle clic a la opción Ver certificado:

certificado ssl 2

  • Paso 3: Darle clic a exportar certificado y luego guardar en el formato “X.509 (PEM)”:

certificado ssl 3

   2.Incluir el certificado en la app. Simplemente copiamos el fichero obtenido en el paso anterior en directorio assets.

   3.Crear un SSLSocketFactory que nos permita confiar en el certificado (podemos incluir todos los certificados que sean necesarios). La siguiente clase genérica está lista para ser usada fácilmente es “certificado.crt” por el nombre real del certificado que van a utilizar:

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import android.content.Context;
import android.util.Log;


public class CustomSSLSocketFactory {

private CustomSSLSocketFactory() {
 super();
 }

private static SSLSocketFactory sslSocketFactory;

public static SSLSocketFactory getSSLSocketFactory(Context context) throws CertificateException, IOException, GeneralSecurityException {
 // Load CAs from an InputStream
 // (could be from a resource or ByteArrayInputStream or ...)
 CertificateFactory cf = CertificateFactory.getInstance("X.509");
 InputStream caInput = new BufferedInputStream(context.getAssets().open("certificado.crt"));
 Certificate ca;
 try {
 ca = cf.generateCertificate(caInput);
 Log.d("SSL","ca=" + ((X509Certificate) ca).getSubjectDN());
 } finally {
 caInput.close();
 }

// 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 sslContext = SSLContext.getInstance("TLS");
 sslContext.init(null, tmf.getTrustManagers(), null);

sslSocketFactory = sslContext.getSocketFactory();
 return sslSocketFactory;
 }
}

4. Aplicar el SSLContext generador por CustomSSLSocketFactory a las conexiones que lo requieran. Estas conexiones sólo aceptarán como sitios de confianza aquellos cuyos certificados hayan sido incluídos en el SSLSocketFactory:

URL url = new URL("https://www.dominio.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setSSLSocketFactory(CustomSSLSocketFactory.getSSLSocketFactory(context));

BONUS: Utilizar certificados SSL de GoDaddy en Android

Hace unos días arreglé el problema SSL de una app Android que pretendía utilizar un API con certificado de GoDaddy G2, como no encontré ningún tutorial por la web, tuve que inventar la solución, que es descargar un archivo .crt intermedio: https://certs.godaddy.com/repository , en mi caso utilicé “gdig2.crt” y funcionó sin problemas siguiendo también los pasos 2, 3 y 4 (ignorando el paso 1) que están más arriba en éste artículo.
Si tienen algunas dudas con gusto les responderé y sobre todo espero que éste artículo les sea de mucha utilidad 🙂

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Crear múltiples productFlavors con Android Studio
  • Gson: Librería para parsear JSON en Android
  • Butter Knife: librería para inyectar vistas en Android
  • certificados-ssl-libres-gratis-lets-encrytp-550x340 Utilizar certificado SSL grautito de Let’s Encrypt en Apache utilizando Ubuntu