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
4. Compliance and Legal Requirements
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
- Multi-layered protection - providing defense against various attack vectors
- Risk mitigation - reducing potential impact of data breaches and security incidents
- Trust establishment - building confidence with customers and partners through strong security
- Competitive advantage - demonstrating superior security capabilities
2. Regulatory Compliance
- Legal adherence - meeting mandatory data protection requirements
- Audit readiness - maintaining documentation and controls for compliance audits
- Risk reduction - minimizing legal and financial penalties from non-compliance
- Global operations - enabling business operations across different regulatory jurisdictions
3. Business Value
- Customer confidence - building trust through demonstrated data protection
- Partner enablement - facilitating secure collaboration with external organizations
- Market access - meeting security requirements to access new markets and customers
- Operational continuity - maintaining business operations despite security threats
4. Technical Excellence
- System integrity - maintaining data quality and system reliability through protection mechanisms
- Performance optimization - using efficient encryption algorithms that minimize performance impact
- Scalability support - implementing encryption solutions that scale with business growth
- Interoperability - ensuring encrypted systems can communicate effectively with partners
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
- Use modern, well-vetted encryption algorithms (AES-256, ChaCha20-Poly1305) and avoid deprecated algorithms
- Implement authenticated encryption modes (GCM, CCM) that provide both confidentiality and integrity
- Use cryptographically secure random number generators for keys, IVs, and nonces
- Follow current cryptographic standards and recommendations (NIST, RFC specifications)
- Regularly review and update cryptographic implementations based on security research
2. Key Management
- Implement comprehensive key lifecycle management including generation, distribution, rotation, and revocation
- Use hardware security modules (HSMs) or key management services for critical key storage
- Implement key derivation functions (KDF) for generating encryption keys from master keys
- Separate encryption keys by purpose, environment, and data classification levels
- Implement secure key backup and recovery procedures
3. Performance Optimization
- Use streaming encryption for large files and data sets to minimize memory usage
- Implement connection pooling and cipher instance reuse where appropriate
- Consider hardware acceleration for encryption operations in high-throughput scenarios
- Cache frequently used keys while maintaining security boundaries
- Monitor encryption performance and optimize bottlenecks
4. Security Implementation
- Implement proper IV/nonce generation with sufficient entropy and uniqueness
- Use constant-time comparison functions to prevent timing attacks
- Implement secure memory management for cryptographic operations
- Validate all cryptographic inputs and handle errors securely
- Implement proper secure deletion of sensitive data and cryptographic material
5. Error Handling and Logging
- Implement comprehensive error handling that doesn't leak cryptographic information
- Log encryption operations for audit purposes without exposing sensitive data
- Implement monitoring and alerting for encryption failures and security events
- Provide clear error messages that help with troubleshooting without revealing implementation details
- Implement fallback procedures for encryption service outages
6. Compliance and Documentation
- Maintain documentation of all encryption implementations, algorithms, and key management procedures
- Ensure encryption implementations meet relevant regulatory requirements (FIPS 140-2, Common Criteria)
- Implement regular security assessments and penetration testing of encryption systems
- Maintain encryption key inventories and access logs for compliance audits
- Provide training and guidelines for developers implementing encryption features
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