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

Leverages the Newtonsoft extension API to encrypt/decrypt specific nodes at serialization time

License

Notifications You must be signed in to change notification settings

NServiceBusExtensions/Newtonsoft.Json.Encryption

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

Leverages the Newtonsoft extension API to encrypt/decrypt specific nodes at serialization time

Resources

License

Security policy

Stars

Watchers

Forks

Languages