-
Notifications
You must be signed in to change notification settings - Fork 16
MessageInterception
Messages can be intercepted after serialization, but before the transport stores them. This can be used to compress or encrypt messages as needed. The queue will keep track of which message interceptors a message used, and re-create them as best it can. In order for that to work
- The queue must be able to create the interceptor class
- Any configuration classes must created and injected into the queue container
There are three built in message interceptors
- Gzip (GZipMessageInterceptor)
This interceptor will use the build in gzip compression class to compress messages after serialization and decompress on dequeue.
- AES-256-GCM encryption (AesMessageInterceptor) — recommended
This interceptor encrypts the message after serialization and decrypts on dequeue using AES-256-GCM authenticated encryption (confidentiality and tamper detection). It requires a 32-byte key, injected via AesMessageInterceptorConfiguration. A fresh random nonce is generated per message and stored with the ciphertext — there is no IV to manage. Consumers must use the same key. The queue never saves your key; you must provide it on both the producer and consumer.
- 3DES encryption (TripleDesMessageInterceptor) — deprecated
Deprecated:
TripleDesMessageInterceptoris obsolete (Triple-DES is retired by NIST SP 800-131A and is vulnerable to Sweet32) and will be removed in a future major version. UseAesMessageInterceptorfor new work. 3DES is retained only so existing 3DES-encrypted messages remain decryptable — see Migrating from 3DES to AES.
This interceptor encrypts the message after serialization and decrypts on dequeue. It requires a key and initialization vector via TripleDesMessageInterceptorConfiguration; consumers must use the same key and vector.
Message interceptors are ran in the order you provide. So, if you are using both compression and encryption, make sure you specify the compression module first.
var queueContainer = new QueueContainer<TTransportInit>(serviceRegister => serviceRegister.RegisterCollection<IMessageInterceptor>(new[]
{
typeof (GZipMessageInterceptor), //gzip compression
typeof (AesMessageInterceptor) //encryption (AES-256-GCM)
}).Register(() => new AesMessageInterceptorConfiguration(
RandomNumberGenerator.GetBytes(32) /* load your stable 32-byte key from a secret store */),
LifeStyles.Singleton));Security warning:
RandomNumberGenerator.GetBytes(32)above is a placeholder — it generates a new key every run. Real deployments must load a stable 32-byte key from a secrets manager or environment variable (the producer and every consumer need the same key), never hardcoded in source. AES-256-GCM needs no IV — a fresh per-message nonce is generated automatically.
In the above example, we are
- registering the gzip compression first
- registering the encryption second
- registering the encryption configuration so that we have a key to use (AES-256-GCM needs only a key; the older 3DES interceptor also needs an initialization vector)
var queueContainer = new QueueContainer<TTransportInit>(
serviceRegister => serviceRegister.Register(() => new AesMessageInterceptorConfiguration(
yourStable32ByteKey), LifeStyles.Singleton));The consumers only need the configuration. They will re-create the interceptors used by the producers as needed. You may register them just like the producer, but it's not required.
Note: The same key management rules apply on the consumer side — see the security warning in the producer section above.
AesMessageInterceptor and TripleDesMessageInterceptor coexist: the per-message interceptor graph records which one encrypted each message, so a consumer can decrypt both during a migration.
-
If you can drain the queue: stop producing, let consumers finish the existing 3DES messages, then replace
TripleDesMessageInterceptorwithAesMessageInterceptoreverywhere. -
If you cannot drain (long-lived messages):
-
Producers: register only
AesMessageInterceptor— new messages are encrypted with AES (no double-encryption). -
Consumers: register both configurations —
AesMessageInterceptorConfigurationandTripleDesMessageInterceptorConfiguration. As noted above, consumers only need the configuration for built-in interceptors; the queue re-creates the interceptors as needed, and the graph selects AES for new messages and 3DES for old ones (a consumer never re-encrypts, so 3DES only ever decrypts). Once the 3DES backlog is gone, drop theTripleDesMessageInterceptorConfigurationfrom the consumers.
-
Producers: register only
You can create your own message interceptors by implementing IMessageInterceptor. Usage is the same as the built-in interceptors; however, for the consumer you may need to register your custom interceptor explicitly so the queue can resolve it.
public interface IMessageInterceptor
{
// Transforms bytes before storage (serialization path).
// Return MessageInterceptorResult(output, addToGraph, interceptorType).
// Set addToGraph = false to opt out (e.g., when compression is not beneficial).
MessageInterceptorResult MessageToBytes(byte[] input, IReadOnlyDictionary<string, object> headers);
// Transforms bytes on retrieval (deserialization path).
byte[] BytesToMessage(byte[] input, IReadOnlyDictionary<string, object> headers);
// Human-readable name used in logging and diagnostics.
string DisplayName { get; }
// Base type used by the queue to re-create the interceptor on the consumer side.
Type BaseType { get; }
}MessageInterceptorResult carries three values:
| Parameter | Type | Purpose |
|---|---|---|
output |
byte[] |
The transformed message bytes |
addToGraph |
bool |
When true, the interceptor records itself so consumers can reverse the transformation; set false to skip (e.g., the GZip interceptor opts out when the compressed result would be larger than the input) |
interceptorType |
Type |
The interceptor type used for re-creation on the consumer side |
Note that interceptors run in the order they are registered. If both compression and encryption are used, register compression first so it runs before encryption.
For any issues please use the GitHub issues