random .NET and web development musings

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);
}
9 COMMENTS
Ilan Galini
June 11, 2010
ad

I was trying to implement a similar approach and was happy to find your code.

The part that puzzles me now is something so obvious I moust be overlooking it:

After setting an entity’s type to be Encrypted, how do I assign a string value to the property ?

The compiler shows a “Cannot implicitly convert type” error.

Ilan Galini
June 11, 2010
ad

HTML atgs stripping… Encrypted is Encrypted<string>

June 11, 2010
ad

the property on your object is still a string, set a customtype in your NH configuration. I’ll update the post later to show this.

Ilan Galini
June 11, 2010
ad

Thanks a lot, that did the trick. Encryption is on !

Ilan Galini
June 12, 2010
ad

I went one step further and wrote a fluent nh automapping convention to look for a custom attribute and apply this CustomType.

public class EncryptedPropetiesConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Property.MemberInfo.IsDefined(typeof(Encrypted), false))
{
Type propertyType = instance.Property.PropertyType;
Type generic = typeof(EncryptedType<>);
Type specific = generic.MakeGenericType(propertyType);
instance.CustomType(specific);

}
}
}

J (Encrypted Flash Drive Guy)
February 17, 2011
ad

Thanks for the code. But I need to save database password in encrypted text and so tell me any solution to use NHibernate to decrypt the password.

February 17, 2011
ad

do you mean you need to encrypt you connection string? This post has nothing to do with doing this. You want: http://www.google.co.uk/search?sourceid=chrome&ie=UTF-8&q=encrypt+connection+string

Dave K
April 13, 2011
ad

Where does ServiceLocator come from?

USB Encryption
May 2, 2011
ad

Nhibernate encryption comes with alot of features like supporting inheritance, polymorphism, the .NET collections framework, including generic collections and composition. No supplementary
codes are required for doing the job.

Post a comment