Blog de divulgación de Ciencia y Tecnología
How to

Cómo utilizar una librería externa en Mirth Connect

Aunque Mirth Connect es una herramienta muy versátil por la cantidad de formatos y protocolos que maneja, es muy frecuente que nos encontremos una situación en la que necesitamos incluir componentes externos. Para ello veremos cómo utilizar una librería externa en Mirth Connect.

Como ejemplo de por qué podemos necesitar incluir componentes externos en Mirth puedo citar una experiencia profesional, desarrollando una integración para un Servicio de Salud. En aquella ocasión, se necesitaba encriptar las comunicaciones, y en lugar de definir un algoritmo, simplemente nos proporcionaron un fichero .jar con las clases necesarias. Caja negra.

Por tanto, añadí el jar en nuestro Mirth Connect. Posteriormente utilicé las funciones que incluía para encriptar las comunicaciones y generar los tokens en los mensajes.

Mirth Connect está desarrollado en Java, lo que proporciona bastante simplicidad para incluir librerías externas .jar. Siguiendo unos sencillos pasos veremos cómo usarla en nuestros canales con programa.

Caso práctico: Encriptar campos con información sensible

Siempre he considerado que es mas fácil ver las cosas siguiendo un ejemplo paso a paso, y eso es lo que vamos a hacer.

Suponemos 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.

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.

Despliegue del sistema
Despliegue del sistema propuesto

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:

/**
     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.

Recargar recursos en Mirth
Recargar recursos para librería externa en Mirth Connect

3. Utilizar la librería externa en Mirth Connect

Para comenzar a utilizar la librería externa en Mirth Connect hay que saber que podemos hacer referencia a ella en cualquier Transformer.

Mediante un paso de un Transformer con Javascript, por ejemplo, podremos instanciar la clase Crypt y utilizar los métodos para encriptar/desencriptar.

En este caso, en el Source haremos una lectura programada sobre la base de datos del HIS. En esta lectura obtendremos tanto el reporte como los datos del paciente que luego utilizaremos para mapear en el Transformer.

SourceUsarLibreriaExterna
Source Connector para cargar los datos del informe

En el Source Transformer de este canal, realizaremos un mapeo de todos los datos obtenidos al mensaje destino HL7 MDM, campo por campo.

En el último paso creado, de tipo Javascript, es donde voy a colocar el código que me permita instanciar mi clase Crypt y acceder a los métodos para encriptar.

Transformer Usar Librería Externa
Transformer donde usar librería externa en Mirth Connect

Marco en amarillo donde usamos la clase Crypt para encriptar.

He aprovechado código que utilicé para mi Trabajo de Fin de Grado, donde se encriptaban las comunicaciones con un sistema de tickets y claves compartidas dinámicas. La llave se calculaba en función de la fecha y otros datos compartidos,. El ticket tenía un tiempo de vida de 60 segundos desde su creación, posterior a ese tiempo se descartaba el mensaje.

Aquí dejo el código de este paso del transformer:

var version = globalMap.get('version');
var instalacion = globalMap.get('instalacion');
var did = globalMap.get('did');

// Calculamos la llave (Aqui algoritmo simple de llave dinamica conocido por ambas partes): 
//    concatenar fecha con version e instalacion
//    -> Un mensaje/ticket/cadena de ayer no esta autorizada para hoy
var llave =  DateUtil.getCurrentDate("yyyyMMdd") + version + instalacion + did;
// Vector de inicializacion constante y conocido por ambas partes
var iv = globalMap.get('iv'); 

try{

// Cifrar campos sensibles    
// A modo ilustrativo solo consideraremos nombres y apellidos

tmp['PID']['PID.5']['PID.5.2']  = new Packages.crypt.Crypt.encrypt(llave,iv,msg['PacNombre'].toString());
tmp['PID']['PID.5']['PID.5.1'] = new Packages.crypt.Crypt.encrypt(llave,iv,msg['PacApell1'].toString());
tmp['PID']['PID.6']['PID.6.1'] = new Packages.crypt.Crypt.encrypt(llave,iv,msg['PacApell2'].toString());

}catch(e){
    //Aqui haremos algo con la excepcion que ocurra
    logger.error("Error descifrando ticket: " + e);
    channelMap.put('responseStatusCode', '401');
    return -1;
}








Mensajes generados

A modo de prueba, si generamos dos mensajes, uno sin encriptación, y otro con encriptación, estos serán los resultados.

Nota: Prestar atención a los componentes del campo PID-5, donde se incluyen nombre y apellido, y PID-6, donde se incluye el apellido2.

Mensaje sin encriptación

MSH|^~\&|HIS|HOSPITAL|ANALIZADOR|SEMANTICO|20190820183002||MDM^T02^MDM_T02|E59DD457-874F-4044-8239-86AB68B3BB58|P|2.5
EVN|T10|20190820183002
PID|1||20237313||APE1INVENTADO^PEDRO|APE2NINGUNO|20150605|M|||||||||||
PV1|1|E|^^|||^^||||HOS|||||||||11112545|||||||||||||||||0 ||||||||20170614220819|20170615000100|||||||
TXA|1|Informe de Alta|TX|||20170614235600||||||GUID12398755a5a555a51|||||AU|||||
OBX|1|ED|INF_HOL^GUID12398755a5a555a51.pdf^INTR^^|1|^^TX^Base64^e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcZGVmZjBcZGVmbGFuZzMwODJ7XG...|^^^^|||||U||||^^^^|^^^|^^^^|||

Mensaje con encriptación de los datos sensibles

MSH|^~\&|HIS|HOSPITAL|ANALIZADOR|SEMANTICO|20190820190331||MDM^T02^MDM_T02|7394D297-E186-476A-B811-85822C3894F2|P|2.5
EVN|T10|20190820190331
PID|1||20237313||zDHVlGwlajTWVIXkeEuMcg==^zDHVlGwlajTWVIXkeEuMcg==|zDHVlGwlajTWVIXkeEuMcg==|20150605|M|||||||||||
PV1|1|E|^^|||^^||||HOS|||||||||11112545|||||||||||||||||0 ||||||||20170614220819|20170615000100|||||||
TXA|1|Informe de Alta|TX|||20170614235600||||||GUID12398755a5a555a51|||||AU|||||
OBX|1|ED|INF_HOL^GUID12398755a5a555a51.pdf^INTR^^|1|^^TX^Base64^e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcZGVmZjBcZGVmbGFuZzMwODJ7XG...|^^^^|||||U||||^^^^|^^^|^^^^|||

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

3 ideas sobre “Cómo utilizar una librería externa en Mirth Connect”

Follow by Email
LinkedIn
Share
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Ver
Privacidad