This site is in English. Use your browser's built-in translate feature to read it in your language.

Encryption Pattern

Overview

The Encryption pattern is a fundamental security mechanism in enterprise integration architectures that transforms readable data into an encoded format that can only be decoded by authorized parties possessing the correct decryption keys. Like a sophisticated safe that protects valuable documents by making them unreadable to anyone without the proper combination, encryption ensures that sensitive information remains protected both during transmission across networks and while stored in databases, files, or other storage systems. This pattern encompasses various encryption algorithms, key management strategies, and implementation approaches that form the backbone of data protection in modern integration systems.

Theoretical Foundation

The Encryption pattern is grounded in cryptographic theory and information security principles, specifically addressing the challenges of confidentiality, integrity, and authenticity in distributed systems. The pattern incorporates concepts from symmetric cryptography, asymmetric cryptography, digital signatures, and key management systems to provide comprehensive protection for sensitive data throughout its lifecycle, from creation and processing to transmission and storage.

Core Principles

1. Cryptographic Algorithms

Encryption employs various mathematical algorithms for data protection: - Symmetric encryption - using the same key for encryption and decryption (AES, ChaCha20) - Asymmetric encryption - using key pairs with public and private keys (RSA, ECC) - Hybrid encryption - combining symmetric and asymmetric encryption for optimal performance and security - Stream vs block ciphers - different approaches for encrypting data in streams or fixed-size blocks

2. Key Management

Systematic control of cryptographic keys throughout their lifecycle: - Key generation - creating cryptographically strong keys using secure random number generation - Key distribution - securely sharing keys between authorized parties - Key rotation - regularly updating encryption keys to maintain security - Key revocation - invalidating compromised or obsolete keys

3. Encryption Scope

Comprehensive protection at different levels and contexts: - Data-at-rest encryption - protecting stored data in databases, files, and backup systems - Data-in-transit encryption - securing data during network transmission - Data-in-use encryption - protecting data while being processed (homomorphic encryption, secure enclaves) - Application-level encryption - encrypting specific data fields or messages within applications

4. Cryptographic Services

Supporting services that enhance encryption effectiveness: - Digital signatures - ensuring data authenticity and non-repudiation - Message authentication codes (MAC) - verifying data integrity and authenticity - Hash functions - creating digital fingerprints for data integrity verification - Certificate management - handling digital certificates for public key infrastructure

Why Encryption is Essential in Integration Architecture

1. Data Protection

Encryption provides critical protection for sensitive information: - Confidentiality assurance - ensuring only authorized parties can access sensitive data - Privacy protection - safeguarding personal and confidential information from unauthorized disclosure - Intellectual property protection - securing valuable business data and trade secrets - Regulatory compliance - meeting data protection requirements mandated by law

2. Communication Security

For securing data transmission between systems: - Network protection - defending against eavesdropping and man-in-the-middle attacks - API security - protecting sensitive data exchanged through API calls - Message integrity - ensuring messages haven't been tampered with during transmission - Authentication verification - confirming the identity of communication partners

3. Storage Security

Protecting data in various storage systems: - Database encryption - securing sensitive data stored in relational and NoSQL databases - File system protection - encrypting files and documents stored on disk - Backup security - ensuring backup copies remain protected if storage media is compromised - Cloud storage protection - maintaining data security when using cloud storage services

Meeting regulatory and industry standards: - GDPR compliance - protecting personal data as required by European privacy regulations - PCI DSS requirements - securing payment card data according to industry standards - HIPAA compliance - protecting healthcare information as mandated by law - SOX requirements - securing financial data to meet audit and governance standards

Benefits in Integration Contexts

1. Enhanced Security Posture

2. Regulatory Compliance

3. Business Value

4. Technical Excellence

Integration Architecture Applications

1. API and Web Service Security

Encryption in API security provides: - HTTPS/TLS encryption - securing all API communications with transport-layer security - Message-level encryption - protecting sensitive payload data regardless of transport security - API key protection - encrypting and securing API authentication credentials - Response encryption - protecting sensitive data returned in API responses

2. Database and Storage Security

For protecting stored data: - Transparent data encryption (TDE) - automatically encrypting database files and backups - Column-level encryption - protecting specific sensitive data fields within database tables - File system encryption - securing entire file systems or individual files and directories - Object storage encryption - protecting data stored in cloud object storage systems

