I'm writing a publicly accessible web application which will contain personal user data, such as names and birth dates, and I'm required to encrypt this data in a form that will be difficult for a human who might access the raw data to decrypt. I'm using Fluent NHibernate, mySQL, and C# 3.5.
What method should I use for industry standard encryption and decryption of user information? The method of encryption should not be database dependent.
How do I tell nHibernate to do transparent encryption/decryption on certain mapped classes with a simple property, like StorageType = StorageType.Encrypted
. I don't mind if the resulting database table has just one or two columns, or one for each encrypted field. From what I've found, I should create my own data type from IUserDataType
and encrypt the data in the constructor. Is this correct?
In true Blue Peter fashion, here's one I created earlier to do just this. It relies on a provider pattern to get the encryption algorithm but you could replace this with whatever you want.
This exposes a string property in your domain object, but persists it as a binary (array of bytes) representing the encrypted form. In my provider pattern code, Encrypt takes a string and returns a byte array, and Decrypt does the opposite.
[Serializable]
public class EncryptedStringType : PrimitiveType
{
public EncryptedStringType() : this(new BinarySqlType()) {}
public EncryptedStringType(SqlType sqlType) : base(sqlType) {}
public override string Name
{
get { return "String"; }
}
public override Type ReturnedClass
{
get { return typeof (string); }
}
public override Type PrimitiveClass
{
get { return typeof (string); }
}
public override object DefaultValue
{
get { return null; }
}
public override void Set(IDbCommand cmd, object value, int index)
{
if (cmd == null) throw new ArgumentNullException("cmd");
if (value == null)
{
((IDataParameter)cmd.Parameters[index]).Value = null;
}
else
{
((IDataParameter)cmd.Parameters[index]).Value = EncryptionManager.Provider.Encrypt((string)value);
}
}
public override object Get(IDataReader rs, int index)
{
if (rs == null) throw new ArgumentNullException("rs");
var encrypted = rs[index] as byte[];
if (encrypted == null) return null;
return EncryptionManager.Provider.Decrypt(encrypted);
}
public override object Get(IDataReader rs, string name)
{
return 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 (int i = 0; i < bytes.Length; i++)
{
string hexStr = xml.Substring(i * 2, (i + 1) * 2);
bytes[i] = (byte)(byte.MinValue
+ byte.Parse(hexStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture));
}
return EncryptionManager.Provider.Decrypt(bytes);
}
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();
}
}
I would create an EncryptionService that encrypts strings using whatever Key you'd like. Then I would make 2 properties in your entity. One that NHibernate interacts with (Encrypted values) and another that you (or other developers) interact with that will automatically encrypt the values.
See: http://kockerbeck.blogspot.com/2009/08/fluent-nhibernate-encrypting-values.html
A sample EncryptionService, User entity and UserMap are below.
public class User
{
private readonly EncryptionService _encryptionService =
new EncryptionService();
public virtual int Id { get; set; }
public virtual DateTime? DateOfBirth
{
get
{
return _encryptionService.DecryptObject<DateTime?>(DateOfBirthEncrypted);
}
set
{
DateOfBirthEncrypted= _encryptionService.EncryptString(value.Value
.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
[Obsolete("Use the 'DateOfBirth' property -- this property is only to be used by NHibernate")]
public virtual string DateOfBirthEncrypted { get; set; }
}
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
WithTable("dbo.[User]");
Id(x => x.Id, "[ID]");
Map(x => x.DateOfBirthEncrypted, "DOB");
}
}
And the EncryptionService:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Services
{
public class EncryptionService : IEncryptionService
{
/// <summary>
/// Decrypts a string
/// </summary>
/// <param name="encryptedString"></param>
/// <returns></returns>
public String DecryptString(string encryptedString)
{
if (String.IsNullOrEmpty(encryptedString)) return String.Empty;
try
{
using (TripleDESCryptoServiceProvider cypher = new TripleDESCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes("ENTERAKEYHERE", new byte[0]);
cypher.Key = pdb.GetBytes(16);
cypher.IV = pdb.GetBytes(8);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cypher.CreateDecryptor(), CryptoStreamMode.Write))
{
byte[] data = Convert.FromBase64String(encryptedString);
cs.Write(data, 0, data.Length);
cs.Close();
return Encoding.Unicode.GetString(ms.ToArray());
}
}
}
}
catch
{
return String.Empty;
}
}
/// <summary>
/// Encrypts a string
/// </summary>
/// <param name="decryptedString"
/// <returns></returns>
public String EncryptString(string decryptedString)
{
if (String.IsNullOrEmpty(decryptedString)) return String.Empty;
using (TripleDESCryptoServiceProvider cypher = new TripleDESCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes("ENTERAKEYHERE", new byte[0]);
cypher.Key = pdb.GetBytes(16);
cypher.IV = pdb.GetBytes(8);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cypher.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] data = Encoding.Unicode.GetBytes(decryptedString);
cs.Write(data, 0, data.Length);
cs.Close();
return Convert.ToBase64String(ms.ToArray());
}
}
}
}
/// <summary>
/// Decrypts a given value as type of T, if unsuccessful the defaultValue is used
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public T DecryptObject<T>(object value, T defaultValue)
{
if (value == null) return defaultValue;
try
{
Type conversionType = typeof(T);
// Some trickery for Nullable Types
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
conversionType = new NullableConverter(conversionType).UnderlyingType;
}
return (T)Convert.ChangeType(DecryptString(Convert.ToString(value)), conversionType);
}
catch
{
// Do nothing
}
return defaultValue;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With