leanmind logo leanmind text logo

Blog

Código Sostenible

Cómo escribir código fácil de mantener mediante valores, principios y técnicas.

AndroidSecurityConfig para desarrollo local y comandos útiles

Por Iván Santos González y Mireia Scholz

Durante los últimos meses, en uno de los proyectos en los que colaboramos, hemos trabajado tanto en desarrollo nativo de aplicaciones móviles, como en desarrollo web (front y back). De forma muy resumida: la aplicación móvil envía mensajes al back mediante POST requests, el back los almacena en la base de datos y el front los muestra en un dashboard.

Por defecto, Android no confía en conexiones no seguras (HTTP), que son las que inicialmente estábamos utilizando para enviar los mensajes al back. Por esto, permitimos estas conexiones para poder seguir trabajando.

AndroidManifest.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<application
    ...
    cleartextTrafficPermitted="true"
    ... >
</application>
</manifest>

Con esto, ya podíamos seguir haciendo nuestras pruebas en local y en las máquinas del entorno producción, al menos mientras utilizaremos conexiones HTTP.


Cuando llegó la hora de añadir el certificado al back, redirigimos todo el tráfico a HTTPS, así que creíamos que ya no habría más problemas. Sin embargo, tampoco podíamos hacer peticiones a nuestro backend (ahora con HTTPS) en local:

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Por partes ¿Qué es un trust anchor? ¿Dónde debería estar?, ¿y dónde lo está buscando?

Estuvimos una semana entera intentando hacer que esto funcionara (seguro que a algún compi le suena 👀) y vamos a contarles cómo lo solucionamos.

Buscando en internet, encontramos muchos “workarounds” para solucionar este problema, como añadir “parches” al código de producción o permitir TODAS las conexiones, vinieran de dónde vinieran. Nada de esto nos convenció, así que seguimos buscando hasta encontrar este artículo en lo profundo de la documentación de Android Developers.


La solución es generar nuestra propia autoridad certificadora (CA), para a su vez generar un certificado de cliente. Tendríamos que añadir el certificado de la CA que acabamos de generar a la aplicación de Android, para que la acepte como CA de confianza; y, por otra parte, habría que añadir el keystore (que internamente incluye el de la CA también) a nuestro backend. Vamos a verlo en detalle:

Para importar esta CA en la aplicación Android, debemos indicarlo en un fichero de configuración, que a su vez debemos referenciar desde el manifiesto de la aplicación. Basta con añadir la subsección android:networkSecurityConfig="@xml/network_security_config", en el archivo AndroidManifest.xml, dentro de la sección application. En este caso, @xml/network_security_config hace referencia a un fichero de configuración que debe ser creado en el directorio xml y cuyo contenido debe ser el siguiente.

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/ca" /> <!-- ca certificate with .pem extension -->
        </trust-anchors>
    </debug-overrides>
</network-security-config>

De esta manera, no será necesario instalar el certificado de la CA en todos los dispositivos en los que queramos probar la aplicación, ya que está en la configuración del proyecto. Cabe destacar que como esta configuración se encuentra dentro del bloque debug-overrides, solo será válida cuando la aplicación se lance en modo debug, que es como Android Studio ejecuta las aplicaciones mientras estamos en fase de desarrollo.

Como se menciona anteriormente, el backend debe utilizar un keystore que incorpore tanto el certificado de la CA como el certificado cliente previamente firmado por dicha CA.

Para realizar el proceso de generación y firma de certificados, son necesarias las herramientas openssl y keytool. Los siguientes comandos pueden ser utilizados para realizar este proceso:

1
2
keytool -genkeypair -alias root -dname "cn=Local Development" -validity 10000 -keyalg RSA -keysize 2048 -ext bc:c -keystore root.jks -keypass 123456-storepass 123456
keytool -genkeypair -alias ca -dname "cn=Local Development" -validity 10000 -keyalg RSA -keysize 2048 -ext bc:c -keystore ca.jks -keypass 123456-storepass 123456
1
    keytool -exportcert -rfc -keystore root.jks -alias root -storepass 123456 > root.pem
1
2
3
    keytool -keystore ca.jks -storepass 123456 -certreq -alias ca| keytool -keystore root.jks -storepass 123456 -gencert -alias root -ext bc=0 -ext san=dns:ca -rfc > ca.pem
    keytool -keystore ca.jks -storepass 123456 -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore ca.jks -storepass 123456 -importcert -alias ca -file ca.pem
1
2
    keytool -genkeypair -alias server -dname cn=server -validity 10000 -keyalg RSA -keysize 2048 -keystore localDevkeystore.jks -keypass 123456 -storepass 123456
    keytool -keystore localDevkeystore.jks -storepass 123456 -certreq -alias server | keytool -keystore ca.jks -storepass 123456 -gencert -alias ca -ext ku:c=dig,keyEnc -ext "san=ip:10.0.2.2" -ext eku=sa,ca -rfc > server.pem
1
2
3
    keytool -keystore localDevkeystore.jks -storepass 123456 -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore localDevkeystore.jks -storepass 123456 -importcert -alias ca -file ca.pem
    keytool -keystore localDevkeystore.jks -storepass 123456 -importcert -alias server -file server.pem

Otros comandos útiles:

1
keytool -importkeystore -srckeystore clientkeystore -srcstoretype JKS -deststoretype PKCS12 -destkeystore keystore.p12
1
openssl pkcs12 -in certificate.p12 -out certificate.pem -clcerts -nokeys
1
openssl pkcs12 -in certificado.p12 -out certificado_key.pem -nocerts -nodes
1
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt
1
keytool -list -v -keystore keystore.jks
Publicado el 30/07/2021 por
Iván image

Iván Santos González

https://ivansantosgonzalez.dev/

Mireia image

Mireia Scholz

¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.

Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?

Impulsamos el crecimiento profesional de tu equipo de developers