3. Message Queue and Event Security

In messaging systems: - Message payload encryption - protecting sensitive data within message content - Queue encryption - securing message queues and persistent message storage - Event stream protection - encrypting sensitive data in event streaming platforms - Dead letter queue security - protecting failed messages that may contain sensitive data

4. Enterprise Application Integration

For connecting enterprise systems: - Legacy system protection - adding encryption layers to existing systems without encryption - Cross-system data exchange - securing data as it moves between different enterprise applications - Batch processing security - protecting large data transfers and batch operations - Backup and archival encryption - securing long-term data storage and disaster recovery systems

Implementation Patterns

1. Symmetric Encryption Implementation

// AES encryption service for high-performance symmetric encryption
@Service
public class AESEncryptionService {

    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 16;

    @Autowired
    private KeyManagementService keyManagementService;

    public EncryptionResult encrypt(byte[] plaintext, String keyId) {
        try {
            // Retrieve encryption key
            SecretKey secretKey = keyManagementService.getSecretKey(keyId);

            // Initialize cipher
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);

            // Generate random IV
            byte[] iv = new byte[GCM_IV_LENGTH];
            new SecureRandom().nextBytes(iv);

            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);

            // Encrypt data
            byte[] ciphertext = cipher.doFinal(plaintext);

            return EncryptionResult.builder()
                .ciphertext(ciphertext)
                .iv(iv)
                .keyId(keyId)
                .algorithm(TRANSFORMATION)
                .timestamp(Instant.now())
                .build();

        } catch (Exception e) {
            log.error("Encryption failed for keyId: {}", keyId, e);
            throw new EncryptionException("Failed to encrypt data", e);
        }
    }

    public byte[] decrypt(EncryptionResult encryptionResult) {
        try {
            // Retrieve decryption key
            SecretKey secretKey = keyManagementService.getSecretKey(encryptionResult.getKeyId());

            // Initialize cipher for decryption
            Cipher cipher = Cipher.getInstance(encryptionResult.getAlgorithm());
            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, encryptionResult.getIv());
            cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);

            // Decrypt data
            return cipher.doFinal(encryptionResult.getCiphertext());

        } catch (Exception e) {
            log.error("Decryption failed for keyId: {}", encryptionResult.getKeyId(), e);
            throw new DecryptionException("Failed to decrypt data", e);
        }
    }

    public String encryptString(String plaintext, String keyId) {
        EncryptionResult result = encrypt(plaintext.getBytes(StandardCharsets.UTF_8), keyId);

        // Combine IV and ciphertext for storage
        byte[] combined = new byte[GCM_IV_LENGTH + result.getCiphertext().length];
        System.arraycopy(result.getIv(), 0, combined, 0, GCM_IV_LENGTH);
        System.arraycopy(result.getCiphertext(), 0, combined, GCM_IV_LENGTH, result.getCiphertext().length);

        // Encode as Base64 for string representation
        return Base64.getEncoder().encodeToString(combined);
    }

    public String decryptString(String encryptedData, String keyId) {
        byte[] combined = Base64.getDecoder().decode(encryptedData);

        // Extract IV and ciphertext
        byte[] iv = new byte[GCM_IV_LENGTH];
        byte[] ciphertext = new byte[combined.length - GCM_IV_LENGTH];

        System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH);
        System.arraycopy(combined, GCM_IV_LENGTH, ciphertext, 0, ciphertext.length);

        // Create encryption result for decryption
        EncryptionResult result = EncryptionResult.builder()
            .ciphertext(ciphertext)
            .iv(iv)
            .keyId(keyId)
            .algorithm(TRANSFORMATION)
            .build();

        return new String(decrypt(result), StandardCharsets.UTF_8);
    }
}

@Service
public class ChaCha20EncryptionService {

    private static final String ALGORITHM = "ChaCha20-Poly1305";
    private static final int NONCE_LENGTH = 12;

