Encriptar campos con información sensible en Mirth Connect

En un escenario hipotético en el que, en una integración de pacientes con un sistema, nos han pedido que encriptemos algunos campos con información sensible. De esta forma, según la password, encontramos dos tipos de encriptación de archivos: simétrico o asimétricos.

El sistema de cifrado simétrico es aquel que utiliza una misma clave para cifrar y descifrar, mientras que, en la encriptación de datos asimétrica se usan diferentes claves: una clave pública para cifrar y una de carácter privado para descifrar, de forma que sea imposible deducir la contraseña privada a través de la pública.

En el Hospital X se está instalando un nuevo analizador semántico para la codificación automática en CIE-10. El sistema recibirá los informes de alta de los pacientes, narrativa sin codificar, y tras analizarlos generará una codificación automática. Para ello se decide enviar estos informes como mensajes HL7 v2.5 de tipo MDM, donde el informe irá codificado en Base64 y encriptado en un segmento OBX.

En el segmento PID irán los datos del paciente.

El sistema analizador semántico está en una red externa del Hospital. Por cumplir un mínimo de seguridad se requiere que los datos del paciente sensibles a la LOPD estén encriptados.


Para ello, se utilizará el algoritmo AES con 128 bits para cada cadena, con una clave compartida y conocida, que permita desencriptar en el destino.

1. Crear nuestro jar externo

Vamos a empezar creándonos nuestro jar, con una clase Crypt que permita a través de dos métodos encriptar y desencriptar una cadena de texto. Para los propósitos ilustrativos lo haremos de la forma mas sencilla y directa.

AES (Advanced Encryption Standard) es un algoritmo de cifrado por bloques, cifrado de clave simétrica que opera sobre bloques de bits de tamaño fijo, con una transformación invariante. No vamos a profundizar mas en este algoritmo, porque no es el objeto del artículo. Solo decir que, para que funcione necesitamos pasarle un vector de inicialización, y una semilla.

Cifrado por bloques. Fuente: Wikipedia.
Cifrado por bloques. Fuente: Wikipedia.

Para el desarrollo de esta clase Crypt necesitaremos descargarnos las librerías la librería Apache Commons Codec.

Utilizaremos la class Cipher para encriptar/desencriptar en Java, con el AES/CBC/PKCS5Padding.

Nuestra clase será muy simple, expondrá dos métodos que encapsulan el cifrado:

encrypt

Utilizaremos este método para encriptar un texto. Recibirá tres parámetros:

  • String llave: La semilla o llave.
  • String iv: El vector de inicialización.
  • String texto: El texto que queremos encriptar.

Y devolverá un valor String con el texto encriptado. Este método puede devolver las siguientes excepciones: NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException.

decrypt

Este otro método servirá para desencriptar un texto previamente encriptado con AES 128. Aunque en este ejemplo no lo vamos a usar, porque la integración será en un sentido, ya lo tenemos para posteriores usos. No cuesta nada.

Igualmente recibirá tres parámetros:

  • String llave: La semilla o llave.
  • String iv: El vector de inicialización.
  • String encrypted: El texto que queremos desencriptar.

Y devolverá un valor String con el texto desencriptado. Este método también puede devolver las siguientes excepciones: NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException.

Ya lo tenemos, y el código queda así de simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
Clase de ejemplo ilustrativo para otros propósitos con:
Función para encriptación de un String mediante algoritmo AES por bloques
Autor: José Ramón Pascual
**/
package crypt;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static org.apache.commons.codec.binary.Base64.decodeBase64;
import static org.apache.commons.codec.binary.Base64.encodeBase64;
public class Crypt {
// Algoritmo (AES, DES, RSA)
private final static String algoritmo = "AES";
// Tipo de cifrado, por bloques, padding etc.
private final static String tipoCifrado = "AES/CBC/PKCS5Padding";
/**
Función para encriptación de un String mediante algoritmo AES por bloques con los siguientes parámetros:
@param llave tipo String a utilizar
@param iv el vector de inicialización
@param texto el texto a encriptar
@return el texto cifrado en modo String codificado en base64
@throws Exception excepciones que puede devolver: NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException
*/
public static String encrypt(String llave, String iv, String texto) throws Exception {
Cipher cipher = Cipher.getInstance(tipoCifrado);
SecretKeySpec secretKeySpec = new SecretKeySpec(llave.getBytes(), algoritmo);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(texto.getBytes());
return new String(encodeBase64(encrypted));
}
/**
Función para desencriptar un String mediante algoritmo AES por bloques con los siguientes parámetros:
@param llave tipo String a utilizar
@param iv el vector de inicialización
@param encrypted el texto a desencriptar previamente encriptado con la misma llave y codificado en base64
@return el texto descrifrado en modo String codificado en base64
@throws Exception excepciones que puede devolver: NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException
*/
public static String decrypt(String llave, String iv, String encrypted) throws Exception {
Cipher cipher = Cipher.getInstance(tipoCifrado);
SecretKeySpec secretKeySpec = new SecretKeySpec(llave.getBytes(), algoritmo);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
byte[] enc = decodeBase64(encrypted);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipher.doFinal(enc);
return new String(decrypted);
}
}

Para mas información, consultar la documentación de javax.cryptor.Cipher

Empaquetamos la clase en el paquete crypt y exportamos a un jar que podamos utilizar en Mirth Connect.

2. Cargar el recurso externo

Una vez tenemos nuestro jar empaquetado, únicamente necesitamos cargar el nuevo recurso externo en Mirth. Para ello, copiamos el jar en la ruta custom-lib de donde hayamos desplegado Mirth Connect, por ejemplo:

C:\Program Files\Mirth Connect\custom-lib
CopiarCryptoCustomLib
Copiar el jar en la ruta de librerias personalizadas en Mirth

También tenemos la opción de crear nuevas subcarpetas en dicha ruta para organizar nuestros recursos externos, o especificar otra, pero por simplicidad suelo utilizar la misma.

Nos vamos al menú Settings, y a la pestaña Resources. Aquí veremos los recursos externos cargados en Mirth Connect. Pulsaremos el botón Reload Resource en el submenú Resources Tasks de la izquierda. Esto volverá a cargar los recursos externos que se encuentren en las rutas especificadas, y aparecerá nuestra librería crypt.


Referencias

https://www.disrupciontecnologica.com/como-utilizar-una-libreria-externa-en-mirth-connect/

https://forums.mirthproject.io/forum/mirth-connect/support/16140-import-java-libraries

https://hl7latam.blogspot.com/2021/06/como-crear-codigo-java-en-mirth-connect.html

https://www.mirthcorp.com/community/wiki/display/mirth/How+to+create+and+invoke+custom+Java+code+in+Mirth+Connect

Comentarios