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