使用NHibernate加密和解密数据

时间:2021-07-05 04:03:59

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.

我正在编写一个可公开访问的Web应用程序,其中包含个人用户数据,例如姓名和出生日期,我需要以一种对可能访问原始数据的人很难解密的形式加密这些数据。我正在使用Fluent NHibernate,mySQL和C#3.5。

  1. What method should I use for industry standard encryption and decryption of user information? The method of encryption should not be database dependent.

    我应该使用什么方法进行行业标准加密和解密用户信息?加密方法不应该依赖于数据库。

  2. 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?

    如何告诉nHibernate使用简单属性(如StorageType = StorageType.Encrypted)对某些映射类进行透明加密/解密。我不介意生成的数据库表只有一列或两列,或者每个加密字段一列。根据我的发现,我应该从IUserDataType创建自己的数据类型并加密构造函数中的数据。它是否正确?

2 个解决方案

#1


12  

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.

这会在域对象中公开字符串属性,但会将其作为表示加密表单的二进制文件(字节数组)保留。在我的提供者模式代码中,Encrypt接受一个字符串并返回一个字节数组,而Decrypt则相反。

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

#2


4  

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.

我将创建一个EncryptionService,使用您想要的任何Key加密字符串。然后我会在你的实体中制作2个属性。 NHibernate与(加密值)交互的一个和您(或其他开发人员)与之交互的另一个将自动加密值。

See: http://kockerbeck.blogspot.com/2009/08/fluent-nhibernate-encrypting-values.html

A sample EncryptionService, User entity and UserMap are below.

下面是一个示例EncryptionService,User实体和UserMap。

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:

和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;
        }
    }
}

#1


12  

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.

这会在域对象中公开字符串属性,但会将其作为表示加密表单的二进制文件(字节数组)保留。在我的提供者模式代码中,Encrypt接受一个字符串并返回一个字节数组,而Decrypt则相反。

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

#2


4  

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.

我将创建一个EncryptionService,使用您想要的任何Key加密字符串。然后我会在你的实体中制作2个属性。 NHibernate与(加密值)交互的一个和您(或其他开发人员)与之交互的另一个将自动加密值。

See: http://kockerbeck.blogspot.com/2009/08/fluent-nhibernate-encrypting-values.html

A sample EncryptionService, User entity and UserMap are below.

下面是一个示例EncryptionService,User实体和UserMap。

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:

和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;
        }
    }
}