Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Latest commit

 

History

History
348 lines (264 loc) · 10.8 KB

readme.md

File metadata and controls

348 lines (264 loc) · 10.8 KB

Newtonsoft.Json.Encryption

Build status NuGet Status NuGet Status NuGet Status

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

Community backed

It is expected that all developers either become a Patron to use NServiceBusExtensions. Go to licensing FAQ

Sponsors

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.

Patrons

Thanks to all the backing developers. Support this project by becoming a patron.

Contents

NuGet packages

Encryption Algorithms

Any implementation of SymmetricAlgorithm is supported.

Decorating properties

public class ClassToSerialize
{
    [Encrypt]
    public string Property { get; set; }
}

Serialized

{
    "Property":"wSayABpFI3g7a/D6gGTq5g=="
}

Supported property types

  • 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.

Usage

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);
    }
}

snippet source | anchor

Breakdown

Key

See SymmetricAlgorithm.Key.

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.

EncryptionFactory and JsonSerializer

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()
};

Serialization

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();
    }
}

snippet source | anchor

Deserialization

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);
    }
}

snippet source | anchor

Rebus

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
        });

snippet source | anchor

NServiceBus

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
        });

snippet source | anchor

Icon

Lock designed by Mourad Mokrane from The Noun Project.