    public EncryptionResult encrypt(byte[] plaintext, byte[] key, byte[] associatedData) {
        try {
            // Generate random nonce
            byte[] nonce = new byte[NONCE_LENGTH];
            new SecureRandom().nextBytes(nonce);

            // Create cipher instance
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20");
            ChaCha20ParameterSpec parameterSpec = new ChaCha20ParameterSpec(nonce, 1);

            cipher.init(Cipher.ENCRYPT_MODE, keySpec, parameterSpec);

            // Add associated data for authentication
            if (associatedData != null) {
                cipher.updateAAD(associatedData);
            }

            // Encrypt data
            byte[] ciphertext = cipher.doFinal(plaintext);

            return EncryptionResult.builder()
                .ciphertext(ciphertext)
                .iv(nonce)
                .algorithm(ALGORITHM)
                .associatedData(associatedData)
                .timestamp(Instant.now())
                .build();

        } catch (Exception e) {
            throw new EncryptionException("ChaCha20 encryption failed", e);
        }
    }
}

2. Asymmetric Encryption Implementation

// RSA encryption service for asymmetric encryption scenarios
@Service
public class RSAEncryptionService {

    private static final String ALGORITHM = "RSA";
    private static final String TRANSFORMATION = "RSA/OAEP/SHA-256";
    private static final int RSA_KEY_SIZE = 2048;

    @Autowired
    private KeyPairService keyPairService;

    public AsymmetricEncryptionResult encryptWithPublicKey(byte[] plaintext, String keyPairId) {
        try {
            // Retrieve public key
            PublicKey publicKey = keyPairService.getPublicKey(keyPairId);

            // Initialize cipher
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            // Encrypt data
            byte[] ciphertext = cipher.doFinal(plaintext);

            return AsymmetricEncryptionResult.builder()
                .ciphertext(ciphertext)
                .keyPairId(keyPairId)
                .algorithm(TRANSFORMATION)
                .encryptedWithPublicKey(true)
                .timestamp(Instant.now())
                .build();

        } catch (Exception e) {
            log.error("RSA encryption failed for keyPairId: {}", keyPairId, e);
            throw new EncryptionException("Failed to encrypt with RSA public key", e);
        }
    }

    public byte[] decryptWithPrivateKey(AsymmetricEncryptionResult encryptionResult) {
        try {
            // Retrieve private key
            PrivateKey privateKey = keyPairService.getPrivateKey(encryptionResult.getKeyPairId());

            // Initialize cipher for decryption
            Cipher cipher = Cipher.getInstance(encryptionResult.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            // Decrypt data
            return cipher.doFinal(encryptionResult.getCiphertext());

        } catch (Exception e) {
            log.error("RSA decryption failed for keyPairId: {}", encryptionResult.getKeyPairId(), e);
            throw new DecryptionException("Failed to decrypt with RSA private key", e);
        }
    }

    public HybridEncryptionResult hybridEncrypt(byte[] plaintext, String recipientPublicKeyId) {
        try {
            // Generate symmetric key for bulk encryption
            KeyGenerator keyGen = KeyGenerator.getInstance("AES");
            keyGen.init(256);
            SecretKey symmetricKey = keyGen.generateKey();

            // Encrypt data with symmetric key
            EncryptionResult dataEncryption = aesEncryptionService.encrypt(plaintext, symmetricKey);

            // Encrypt symmetric key with recipient's public key
            AsymmetricEncryptionResult keyEncryption = encryptWithPublicKey(
                symmetricKey.getEncoded(), 
                recipientPublicKeyId
            );

            return HybridEncryptionResult.builder()
                .encryptedData(dataEncryption)
                .encryptedKey(keyEncryption)
                .recipientKeyId(recipientPublicKeyId)
                .timestamp(Instant.now())
                .build();

        } catch (Exception e) {
            throw new EncryptionException("Hybrid encryption failed", e);
        }
    }

    public byte[] hybridDecrypt(HybridEncryptionResult hybridResult) {
        try {
            // Decrypt symmetric key with private key
            byte[] symmetricKeyBytes = decryptWithPrivateKey(hybridResult.getEncryptedKey());
            SecretKey symmetricKey = new SecretKeySpec(symmetricKeyBytes, "AES");

            // Decrypt data with recovered symmetric key
            return aesEncryptionService.decrypt(hybridResult.getEncryptedData(), symmetricKey);

        } catch (Exception e) {
            throw new DecryptionException("Hybrid decryption failed", e);
        }
    }
}

@Service
public class ECCEncryptionService {

    private static final String ALGORITHM = "EC";
    private static final String SIGNATURE_ALGORITHM = "SHA256withECDSA";

