Leverages the Newtonsoft extension API to encrypt/decrypt specific nodes at serialization time. So only the nodes that require encryption are touched, the remaining content is still human readable. This approach provides an compromise between readability/debugabaility and security.
Already a Patron? skip past this section
It is expected that all developers either become a Patron to use NServiceBusExtensions. Go to licensing FAQ
Support this project by becoming a Sponsor. The company avatar will show up here with a website link. The avatar will also be added to all GitHub repositories under the NServiceBusExtensions organization.
Thanks to all the backing developers. Support this project by becoming a patron.
- Encryption Algorithms
- Decorating properties
- Serialized
- Supported property types
- Usage
- Breakdown
- Rebus
- NServiceBus
- https://nuget.org/packages/Newtonsoft.Json.Encryption/
- https://nuget.org/packages/Rebus.Newtonsoft.Encryption/
- https://nuget.org/packages/NServiceBus.Newtonsoft.Encryption/
Any implementation of SymmetricAlgorithm is supported.
public class ClassToSerialize
{
[Encrypt]
public string Property { get; set; }
}
{
"Property":"wSayABpFI3g7a/D6gGTq5g=="
}
string
byte[]
Guid
IDictionary<T, string>
IDictionary<T, byte[]>
IDictionary<T, Guid>
IEnumerable<string>
IEnumerable<byte[]>
IEnumerable<Guid>
Note that only the values in a IDictionary
are encrypted.
The full serialize and deserialization workflow:
// per system (periodically rotated)
var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6");
// per app domain
using var factory = new EncryptionFactory();
var serializer = new JsonSerializer
{
ContractResolver = factory.GetContractResolver()
};
// transferred as meta data with the serialized payload
byte[] initVector;
string serialized;
// per serialize session
using (var algorithm = new RijndaelManaged
{
Key = key
})
{
//TODO: store initVector for use in deserialization
initVector = algorithm.IV;
using (factory.GetEncryptSession(algorithm))
{
var instance = new ClassToSerialize
{
Property = "PropertyValue",
};
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
serializer.Serialize(writer, instance);
}
serialized = builder.ToString();
}
}
// per deserialize session
using (var algorithm = new RijndaelManaged
{
IV = initVector,
Key = key
})
{
using (factory.GetDecryptSession(algorithm))
{
using var stringReader = new StringReader(serialized);
using var jsonReader = new JsonTextReader(stringReader);
var deserialized = serializer.Deserialize<ClassToSerialize>(jsonReader);
Console.WriteLine(deserialized!.Property);
}
}
Example Key used for RijndaelManaged algorithm in the below sample code:
var key = Encoding.UTF8.GetBytes("gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e6");
A new valid key can be generated by instanitiating a SymmetricAlgorithm and accessing SymmetricAlgorithm.Key.
Generally a single instance of EncryptionFactory
will exist per AppDomain.
A single instance of EncryptionFactory
is safe to be used for multiple instances of JsonSerializer
.
var factory = new EncryptionFactory();
var serializer = new JsonSerializer
{
ContractResolver = factory.GetContractResolver()
};
A single encrypt session is used per serialization instance.
On instantiation the SymmetricAlgorithm
will generate a valid IV. This is generally a good value to use for serialization and then stored for deserialization.
// per serialize session
using (var algorithm = new RijndaelManaged
{
Key = key
})
{
//TODO: store initVector for use in deserialization
initVector = algorithm.IV;
using (factory.GetEncryptSession(algorithm))
{
var instance = new ClassToSerialize
{
Property = "PropertyValue",
};
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
serializer.Serialize(writer, instance);
}
serialized = builder.ToString();
}
}
A single decrypt session is used per serialization instance.
key
must be the same as the one use for serialization.initVector
must be the same as the one use for serialization. It is safe to be transferred with the serialized text.
// per deserialize session
using (var algorithm = new RijndaelManaged
{
IV = initVector,
Key = key
})
{
using (factory.GetDecryptSession(algorithm))
{
using var stringReader = new StringReader(serialized);
using var jsonReader = new JsonTextReader(stringReader);
var deserialized = serializer.Deserialize<ClassToSerialize>(jsonReader);
Console.WriteLine(deserialized!.Property);
}
}
var activator = new BuiltinHandlerActivator();
activator.Register(() => new Handler());
var configurer = Configure.With(activator);
var encryptionFactory = new EncryptionFactory();
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
ContractResolver = encryptionFactory.GetContractResolver()
};
configurer.Serialization(s => { s.UseNewtonsoftJson(settings); });
configurer.EnableJsonEncryption(
encryptionFactory: encryptionFactory,
encryptStateBuilder: () =>
(
algorithm: new RijndaelManaged
{
Key = key
},
keyId: "1"
),
decryptStateBuilder: (keyId, initVector) =>
new RijndaelManaged
{
Key = key,
IV = initVector
});
var configuration = new EndpointConfiguration("NServiceBusSample");
var serialization = configuration.UseSerialization<NewtonsoftJsonSerializer>();
var encryptionFactory = new EncryptionFactory();
serialization.Settings(
new()
{
ContractResolver = encryptionFactory.GetContractResolver()
});
configuration.EnableJsonEncryption(
encryptionFactory: encryptionFactory,
encryptStateBuilder: () =>
(
algorithm: new RijndaelManaged
{
Key = key
},
keyId: "1"
),
decryptStateBuilder: (keyId, initVector) =>
new RijndaelManaged
{
Key = key,
IV = initVector
});
Lock designed by Mourad Mokrane from The Noun Project.