Documentation for a newer release is available. View Latest

Cifrado de Mensajes

A veces los mensajes requieren seguridad más allá de la seguridad a nivel de transport, como SSL. Para estos casos los connectors pueden configurarse para cifrar o descifrar mensajes al enviar o recibir mensajes, respectivamente. Esto sitúa la seguridad a nivel de la aplicación además de cualquier mecanismo de seguridad a nivel de transport que pueda estar en uso.

Interfaz Crypto

Para agregar cifrado de mensajes, debe suministrarse una implementación de la interfaz Crypto al construir el connector. Si no se proporciona una implementación de Crypto entonces esta etapa se omite y el mensaje de transporte se recibe tal cual.

La interfaz es simple y consta de dos métodos, encrypt y decrypt.

public interface Crypto {
    /**
     * @param plaintext        message to send
     * @param cryptoProperties properties to define encryption
     * @return encrypted message
     */
    String encrypt(String plaintext, CryptoProperties cryptoProperties);

    /**
     * @param payload          encrypted message received
     * @param cryptoProperties properties to define encryption
     * @return decrypted message
     */
    String decrypt(String payload, CryptoProperties cryptoProperties);
}

Configuración de Crypto

La biblioteca del connector proporciona dos implementaciones de la interfaz Crypto. Estas son:

NoopCrypto

Esta es una clase de paso en texto plano que registrará los datos a nivel DEBUG. Generalmente se usa para pruebas.

NoopCrypto no requiere configuración.

SymmetricCrypto

Requiere una clave para cifrado y descifrado que luego es utilizada por los métodos de cifrado. Se asume que la contraseña de la clave es la misma que la contraseña del keystore.

La clase SymmetricCrypto requiere cierta configuración para funcionar. Las siguientes propiedades deben proporcionarse al construir una instancia de SymmetricCrypto.

Key Description

keystorePath

La ruta absoluta al keystore

keystoreType

Tipo de keystore como PKCS12

keystorePassword

Contraseña usada para el keystore

transformation

Transformación de cifrado a utilizar, p. ej. "AES/CBC/NoPadding" (Ver Javadocs)

Usando el builder de SymmetricCrypto podemos instanciar una instancia.

SymmetricCrypto symmetricCrypto = SymmetricCrypto.builder()
        .withKeystorePath(keystorePath)
        .withKeystoreType("PKCS12")
        .withKeystorePassword("keystore-password")
        .withTransformation("AES/CBC/PKCS5Padding")
        .build();

Uso

Tanto los send como los receive connectors tienen un campo crypto, que permite al desarrollador proporcionar una implementación de Crypto, o no si no es necesaria.

CryptoHeaders son necesarios para asegurar que se pase una clave y que el cifrado pueda activarse o desactivarse a nivel de mensaje.

Ejemplo de Send Connector

Crypto crypto = getSymmetricCrypto(); (1)
SendConnector
        .<ExampleType, ExampleType>builder(connectorName)
        .withActorSystem(actorSystem)
        .withConnectorTransport(connectorTransport)
        .withSendTransportMessageConverter(messageConverter)
        .withCorrelationIdExtractor(correlationIdExtractor)
        .withCorrelationService(correlationService)
        .withCrypto(crypto) (2)
        .build();
1 Obtiene una implementación de Crypto (symmetric en este ejemplo)
2 Proporciona la implementación de Crypto.

Ejemplo de Receive Connector

Crypto crypto = new NoopCrypto(); (1)
ReceiveConnector
        .<ExampleType>builder(connectorName)
        .withActorSystem(actorSystem)
        .withConnectorTransport(connectorTransport)
        .withReceiveTransportMessageConverter(messageConverter)
        .withReceiveHandler(receiver)
        .withMessageLogger(messageLogger)
        .withProcessingContextExtractor(processingContextExtractor)
        .withCrypto(crypto) (2)
        .build();
1 Instancia la implementación de Crypto (no-operation en este ejemplo)
2 Proporciona la implementación de Crypto.

Cabeceras de mensaje para Crypto

Al usar Crypto con connectors, los mensajes deben enviarse con las siguientes cabeceras. Si no se proporcionan, se usarán los valores predeterminados.

Key Description Default

keyAlias

El alias de la clave utilizada

empty string

encryptionScheme

El esquema de cifrado en uso. Puede establecerse en NOPS para desactivar el cifrado

AES

El enum CryptoHeader está incluido en el API.

public enum CryptoHeaders {
    KEY_ID("keyAlias"),
    SCHEME("encryptionScheme");

    private final String headerName;

    CryptoHeaders(String headerName) {
        this.headerName = headerName;
    }
}

Juntando todo esto, podemos crear un método para instanciar las cabeceras de mensaje requeridas.

private MessageHeaders messageHeaders() {
    return new MessageHeaders()
            .putHeader(CryptoHeaders.KEY_ID.getHeaderName(), "GWPAY01")
            .putHeader(CryptoHeaders.SCHEME.getHeaderName(), "AES")
            .putHeader("msgKey", "value1");
}

Luego configúrelas en el mensaje de transporte, junto con el payload, en la interfaz funcional SendTransportMessageConverter que puede declararse usando una lambda.

private SendTransportMessageConverter<ExampleType> sendTransportMessageConverter() {
    return payload -> new TransportMessage(
            messageHeaders(), (1)
            serialisePayload(payload)
    );
}

El SendTransportMessageConverter puede usarse al construir un sending connector y todos los mensajes enviados a través de él tendrán los CryptoHeaders establecidos. Este ejemplo simple demuestra cómo establecer cabeceras de manera estática; sin embargo, pueden establecerse dinámicamente a partir del payload si se requiere con un esfuerzo adicional mínimo.