    public ECDHKeyExchangeResult performKeyExchange(String localKeyPairId, String remotePublicKeyId) {
        try {
            // Get local private key and remote public key
            PrivateKey localPrivateKey = keyPairService.getPrivateKey(localKeyPairId);
            PublicKey remotePublicKey = keyPairService.getPublicKey(remotePublicKeyId);

            // Perform ECDH key agreement
            KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
            keyAgreement.init(localPrivateKey);
            keyAgreement.doPhase(remotePublicKey, true);

            byte[] sharedSecret = keyAgreement.generateSecret();

            // Derive symmetric key from shared secret
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] derivedKey = md.digest(sharedSecret);

            SecretKey symmetricKey = new SecretKeySpec(derivedKey, "AES");

            return ECDHKeyExchangeResult.builder()
                .symmetricKey(symmetricKey)
                .localKeyId(localKeyPairId)
                .remoteKeyId(remotePublicKeyId)
                .sharedSecret(sharedSecret)
                .build();

        } catch (Exception e) {
            throw new CryptographicException("ECDH key exchange failed", e);
        }
    }
}

3. Database Encryption Implementation

// Database field encryption with transparent encryption/decryption
@Service
public class DatabaseFieldEncryptionService {

    @Autowired
    private FieldEncryptionService fieldEncryptionService;

    @Autowired
    private EncryptionKeyResolver keyResolver;

    public void encryptEntity(Object entity) {
        Class<?> entityClass = entity.getClass();
        Field[] fields = entityClass.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(Encrypted.class)) {
                encryptField(entity, field);
            }
        }
    }

    public void decryptEntity(Object entity) {
        Class<?> entityClass = entity.getClass();
        Field[] fields = entityClass.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(Encrypted.class)) {
                decryptField(entity, field);
            }
        }
    }

    private void encryptField(Object entity, Field field) {
        try {
            field.setAccessible(true);
            Object value = field.get(entity);

            if (value != null) {
                Encrypted encryptedAnnotation = field.getAnnotation(Encrypted.class);
                String keyId = keyResolver.resolveKeyId(entity, field, encryptedAnnotation);

                String encryptedValue = fieldEncryptionService.encryptFieldValue(
                    value.toString(), 
                    keyId, 
                    encryptedAnnotation.algorithm()
                );

                field.set(entity, encryptedValue);
            }

        } catch (Exception e) {
            throw new EncryptionException("Failed to encrypt field: " + field.getName(), e);
        }
    }

    private void decryptField(Object entity, Field field) {
        try {
            field.setAccessible(true);
            Object value = field.get(entity);

            if (value != null && value instanceof String) {
                Encrypted encryptedAnnotation = field.getAnnotation(Encrypted.class);
                String keyId = keyResolver.resolveKeyId(entity, field, encryptedAnnotation);

                String decryptedValue = fieldEncryptionService.decryptFieldValue(
                    (String) value, 
                    keyId, 
                    encryptedAnnotation.algorithm()
                );

                // Convert back to original type if needed
                Object typedValue = convertToFieldType(decryptedValue, field.getType());
                field.set(entity, typedValue);
            }

        } catch (Exception e) {
            throw new DecryptionException("Failed to decrypt field: " + field.getName(), e);
        }
    }
}

// JPA Entity Listener for transparent database encryption
@Component
public class EncryptionEntityListener {

    @Autowired
    private DatabaseFieldEncryptionService encryptionService;

    @PrePersist
    public void encryptBeforePersist(Object entity) {
        encryptionService.encryptEntity(entity);
    }

    @PreUpdate
    public void encryptBeforeUpdate(Object entity) {
        encryptionService.encryptEntity(entity);
    }

    @PostLoad
    public void decryptAfterLoad(Object entity) {
        encryptionService.decryptEntity(entity);
    }

    @PostPersist
    public void decryptAfterPersist(Object entity) {
        encryptionService.decryptEntity(entity);
    }

    @PostUpdate
    public void decryptAfterUpdate(Object entity) {
        encryptionService.decryptEntity(entity);
    }
}

// Example encrypted entity
@Entity
@EntityListeners(EncryptionEntityListener.class)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Encrypted(keyId = "customer-pii-key", algorithm = "AES/GCM/NoPadding")
    private String socialSecurityNumber;

    @Encrypted(keyId = "customer-contact-key", algorithm = "AES/GCM/NoPadding")
    private String email;

    @Encrypted(keyId = "customer-contact-key", algorithm = "AES/GCM/NoPadding")
    private String phoneNumber;

    private String address;

    @Encrypted(keyId = "customer-financial-key", algorithm = "AES/GCM/NoPadding")
    private String creditCardNumber;

    // Getters and setters...
}

