The simplest way I’ve found to encrypt data with NHibernate is to use a custom UserType which encrypts/decrypts the data on read/write.
As this post on stackoverflow demonstrates, it’s rather easy to make such a UserType. However, what I didn’t want to have to do is make a different UserType for each different property type that I want to encrypt.
Enter Encrypted<T>:
public class Encrypted<T> : PrimitiveType
{
private readonly IEncryptionService encrypter;
private readonly IBinaryConverter converter;
public Encrypted() : base(new BinarySqlType())
{
this.encrypter = ServiceLocator.Current.GetInstance<IEncryptionService>();
this.converter = ServiceLocator.Current.GetInstance<IBinaryConverter>();
}
public override string Name
{
get { return typeof (T).Name; }
}
public override Type ReturnedClass
{
get { return typeof (T); }
}
public override Type PrimitiveClass
{
get { return typeof (T); }
}
public override object DefaultValue
{
get { return default(T); }
}
public override void Set(IDbCommand cmd, object value, int index)
{
var serialized = this.converter.Serialize(value);
var encrypted = this.encrypter.Encrypt(serialized);
((IDataParameter) cmd.Parameters[index]).Value = encrypted;
}
public override object Get(IDataReader rs, int index)
{
if (rs.IsDBNull(index))
return null;
var encrypted = rs[index] as byte[];
var decrypted = this.encrypter.Decrypt(encrypted);
var deserialized = this.converter.Deserialize(decrypted);
return deserialized;
}
public override object Get(IDataReader rs, string name)
{
return this.Get(rs, rs.GetOrdinal(name));
}
public override object FromStringValue(string xml)
{
if (xml == null)
return null;
if (xml.Length % 2 != 0)
throw new ArgumentException("The string is not a valid xml representation of a binary content.", "xml");
var bytes = new byte[xml.Length / 2];
for (var i = 0; i < bytes.Length; i++)
{
var hexStr = xml.Substring(i * 2, (i + 1) * 2);
bytes[i] = (byte) (byte.MinValue + byte.Parse(hexStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture));
}
var decrypted = this.encrypter.Decrypt(bytes);
var deserialized = this.converter.Deserialize(decrypted);
return deserialized;
}
public override string ObjectToSQLString(object value, Dialect dialect)
{
var bytes = value as byte[];
if (bytes == null)
return "NULL";
var builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
string hexStr = (bytes[i] - byte.MinValue).ToString("x", CultureInfo.InvariantCulture);
if (hexStr.Length == 1)
builder.Append('0');
builder.Append(hexStr);
}
return builder.ToString();
}
}
The implementations of IBinaryConverter and IEncryptionService aren’t important for the purposes of this post, here are their signatures so you can implement then how you like:
public interface IBinaryConverter
{
byte[] Serialize(object obj);
object Deserialize(byte[] bytes);
}
public interface IEncryptionService
{
byte[] Encrypt(byte[] plain);
byte[] Decrypt(byte[] cipher);
}