Table of Contents

Encrypting Objects During Serialization

Implement a custom serializer that runs objects through .NET's CryptoStream to encrypt objects prior to storage in the ScaleOut service.

Example

        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.Security.Cryptography;
        using ProtoBuf;
        using Scaleout.Client;

        /// <summary>
        /// Serializer to encrypt/decrypt objects using AES.
        /// </summary>
        /// <typeparam name="T">Type of object to serialize/deserialize.</typeparam>
        public class CryptoSerializer<T>
        {
            private byte[] _secretKey;

            /// <summary>
            /// Constructor.
            /// </summary>
            /// <param name="secretKey">
            /// Secret key. Legal AES keys are 16, 24, or 32 bytes long.
            /// </param>
            public CryptoSerializer(byte[] secretKey)
            {
                _secretKey = secretKey;
            }

            /// <summary>
            /// Serialization callback that can be registered with 
            /// a cache using CacheBuilder.SetSerialization
            /// </summary>
            public void Serialize(T obj, Stream stream)
            {
                // The first 16 bytes of the serialized stream is the 
                // AES initialization vector. (An IV does not need to be
                // secret, but the same IV should never be used twice with
                // the same key.)
                byte[] iv = RandomNumberGenerator.GetBytes(16);
                stream.Write(iv, 0, 16);

                using (var aes = Aes.Create())
                {
                    aes.Key = _secretKey;
                    aes.IV = iv;

                    var cryptoStream = new CryptoStream(
                            stream,
                            aes.CreateEncryptor(),
                            CryptoStreamMode.Write);

                    // Using protobuf-net for serialization
                    // (but any serializer can be used to write to this CryptoStream).
                    ProtoBuf.Serializer.Serialize(cryptoStream, obj);

                    cryptoStream.FlushFinalBlock();
                }
            }

            /// <summary>
            /// Deserialization callback that can be registered with 
            /// a cache using CacheBuilder.SetSerialization
            /// </summary>
            public T Deserialize(Stream stream)
            {
                // First 16 bytes is the initialization vector.
                byte[] iv = new byte[16];
                stream.Read(iv, 0, 16);

                using (var aes = Aes.Create())
                {
                    aes.Key = _secretKey;
                    aes.IV = iv;

                    var cryptoStream = new CryptoStream(
                            stream,
                            aes.CreateDecryptor(),
                            CryptoStreamMode.Read);

                    return ProtoBuf.Serializer.Deserialize<T>(cryptoStream);
                }
            }
        }


        /// <summary>
        /// Player class to be encrypted and stored in the ScaleOut service.
        /// </summary>
        /// <remarks>
        /// This example uses protobuf-net for serialization, but this 
        /// could be replaced by any serialization library.
        /// </remarks>
        [ProtoContract]
        public class Player
        {
            [ProtoMember(1)]
            public string PlayerId { get; set; }

            [ProtoMember(2)]
            public List<int> ScoreHistory { get; set; }
        }


        class Program
        {
            // IMPORTANT:
            //  This cryptographic key is defined in code for demonstration purposes.
            //  Production keys should be stored in a secure location,
            //  (such as Azure Key Vault or AWS KMS) or protected using .NET's 
            //  ProtectedData class.
            private static readonly byte[] s_key = new byte[]
                                   { 87, 167, 103, 151, 197, 100, 254, 130,
                                     74,  59,  51,  28,  26, 230,   7,  97,
                                    137, 224,  69,  23,  51, 110,   3,  37,
                                    157,  41,  12,  12, 158,  24,  30,  86 };

            static void Main(string[] args)
            {
                var conn = GridConnection.Connect("bootstrapGateways=localhost:721");

                // Build cache and configure it to user our CryptoSerializer:
                var builder = new CacheBuilder<int, Player>("players", conn);
                var cryptoSerializer = new CryptoSerializer<Player>(s_key);
                builder.SetSerialization(cryptoSerializer.Serialize, cryptoSerializer.Deserialize);
                
                var playerCache = builder.Build();
                //...
            }
        }
    }
}