// Custom annotation for field encryption
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypted {
    String keyId();
    String algorithm() default "AES/GCM/NoPadding";
    boolean required() default true;
}

4. Message-Level Encryption Implementation

// Message encryption service for protecting message payloads
@Service
public class MessageEncryptionService {

    @Autowired
    private SymmetricEncryptionService symmetricEncryptionService;

    @Autowired
    private AsymmetricEncryptionService asymmetricEncryptionService;

    @Autowired
    private DigitalSignatureService signatureService;

    public EncryptedMessage encryptMessage(Message message, EncryptionConfig config) {
        try {
            // Serialize message to JSON
            String messageJson = objectMapper.writeValueAsString(message);
            byte[] messageBytes = messageJson.getBytes(StandardCharsets.UTF_8);

            EncryptionResult encryptionResult;

            switch (config.getEncryptionType()) {
                case SYMMETRIC:
                    encryptionResult = symmetricEncryptionService.encrypt(
                        messageBytes, 
                        config.getKeyId()
                    );
                    break;

                case ASYMMETRIC:
                    HybridEncryptionResult hybridResult = asymmetricEncryptionService.hybridEncrypt(
                        messageBytes, 
                        config.getRecipientKeyId()
                    );
                    encryptionResult = hybridResult.toEncryptionResult();
                    break;

                default:
                    throw new IllegalArgumentException("Unsupported encryption type: " + config.getEncryptionType());
            }

            // Create encrypted message envelope
            EncryptedMessage encryptedMessage = EncryptedMessage.builder()
                .messageId(message.getMessageId())
                .senderId(message.getSenderId())
                .recipientId(message.getRecipientId())
                .encryptedPayload(encryptionResult)
                .encryptionConfig(config)
                .timestamp(Instant.now())
                .build();

            // Add digital signature if requested
            if (config.isSigningRequired()) {
                String signature = signatureService.sign(encryptedMessage, config.getSigningKeyId());
                encryptedMessage.setDigitalSignature(signature);
            }

            return encryptedMessage;

        } catch (Exception e) {
            throw new MessageEncryptionException("Failed to encrypt message", e);
        }
    }

    public Message decryptMessage(EncryptedMessage encryptedMessage) {
        try {
            // Verify digital signature if present
            if (encryptedMessage.getDigitalSignature() != null) {
                boolean signatureValid = signatureService.verify(
                    encryptedMessage, 
                    encryptedMessage.getDigitalSignature(),
                    encryptedMessage.getEncryptionConfig().getSigningKeyId()
                );

                if (!signatureValid) {
                    throw new MessageDecryptionException("Digital signature verification failed");
                }
            }

            // Decrypt message payload
            byte[] decryptedBytes;
            EncryptionConfig config = encryptedMessage.getEncryptionConfig();
            EncryptionResult encryptionResult = encryptedMessage.getEncryptedPayload();

            switch (config.getEncryptionType()) {
                case SYMMETRIC:
                    decryptedBytes = symmetricEncryptionService.decrypt(encryptionResult);
                    break;

                case ASYMMETRIC:
                    HybridEncryptionResult hybridResult = HybridEncryptionResult.fromEncryptionResult(encryptionResult);
                    decryptedBytes = asymmetricEncryptionService.hybridDecrypt(hybridResult);
                    break;

                default:
                    throw new IllegalArgumentException("Unsupported encryption type: " + config.getEncryptionType());
            }

            // Deserialize message
            String messageJson = new String(decryptedBytes, StandardCharsets.UTF_8);
            return objectMapper.readValue(messageJson, Message.class);

        } catch (Exception e) {
            throw new MessageDecryptionException("Failed to decrypt message", e);
        }
    }
}

@Service
public class StreamEncryptionService {

