begincsdn .NET 趴趴窝
[天行健,君子以自强不息]
[天道酬勤思]

对于小量数据的加密,我们可以使用DPAPI,对称密钥,非对称密钥等。
对于大量数据的加密,非对称密钥加密不仅麻烦,而且速度也很慢,同时还要对公钥和密钥进行保密。
使用对称密钥,速度是相当快的,但仍然要处理密钥的问题,当然这种密钥也有时效性,因为它容易被破解。所出一般情况下用于网络上的会话传输,SSL中用的较多。
对于不想保存密钥,而又要加密和解密来说,DPAPI可能显得简单的多,而且也不需要自己写太多的代码,安全性也得到一定的保证,同时不需要保存密钥,一举多得。

使用举例,下面就DPAPI加密和解密数据库连接字符串举例:

1string dbconn = "Provider=SQLOLEDB;SERVER={0};uid={1};pwd={2};database={3}";
2DataProtector dataProtector = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
3byte[] bts =  Encoding.Default.GetBytes(dbconn);
4byte[] bytes = dataProtector.Encrypt(bts, null);//加密
5string values = Encoding.Default.GetString(dataProtector.Decrypt(bytes, null));//解密
6

下面是对DPAPI的封装。

  1using System;
  2using System.Runtime.InteropServices;
  3
  4namespace CNetware.Data.DPAPI
  5{
  6  /// <summary>
  7  /// DataProtector 是使用 DPAPI 来加密和解密数据的托管库。
  8  /// Web 应用程序经常需要在应用程序配置文件中存储与安全性密切相关的数据,
  9  /// 如数据库连接字符串和服务帐户凭据。出于安全性考虑,决不要以明文形式存
 10  /// 储此类信息,而一定要在存储之前进行加密。本类是一个托管类库,它用于封装
 11  /// 对数据保护 API (DPAPI) 的调用以使用基于计算机或用户的密钥存储来加密和
 12  /// 解密数据。可随后从其他托管应用程序使用该库,如 ASP.NET Web 应用程序、Web
 13  /// 服务以及企业服务应用程序。  
 14  /// DPAPI 是加密 API (Crypto API) 的一部分并且是在 crypt32.dll 中实现的。它
 15  /// 包含两个方法:CryptProtectData 和 CryptUnprotectData,本类库中方法被引用
 16  /// 成了私有方法,在类外不可访问。
 17  /// DPAPI 特别有用,因为它能够消除使用密码的应用程序所带来的密钥管理问题。虽
 18  /// 然加密能确保数据安全,但您必须采取额外的步骤来确保密钥的安全。DPAPI 使用
 19  /// 与 DPAPI 函数的调用代码关联的用户帐户的密码,以便派生加密密钥。因此,是
 20  /// 操作系统(而非应用程序)管理着密钥。
 21  /// 
 22  /// DPAPI 能够与计算机存储或用户存储(需要一个已加载的用户配置文件)配合使用。
 23  /// DPAPI 默认情况下用于用户存储,但您可以通过将 CRYPTPROTECT_LOCAL_MACHINE 
 24  /// 标志传递给 DPAPI 函数来指定使用计算机存储。
 25  /// 
 26  /// 这种用户配置文件方式提供了一个额外的安全层,因为它限制了哪些用户能访问机
 27  /// 密内容。只有加密该数据的用户才能解密该数据。但是,当通过 ASP.NET Web 应用
 28  /// 程序使用 DPAPI 时,使用用户配置文件需要您执行额外的开发工作,因为您需要采
 29  /// 取明确的步骤来加载和卸载用户配置文件(ASP.NET 不会自动加载用户配置文件)。
 30  /// 
 31  /// 计算机存储方式更容易开发,因为它不需要管理用户配置文件。但是,除非使用一个
 32  /// 附加的熵参数,否则并不安全,因为该计算机的任何用户都可以解密数据。(熵是一
 33  /// 个设计用来使解密机密内容更为困难的随机值)。使用附加的熵参数出现的问题在于
 34  /// 它必须由应用程序安全地存储起来,这带来了另一个密钥管理问题。
 35  /// 
 36  /// 注意:如果您将 DPAPI 和计算机存储一起使用,那么加密字符串仅适用于给定的计
 37  /// 算机,因此您必须在每台计算机上生成加密数据。不要在场或群集中将加密数据从一
 38  /// 台计算机复制到另一台计算机。如果将 DPAPI 和用户存储一起使用,则可以用一个漫
 39  /// 游的用户配置文件在任何一台计算机上解密数据。
 40  /// </summary>

 41  public class DataProtector
 42  {
 43    /// <summary>
 44    /// 用于存储二进制数据流,该结构体有两个成员
 45    /// </summary>

 46    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
 47      internal struct DATA_BLOB
 48    {
 49      /// <summary>
 50      /// 用于记录数据长度
 51      /// </summary>

 52      public int cbData;
 53      /// <summary>
 54      /// 用于存储二进制数据
 55      /// </summary>

 56      public IntPtr pbData;
 57    }

 58    /// <summary>
 59    /// 
 60    /// </summary>

 61    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
 62      internal struct CRYPTPROTECT_PROMPTSTRUCT
 63    {
 64      public int cbSize;
 65      public int dwPromptFlags;
 66      public IntPtr hwndApp;
 67      public String szPrompt;
 68    }

 69    private static  IntPtr NullPtr = (IntPtr)0;
 70
 71    private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;
 72
 73    private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;
 74
 75    /// <summary>
 76    /// 
 77    /// </summary>
 78    /// <param name="pDataIn"></param>
 79    /// <param name="szDataDescr"></param>
 80    /// <param name="pOptionalEntropy"></param>
 81    /// <param name="pvReserved"></param>
 82    /// <param name="pPromptStruct"></param>
 83    /// <param name="dwFlags"></param>
 84    /// <param name="pDataOut"></param>
 85    /// <returns></returns>

 86    [DllImport("Crypt32.dll", SetLastError=true,
 87       CharSet=System.Runtime.InteropServices.CharSet.Auto)]
 88    private static extern bool CryptProtectData(
 89      ref DATA_BLOB pDataIn,
 90      String szDataDescr,
 91      ref DATA_BLOB pOptionalEntropy,
 92      IntPtr pvReserved,
 93      ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
 94      int dwFlags,
 95      ref DATA_BLOB pDataOut);
 96
 97    /// <summary>
 98    /// 
 99    /// </summary>
100    /// <param name="pDataIn"></param>
101    /// <param name="szDataDescr"></param>
102    /// <param name="pOptionalEntropy"></param>
103    /// <param name="pvReserved"></param>
104    /// <param name="pPromptStruct"></param>
105    /// <param name="dwFlags"></param>
106    /// <param name="pDataOut"></param>
107    /// <returns></returns>

108    [DllImport("Crypt32.dll", SetLastError=true,
109       CharSet=System.Runtime.InteropServices.CharSet.Auto)]
110    private static extern bool CryptUnprotectData(
111      ref DATA_BLOB pDataIn,
112      String szDataDescr,
113      ref DATA_BLOB pOptionalEntropy,
114      IntPtr pvReserved,
115      ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
116      int dwFlags,
117      ref DATA_BLOB pDataOut);
118
119    [DllImport("kernel32.dll",
120       CharSet=System.Runtime.InteropServices.CharSet.Auto)]
121    private unsafe static extern int FormatMessage(int dwFlags,
122      ref IntPtr lpSource,
123      int dwMessageId,
124      int dwLanguageId,
125      ref String lpBuffer, 
126      int nSize,
127      IntPtr *Arguments);
128
129    /// <summary>
130    /// 用于指定创建DataProtector的类型
131    /// </summary>

132    public enum Store 
133    {
134      /// <summary>
135      /// 机器存储
136      /// </summary>

137      USE_MACHINE_STORE = 1
138
139      /// <summary>
140      /// 用户自己存储
141      /// </summary>

142      USE_USER_STORE
143    }
;
144
145    /// <summary>
146    /// 
147    /// </summary>

148    private Store m_store;
149
150    /// <summary>
151    /// 创建数据保护对象,该对象可能指定数据和处理方法。
152    /// </summary>
153    /// <param name="store">存储类型</param>

154    public DataProtector(Store store)
155    {
156      m_store = store;
157    }

158
159    /// <summary>
160    /// 对传入的文本信息进行加密处理,并以字节流方式返回
161    /// </summary>
162    /// <param name="plainText">被字节化的文本</param>
163    /// <param name="optionalEntropy">随机熵,可以为空,如果创建的是用户管理的DataProtector,该选择无效</param>
164    /// <returns>返回被加过密的内容</returns>

165    public byte[] Encrypt(byte[] plainText, byte[] optionalEntropy)
166    {
167      bool retVal = false;
168      DATA_BLOB plainTextBlob = new DATA_BLOB();
169      DATA_BLOB cipherTextBlob = new DATA_BLOB();
170      DATA_BLOB entropyBlob = new DATA_BLOB();
171      CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
172      InitPromptstruct(ref prompt);
173      int dwFlags;
174      try
175      {
176        try
177        {
178          int bytesSize = plainText.Length;
179          plainTextBlob.pbData = Marshal.AllocHGlobal(bytesSize);
180          if(IntPtr.Zero == plainTextBlob.pbData)
181          {
182            throw new Exception(Strings.ALLOC_TEXT_BUFFER_EXCEPTION);
183          }

184          plainTextBlob.cbData = bytesSize;
185          Marshal.Copy(plainText, 0, plainTextBlob.pbData, bytesSize);
186        }

187        catch(Exception ex)
188        {
189          throw new Exception(Strings.MERGING_EXCEPTION + ex.Message);
190        }

191        if(Store.USE_MACHINE_STORE == m_store)
192        {//使用计算机存储,应该提供熵。
193          dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
194          //查看熵是否为空
195          if(null == optionalEntropy)
196          {//分配对象
197            optionalEntropy = new byte[0];
198          }

199          try
200          {
201            int bytesSize = optionalEntropy.Length;
202            entropyBlob.pbData = Marshal.AllocHGlobal(optionalEntropy
203              .Length);;
204            if(IntPtr.Zero == entropyBlob.pbData)
205            {
206              throw new Exception(Strings.CANNOT_ALLOC_BUFFER_FOR_ENTROPY);
207            }

208            Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);
209            entropyBlob.cbData = bytesSize;
210          }

211          catch(Exception ex)
212          {
213            
214            throw new Exception(Strings.ENTROPY_MERGING_EXCEPTION +
215              ex.Message);
216          }

217        }

218        else
219        {//使用用户存储
220          dwFlags = CRYPTPROTECT_UI_FORBIDDEN;
221        }

222        retVal = CryptProtectData(ref plainTextBlob, ""ref entropyBlob,
223          IntPtr.Zero, ref prompt, dwFlags,
224          ref cipherTextBlob);
225        if(false == retVal)
226        {
227          throw new Exception(Strings.ENCRYPT_FAILED +
228            GetErrorMessage(Marshal.GetLastWin32Error()));
229        }

230      }

231      catch(Exception ex)
232      {
233        throw new Exception(Strings.ENCRYPT_EXCEPTION + ex.Message);
234      }

235      byte[] cipherText = new byte[cipherTextBlob.cbData];
236      Marshal.Copy(cipherTextBlob.pbData, cipherText, 0, cipherTextBlob.cbData);
237      return cipherText;
238    }

239
240    /// <summary>
241    /// 对已经加过密的数据进行解密
242    /// </summary>
243    /// <param name="cipherText">密文</param>
244    /// <param name="optionalEntropy">随机熵</param>
245    /// <returns>返回解密后的明文</returns>

246    public byte[] Decrypt(byte[] cipherText, byte[] optionalEntropy)
247    {
248      bool retVal = false;
249      DATA_BLOB plainTextBlob = new DATA_BLOB();
250      DATA_BLOB cipherBlob = new DATA_BLOB();
251      CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
252      InitPromptstruct(ref prompt);
253      try
254      {
255        try
256        {
257          int cipherTextSize = cipherText.Length;
258          cipherBlob.pbData = Marshal.AllocHGlobal(cipherTextSize);
259          if(IntPtr.Zero == cipherBlob.pbData)
260          {
261            throw new Exception(String.Format(Strings.ALLOC_BUFFER_FAILED,"cipherBlob"));
262          }

263          cipherBlob.cbData = cipherTextSize;
264          Marshal.Copy(cipherText, 0, cipherBlob.pbData, cipherBlob.cbData);
265        }

266        catch(Exception ex)
267        {
268          throw new Exception(Strings.MERGING_EXCEPTION + ex.Message);
269        }

270        DATA_BLOB entropyBlob = new DATA_BLOB();
271        int dwFlags;
272        if(Store.USE_MACHINE_STORE == m_store)
273        {//使用计算机存储,应该提供熵。
274          dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
275          //查看熵是否为空
276          if(null == optionalEntropy)
277          {//分配对象
278            optionalEntropy = new byte[0];
279          }

280          try
281          {
282            int bytesSize = optionalEntropy.Length;
283            entropyBlob.pbData = Marshal.AllocHGlobal(bytesSize);
284            if(IntPtr.Zero == entropyBlob.pbData)
285            {
286              throw new Exception(String.Format(Strings.ALLOC_BUFFER_FAILED,""));
287            }

288            entropyBlob.cbData = bytesSize;
289            Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);
290          }

291          catch(Exception ex)
292          {
293            throw new Exception("Entropy(熵)"+Strings.MERGING_EXCEPTION +ex.Message);
294          }

295        }

296        else
297        {//使用用户存储
298          dwFlags = CRYPTPROTECT_UI_FORBIDDEN;
299        }

300        retVal = CryptUnprotectData(ref cipherBlob, nullref entropyBlob,
301          IntPtr.Zero, ref prompt, dwFlags,
302          ref plainTextBlob);
303        if(false == retVal)
304        {
305          throw new Exception(Strings.DECRYPT_FAILED +
306            GetErrorMessage(Marshal.GetLastWin32Error()));
307        }

308        //释放 blob 和熵。
309        if(IntPtr.Zero != cipherBlob.pbData)
310        {
311          Marshal.FreeHGlobal(cipherBlob.pbData);
312        }

313        if(IntPtr.Zero != entropyBlob.pbData)
314        {
315          Marshal.FreeHGlobal(entropyBlob.pbData);
316        }

317      }

318      catch(Exception ex)
319      {
320        throw new Exception(Strings.DECRYPT_EXCEPTION + ex.Message);
321      }

322      byte[] plainText = new byte[plainTextBlob.cbData];
323      Marshal.Copy(plainTextBlob.pbData, plainText, 0, plainTextBlob.cbData);
324      return plainText;
325    }

