为数据库中的关键字段进行加密是必不可少的,特别是一些用户密码,银行卡账号等。现在我们来说一下如何在Nhibernate中创建一个加密类来为数据库中的关键字段加密。
1 创建一个接口:IEncryptor
public interface IEncryptor { string Encrypt(string plainText); string Decrypt(string encryptedText); string EncryptionKey { get; set; } }
2 再创建一个继承它的子类:SymmetricEncryptorBase
public class SymmetricEncryptorBase : IEncryptor { private readonly SymmetricAlgorithm _cryptoProvider; private byte[] _myBytes; protected SymmetricEncryptorBase( SymmetricAlgorithm cryptoProvider) { _cryptoProvider = cryptoProvider; } #region IEncryptor 成员 public string EncryptionKey { get; set; } /// <summary> /// 加密,利用CryptoStream /// </summary> /// <param name="plainText"></param> /// <returns></returns> public string Encrypt(string plainText) { var bytes = GetEncryptionKeyBytes(); using (var memoryStream = new MemoryStream()) { ICryptoTransform encryptor = _cryptoProvider .CreateEncryptor(bytes, bytes); using (var cryptoStream = new CryptoStream( memoryStream, encryptor, CryptoStreamMode.Write)) { using (var writer = new StreamWriter(cryptoStream)) { writer.Write(plainText); writer.Flush(); cryptoStream.FlushFinalBlock(); return Convert.ToBase64String( memoryStream.GetBuffer(), 0, (int)memoryStream.Length); } } } } //获取密钥 private byte[] GetEncryptionKeyBytes() { if (_myBytes == null) _myBytes = Encoding.ASCII.GetBytes(EncryptionKey); return _myBytes; } /// <summary> /// 解密 /// </summary> /// <param name="encryptedText"></param> /// <returns></returns> public string Decrypt(string encryptedText) { var bytes = GetEncryptionKeyBytes(); using (var memoryStream = new MemoryStream( Convert.FromBase64String(encryptedText))) { ICryptoTransform decryptor = _cryptoProvider .CreateDecryptor(bytes, bytes); using (var cryptoStream = new CryptoStream( memoryStream, decryptor, CryptoStreamMode.Read)) { using (var reader = new StreamReader(cryptoStream)) { return reader.ReadToEnd(); } } } } #endregion }
3 再创建一个类:DESEncryptor
public class DESEncryptor : SymmetricEncryptorBase { public DESEncryptor() : base(new DESCryptoServiceProvider()) { } }
4 再新建一个类:EncryptedString,通过这个类来调用前面的DESEncryptor来加密解密字符串
public class EncryptedString : IUserType, IParameterizedType { private IEncryptor _encryptor; public object NullSafeGet( IDataReader rs, string[] names, object owner) { //treat for the posibility of null values object passwordString = NHibernateUtil.String.NullSafeGet(rs, names[0]); if (passwordString != null) { return _encryptor.Decrypt((string)passwordString); } return null; } public void NullSafeSet(IDbCommand cmd,object value,int index) { if (value == null) { NHibernateUtil.String.NullSafeSet(cmd, null, index); return; } string encryptedValue = _encryptor.Encrypt((string)value); NHibernateUtil.String.NullSafeSet( cmd, encryptedValue, index); } public object DeepCopy(object value) { return value == null ? null : string.Copy((string)value); } public object Replace(object original, object target, object owner) { return original; } public object Assemble(object cached, object owner) { return DeepCopy(cached); } public object Disassemble(object value) { return DeepCopy(value); } public SqlType[] SqlTypes { get { return new[] { new SqlType(DbType.String) }; } } public Type ReturnedType { get { return typeof(string); } } public bool IsMutable { get { return false; } } public new bool Equals(object x, object y) { if (ReferenceEquals(x, y)) { return true; } if (x == null || y == null) { return false; } return x.Equals(y); } public int GetHashCode(object x) { if (x == null) { throw new ArgumentNullException("x"); } return x.GetHashCode(); } public void SetParameterValues( IDictionary<string, string> parameters) { if (parameters != null) { var encryptorTypeName = parameters["encryptor"]; _encryptor = !string.IsNullOrEmpty(encryptorTypeName) ? (IEncryptor)Instantiate(encryptorTypeName) : new DESEncryptor(); var encryptionKey = parameters["encryptionKey"]; if (!string.IsNullOrEmpty(encryptionKey)) _encryptor.EncryptionKey = encryptionKey; } else { _encryptor = new DESEncryptor(); } } private static object Instantiate(string typeName) { var type = Type.GetType(typeName); return Activator.CreateInstance(type); } }
OK.这个加密类到此就完成了。现在我们来创建一个Account对象来测试。
public class Account { public virtual Guid Id { get; set; } public virtual string EMail { get; set; } public virtual string Name { get; set; } public virtual string CardNumber { get; set; } public virtual int ExpirationMonth { get; set; } public virtual int ExpirationYear { get; set; } public virtual string ZipCode { get; set; } }
重要的是定义相应的Account.hbm.xml文件:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="EncryptedStringExample" namespace="EncryptedStringExample"> <typedef name="encrypted" class="EncryptedStringExample.EncryptedString, EncryptedStringExample"> <!--定义加密类--> <param name="encryptor"> EncryptedStringExample.DESEncryptor, EncryptedStringExample </param> <!--设置密钥--> <param name="encryptionKey">12345678</param> </typedef> <class name="Account"> <id name="Id"> <generator class="guid.comb" /> </id> <property name="Name" not-null="true" /> <property name="EMail" not-null="true" /> <!--要加密的字段,注意设置了type="encrypted"--> <property name="CardNumber" not-null="true" type="encrypted" /> <property name="ExpirationMonth" not-null="true" /> <property name="ExpirationYear" not-null="true" /> <property name="ZipCode" not-null="true" /> </class> </hibernate-mapping>
在一个新建的控制台应用程序中,我们新建一个Account对象来测试一下:
private static void AddAccount(NHibernate.ISession session) { session.Save(new Account() { CardNumber="45678", EMail="bb@qq.com", ExpirationMonth=12, ExpirationYear=2012, Name="gyoung", ZipCode="55555" }); }
加密的字段为:CardNumber.打开数据库,看到存储在其中的字段已经被加密了.
现在我们从数据库中取出该字段看看。因为只有一条记录,我就只取第一条了。
private static Account GetAccount(NHibernate.ISession session) { return session.QueryOver<Account>().Take(1).SingleOrDefault(); }
在Main方法中增加两行代码:
Account account = GetAccount(session);
Console.WriteLine(account.CardNumber);
我们可以看到控制台上显示出来的值为:
可以看出解密正确。
源码下载:点我。项目中的EncryptedStringExample与EncryptedStringTest