    public EncryptedInputStream encryptStream(InputStream inputStream, String keyId) {
        try {
            SecretKey secretKey = keyManagementService.getSecretKey(keyId);

            // Initialize cipher for streaming encryption
            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

            // Generate random IV
            byte[] iv = new byte[16];
            new SecureRandom().nextBytes(iv);

            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);

            return new EncryptedInputStream(inputStream, cipher, iv, keyId);

        } catch (Exception e) {
            throw new EncryptionException("Failed to create encrypted stream", e);
        }
    }

    public DecryptedInputStream decryptStream(EncryptedInputStream encryptedStream) {
        try {
            SecretKey secretKey = keyManagementService.getSecretKey(encryptedStream.getKeyId());

            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptedStream.getIv());
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);

            return new DecryptedInputStream(encryptedStream, cipher);

        } catch (Exception e) {
            throw new DecryptionException("Failed to create decrypted stream", e);
        }
    }
}

// Custom InputStream for streaming encryption
public class EncryptedInputStream extends InputStream {
    private final InputStream sourceStream;
    private final Cipher cipher;
    private final byte[] iv;
    private final String keyId;
    private final CipherInputStream cipherStream;

    public EncryptedInputStream(InputStream sourceStream, Cipher cipher, byte[] iv, String keyId) {
        this.sourceStream = sourceStream;
        this.cipher = cipher;
        this.iv = iv;
        this.keyId = keyId;
        this.cipherStream = new CipherInputStream(sourceStream, cipher);
    }

    @Override
    public int read() throws IOException {
        return cipherStream.read();
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return cipherStream.read(b, off, len);
    }

    // Getters for encryption metadata
    public byte[] getIv() { return iv.clone(); }
    public String getKeyId() { return keyId; }
}

Apache Camel Implementation

1. Message Encryption Route

@Component
public class MessageEncryptionRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:encryptMessage")
            .routeId("message-encryption-route")
            .log("Encrypting message: ${header.messageId}")
            .process(exchange -> {
                Object messageBody = exchange.getIn().getBody();
                String recipientId = exchange.getIn().getHeader("recipientId", String.class);

                // Determine encryption configuration
                EncryptionConfig config = determineEncryptionConfig(recipientId);

                // Encrypt message
                EncryptedMessage encryptedMessage = messageEncryptionService.encryptMessage(
                    Message.builder()
                        .messageId(UUID.randomUUID().toString())
                        .payload(messageBody)
                        .senderId(getCurrentUserId())
                        .recipientId(recipientId)
                        .build(),
                    config
                );

                exchange.getIn().setBody(encryptedMessage);
                exchange.getIn().setHeader("encrypted", true);
                exchange.getIn().setHeader("encryptionAlgorithm", config.getAlgorithm());
            })
            .log("Message encrypted successfully")
            .to("direct:sendEncryptedMessage");
    }

    private EncryptionConfig determineEncryptionConfig(String recipientId) {
        // Determine appropriate encryption based on recipient
        if (isInternalRecipient(recipientId)) {
            return EncryptionConfig.builder()
                .encryptionType(EncryptionType.SYMMETRIC)
                .keyId("internal-message-key")
                .algorithm("AES/GCM/NoPadding")
                .signingRequired(false)
                .build();
        } else {
            return EncryptionConfig.builder()
                .encryptionType(EncryptionType.ASYMMETRIC)
                .recipientKeyId(getRecipientPublicKeyId(recipientId))
                .algorithm("RSA/OAEP/SHA-256")
                .signingRequired(true)
                .signingKeyId("our-signing-key")
                .build();
        }
    }
}

2. Database Encryption Route

@Component
public class DatabaseEncryptionRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("direct:storeEncryptedData")
            .routeId("database-encryption-route")
            .log("Storing encrypted data for entity: ${header.entityType}")
            .process(exchange -> {
                Object entity = exchange.getIn().getBody();

                // Encrypt sensitive fields before database storage
                databaseFieldEncryptionService.encryptEntity(entity);

                exchange.getIn().setBody(entity);
                exchange.getIn().setHeader("encrypted-for-storage", true);
            })
            .to("jpa://" + "entityClassName")
            .log("Data stored with encryption");

        from("direct:retrieveEncryptedData")
            .routeId("database-decryption-route")
            .log("Retrieving encrypted data for entity: ${header.entityId}")
            .to("jpa://findEntityById")
            .process(exchange -> {
                Object entity = exchange.getIn().getBody();

                // Decrypt sensitive fields after database retrieval
                if (entity != null) {
                    databaseFieldEncryptionService.decryptEntity(entity);
                }

                exchange.getIn().setBody(entity);
                exchange.getIn().setHeader("decrypted-from-storage", true);
            })
            .log("Data retrieved and decrypted");
    }
}