326
327    /// <summary>
328    /// 
329    /// </summary>
330    /// <param name="ps"></param>

331    private void InitPromptstruct(ref CRYPTPROTECT_PROMPTSTRUCT ps)
332    {
333      ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT));
334      ps.dwPromptFlags = 0;
335      ps.hwndApp = NullPtr;
336      ps.szPrompt = null;
337    }

338
339    /// <summary>
340    /// 
341    /// </summary>
342    /// <param name="errorCode"></param>
343    /// <returns></returns>

344    private unsafe static String GetErrorMessage(int errorCode)
345    {
346      int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
347      int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
348      int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
349      int messageSize = 255;
350      String lpMsgBuf = "";
351      int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
352        FORMAT_MESSAGE_FROM_SYSTEM |
353        FORMAT_MESSAGE_IGNORE_INSERTS;
354      IntPtr ptrlpSource = new IntPtr();
355      IntPtr prtArguments = new IntPtr();
356      int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0,
357        ref lpMsgBuf, messageSize, &
358        prtArguments);
359      if(0 == retVal)
360      {
361        throw new Exception(Strings.SET_ERROR_MSG_FAILED + errorCode + "");
362      }

363      return lpMsgBuf;
364    }

365  }

366  internal class Strings
367  {
368    private Strings()
369    {
370    }

371    internal static string ENTROPY_MERGING_EXCEPTION    ="熵整理数据时发生异常。";
372    internal static string MERGING_EXCEPTION        ="整理数据时发生异常。";
373    internal static string CANNOT_ALLOC_BUFFER_FOR_ENTROPY  ="无法分配熵数据缓冲区。";
374    internal static string ALLOC_TEXT_BUFFER_EXCEPTION    ="无法分配纯文本缓冲区。";
375    internal static string ALLOC_BUFFER_FAILED        ="为{0}分配缓存区失败。";
376    internal static string ENCRYPT_FAILED         ="加密失败。";
377    internal static string DECRYPT_FAILED         ="解密失败。";
378    internal static string ENCRYPT_EXCEPTION        ="加密时发生异常。";
379    internal static string DECRYPT_EXCEPTION        ="解密时发生异常。";
380    internal static string SET_ERROR_MSG_FAILED       ="无法设置错误代码消息的格式。";
381    internal static string NOT_RIGHT_CONFIGPATH       ="不正确的配置文件路径。";
382    internal static string CONFIG_PATH_NOT_SETTED     ="配置文件的全路径未设置。";
383    internal static string NOT_FOUND_CONNECTION_STRING    ="没有发现配置文件中有此文件。";
384    internal static string CONN_STRING_NAMED_EXCEPTION    ="配置文件中定义的数据库连接字符串的key的名字应当以ConnectionString打头。";
385  }

386}

387
388
posted on 2005-07-13 11:17  begincsdn  阅读(4900)  评论(3编辑  收藏  举报