El proceso de poner en marcha una API tiene varios aspectos importantes a considerar: rendimiento, disponibilidad, seguridad, etc. En este artículo vamos a ver un ítem que debiese sí o sí estar en tu checklist de seguridad previo a montar una API en producción.

Hasta hace unos años, obtener certificados SSL para tu sitio o aplicación web era altamente costoso. Hoy lo sigue siendo, pero la diferencia es que tenemos mayor cantidad de alternativas. Los proveedores están ofreciendo certificados más baratos y hasta tenemos un proveedor de certificados gratuito: Let’s Encrypt.

Let’s Encrypt es lo que se conoce como una Certificate Authority (o CA). Esto es básicamente una entidad que emite certificados de confianza en los que software como navegadores pueden confiar a la hora de ingresar a un sitio y verificar que es quien dice ser.

Hoy en día este es un pilar básico de seguridad en la red. Es impensable poner en marcha un sitio o API sin utilizar HTTPS, mucho menos en aplicaciones donde se transmita información sensible como credenciales de usuario o tarjetas de crédito. Si lo transmitimos en texto plano (HTTP) estamos poniendo en riesgo la información de nuestros usuarios, pudiendo permitir que terceros vean estos datos. Con HTTPS el cuento es distinto, toda la información viaja encriptada desde el navegador del usuario hasta tu sitio o API, y solo con el certificado podremos desencriptarla en el servidor y trabajar con estos datos de forma normal.

En este tutorial vamos a aprender como instalar Let’s Encrypt en tu servidor, generar un certificado e implementarlo en una API Java con Jetty (embedded). Si estás utilizando otra tecnología puedes ignorar este último paso y considerar solo la primera parte.

IMPORTANTE: Debes contar con un dominio que esté apuntando a tu servidor. En nuestro caso, apuntaremos un subdominio.

Instalar certbot

En mi caso estoy usando una máquina Ubuntu, pero el proceso para cualquier distribución es básicamente el mismo.

Ejecutar los siguientes comandos como root:

add-apt-repository ppa:certbot/certbot -y
apt update
apt -y install certbot software-properties-common

Certbot es una utilidad (by Let’s Encrypt) que nos permite generar y renovar de forma automática un certificado en nuestro servidor.

Si estás trabajando sobre otra plataforma puedes ver las instrucciones en el sitio de certbot.

Generar certificado

Una vez instaladas las utilidades, pedimos a Let’s Encrypt que nos de un certificado:

certbot certonly --standalone -d api.tudominio.com

Este certificado se provee con un tiempo de caducidad de 3 meses. Una vez cumplido ese periodo deberás renovarlo con el comando:

certbot renew

Además, si solo quieres validar cuanto tiempo falta para renovar sin alterar nada, puedes ejecutar la versión dry run:

certbot renew --dry-run

PROTIP: Puedes crear un cronjob de certbot renew para automatizar la renovación.

Cambiar formato de llave

Con esto ya podríamos empezar a trabajar:

cd /etc/letsencrypt/live/api.tudominio.com
openssl pkcs12 -export -out keystore.pkcs12 -in fullchain.pem -inkey privkey.pem
keytool -importkeystore -srckeystore keystore.pkcs12 -srcstoretype PKCS12 -destkeystore keystore.jks
rm keystore.pkcs12

Para el comando openssl deberás ingresar una passphrase bajo la cual quedará encriptada tu llave pkcs12. El comando keytool, que hará la transformación de pkcs12 a jks te pedirá que ingreses esa contraseña nuevamente así que asegurate de anotarla.

Lo que hicimos acá fue ir a la carpeta donde está nuestro certificado y básicamente cambiarle el formato a JKS (Java Keystore) para poder leerlo en nuestra aplicación Java.

Implementar certificado en Jetty

Si no utilizas Jetty no importa, ya habrás aprendido como generar y cambiar el formato de tu certificado que en el fondo era lo más importante de este artículo.

Sea cual sea el servidor que estés ocupando, posiblemente deberás trasladar la llave desde el directorio de certbot a uno más conveniente, en este caso lo pasamos a la raíz de nuestra aplicación:

cp /etc/letsencrypt/live/api.tudominio.com/keystore.jks /home/user/app/keystore.jks
chown user /home/user/app/keystore.jks

Además, cambiamos el propietario con chown. Asegurate de asignarle los permisos necesarios al archivo jks para que pueda ser leído por tu aplicación

En la siguiente implementación, lo que se hizo fue cargar la jks en la aplicación para poder exponer el servidor mediante HTTPS, pero si no encuentra el archivo, hará un fallback a HTTP, si bien no es lo correcto en producción para propósitos de desarrollo nos ahorrará bastante tiempo:

// Inicializamos el servidor Jetty
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");

Server jettyServer = new Server(httpPort);
jettyServer.setHandler(context);

try {
    // Inicializamos la configuración HTTPS
    HttpConfiguration https = new HttpConfiguration();
    https.addCustomizer(new SecureRequestCustomizer());

    // Cargamos la llave del certificado
    SslContextFactory sslContextFactory = new SslContextFactory();
    sslContextFactory.setKeyStorePath(App.class.getResource("/keystore.jks").toExternalForm());
    sslContextFactory.setKeyStorePassword("passphrase-de-la-llave");
    sslContextFactory.setKeyManagerPassword("passphrase-de-la-llave");

    // Creamos el conector SSL con la llave
    ServerConnector sslConnector = new ServerConnector(jettyServer, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(https));
    sslConnector.setPort(httpsPort);

    // Cargamos el conector SSL en el servidor Jetty
    jettyServer.setConnectors(new Connector[]{sslConnector});

    logger.debug("Certificado cargado, servidor corriendo en 8443");
} catch (Exception e) {
    logger.warn("Error cargando certificado, servidor corriendo en 8080", e);
}

La passphrase de la llave es la que ingresaste cuando le cambiaste el formato a la llave el punto anterior.

Los pasos principales están comentados en el código:

  1. Inicializamos el servidor Jetty
  2. Inicializamos el objeto de configuración HTTPS
  3. Cargamos la llave del certificado desde la ruta de nuestra aplicación
  4. Creamos el conector SSL con dicha llave
  5. Le inyectamos el conector SSL a nuestro servidor HTTPS

Hecho esto, ya podemos hacer request sobre https://api.tudominio.com.

Final notes

La documentación oficial de certbot la puedes encontrar por acá. Para enterarte como funciona, puedes ver acá.

Si te interesa saber más sobre Let’s Encrypt puedes revisar el about, además comentar que se trata de una organización sin fines de lucro y respaldada por una variedad de organizaciones muy conocidas como RedHat, Mozilla, Cisco, entre muchas otras.