3. File Encryption Route

@Component
public class FileEncryptionRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("file:input/unencrypted?delete=true")
            .routeId("file-encryption-route")
            .log("Encrypting file: ${header.CamelFileName}")
            .process(exchange -> {
                InputStream inputStream = exchange.getIn().getBody(InputStream.class);
                String filename = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);

                // Determine encryption key based on file type or content
                String keyId = determineFileEncryptionKey(filename);

                // Encrypt file stream
                EncryptedInputStream encryptedStream = streamEncryptionService.encryptStream(inputStream, keyId);

                exchange.getIn().setBody(encryptedStream);
                exchange.getIn().setHeader("encryptionKeyId", keyId);
                exchange.getIn().setHeader(Exchange.FILE_NAME, filename + ".encrypted");
            })
            .to("file:output/encrypted")
            .log("File encrypted and saved: ${header.CamelFileName}");

        from("file:input/encrypted?delete=true")
            .routeId("file-decryption-route")
            .log("Decrypting file: ${header.CamelFileName}")
            .process(exchange -> {
                String filename = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);

                if (!filename.endsWith(".encrypted")) {
                    throw new IllegalArgumentException("File does not appear to be encrypted");
                }

                // Extract original filename
                String originalFilename = filename.substring(0, filename.lastIndexOf(".encrypted"));

                // Read encryption metadata (in real implementation, this would be stored with the file)
                String keyId = extractKeyIdFromFile(exchange.getIn().getBody());

                // Decrypt file
                EncryptedInputStream encryptedStream = createEncryptedInputStream(
                    exchange.getIn().getBody(InputStream.class), 
                    keyId
                );
                DecryptedInputStream decryptedStream = streamEncryptionService.decryptStream(encryptedStream);

                exchange.getIn().setBody(decryptedStream);
                exchange.getIn().setHeader(Exchange.FILE_NAME, originalFilename);
            })
            .to("file:output/decrypted")
            .log("File decrypted and saved: ${header.CamelFileName}");
    }
}

4. API Response Encryption Route

@Component
public class APIResponseEncryptionRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("jetty:http://0.0.0.0:8080/api/secure/data")
            .routeId("api-response-encryption-route")
            .log("Processing secure API request")
            .process(exchange -> {
                // Check if client supports encryption
                String acceptEncryption = exchange.getIn().getHeader("Accept-Encryption", String.class);

                if (acceptEncryption == null || !acceptEncryption.contains("AES-GCM")) {
                    throw new UnsupportedOperationException("Client must support encryption");
                }

                // Get client certificate or API key for encryption key derivation
                String clientId = exchange.getIn().getHeader("X-Client-ID", String.class);
                if (clientId == null) {
                    throw new UnauthorizedException("Client ID required for encrypted response");
                }

                exchange.setProperty("clientId", clientId);
            })
            .to("direct:processSecureRequest")
            .process(exchange -> {
                Object responseData = exchange.getIn().getBody();
                String clientId = exchange.getProperty("clientId", String.class);

                // Determine client-specific encryption key
                String encryptionKeyId = "client-" + clientId + "-response-key";

                // Encrypt response data
                String encryptedResponse = aesEncryptionService.encryptString(
                    objectMapper.writeValueAsString(responseData),
                    encryptionKeyId
                );

                // Create encrypted response wrapper
                EncryptedResponse response = EncryptedResponse.builder()
                    .encryptedData(encryptedResponse)
                    .algorithm("AES-256-GCM")
                    .keyId(encryptionKeyId)
                    .timestamp(Instant.now())
                    .build();

                exchange.getIn().setBody(response);
                exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
                exchange.getIn().setHeader("Content-Encryption", "AES-256-GCM");
            })
            .marshal().json(JsonLibrary.Jackson)
            .log("Encrypted API response sent to client");
    }
}

Best Practices

1. Algorithm Selection and Implementation

2. Key Management

3. Performance Optimization

4. Security Implementation

5. Error Handling and Logging

6. Compliance and Documentation

The Encryption pattern is essential for protecting sensitive data in enterprise integration architectures, providing the cryptographic foundation necessary to maintain confidentiality, integrity, and authenticity throughout the data lifecycle in complex distributed systems.

← Back to All Patterns