How To: Use DPAPI to Encrypt and Decrypt Data

The code below demonstrates how to call Data Protection API (DPAPI) functions
CryptProtectData and CryptUnprotectData to encrypt and decrypt data. The code
sample is provided in C#.

Introduction DPAPI functions encrypt and decrypt data using the Triple-DES algorithm. In addition to encryption and decryption, the API handles key generation and protection. DPAPI can generate two types of encryption keys: user- or machine-specific (these key types are commonly referred to as user store and machine store). User store and machine store are mutually exclusive; this means that you cannot combine a user-specific key with machine-specific key in one DPAPI call.

DPAPI with user store When making DPAPI calls with user-specific keys, encryption and decryption must be performed by the same user (i.e. the identity under which the application runs). Applications calling DPAPI functions with user-specific keys must run with loaded user profiles of Windows® domain or local accounts; they cannot use the profiles of the built-in system accounts, such as LocalSystem, ASPNET, IUSR_MachineName, etc. The user profile must be created on the system where DPAPI calls are made, which normally requires the user to log on to the system interactively at least once. IMPORTANT: ASP.NET applications and other programs running under the built-in system accounts, such as Windows® services running as LocalSystem, cannot use DPAPI with user-specific keys.

DPAPI with machine store When making DPAPI calls with machine-specific keys, encryption and decryption can be performed by any user or application as long as both operations are executed on the same computer. Any application - including ASP.NET - can use DPAPI with machine-specific keys. It is worth noting that this option is not secure, because it allows a malicious application installed on a system to decrypt any data encrypted by other applications on the same system using DPAPI with machine-specific keys.

Secondary entropy When an application calls the DPAPI encryption function, it can specify an optional secondary entropy ("secret" bytes) which will have to be provided by an application attempting to decrypt data. The secondary entropy can be used with either user- or machine-specific keys. It must be protected.

Data description When an application calls the DPAPI encryption function, it can specify an optional data description, which will be returned by the DPAPI decryption routine.

More info For additional information about using DPAPI in .NET, check Building Secure ASP.NET Applications, which provides a brief description of DPAPI and several code samples. For a detailed overview of DPAPI, read the Windows Data Protection article.

Resources Our FREE CipherLite.NET™ tool provides a GUI and library, which you can use to encrypt and decrypt data with DPAPI.

View Code
  1 ///////////////////////////////////////////////////////////////////////////////
  2 // SAMPLE: Encryption and decryption using DPAPI functions.
  3 //
  4 // To run this sample, create a new Visual C# project using the Console
  5 // Application template and replace the contents of the Class1.cs file
  6 // with the code below.
  7 //
  8 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  9 // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 10 // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
 11 // PURPOSE.
 12 //
 13 // Copyright (C) 2003 Obviex(TM). All rights reserved.
 14 //
 15 using System;
 16 using System.Text;
 17 using System.Runtime.InteropServices;
 18 using System.ComponentModel;
 19 
 20 /// <summary>
 21 /// Encrypts and decrypts data using DPAPI functions.
 22 /// </summary>
 23 public class DPAPI
 24 {
 25     // Wrapper for DPAPI CryptProtectData function.
 26     [DllImport( "crypt32.dll",
 27                 SetLastError=true,
 28                 CharSet=System.Runtime.InteropServices.CharSet.Auto)]
 29     private static extern
 30         bool CryptProtectData(  ref DATA_BLOB     pPlainText,
 31                                     string        szDescription,
 32                                 ref DATA_BLOB     pEntropy,
 33                                     IntPtr        pReserved,
 34                                 ref CRYPTPROTECT_PROMPTSTRUCT pPrompt,
 35                                     int           dwFlags,
 36                                 ref DATA_BLOB     pCipherText);
 37 
 38     // Wrapper for DPAPI CryptUnprotectData function.
 39     [DllImport( "crypt32.dll",
 40                 SetLastError=true,
 41                 CharSet=System.Runtime.InteropServices.CharSet.Auto)]
 42     private static extern
 43         bool CryptUnprotectData(ref DATA_BLOB       pCipherText,
 44                                 ref string          pszDescription,
 45                                 ref DATA_BLOB       pEntropy,
 46                                     IntPtr          pReserved,
 47                                 ref CRYPTPROTECT_PROMPTSTRUCT pPrompt,
 48                                     int             dwFlags,
 49                                 ref DATA_BLOB       pPlainText);
 50 
 51     // BLOB structure used to pass data to DPAPI functions.
 52     [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
 53     internal struct DATA_BLOB
 54     {
 55         public int     cbData;
 56         public IntPtr  pbData;
 57     }
 58 
 59     // Prompt structure to be used for required parameters.
 60     [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
 61     internal struct CRYPTPROTECT_PROMPTSTRUCT
 62     {
 63         public int      cbSize;
 64         public int      dwPromptFlags;
 65         public IntPtr   hwndApp;
 66         public string   szPrompt;
 67     }
 68 
 69     // Wrapper for the NULL handle or pointer.
 70     static private IntPtr NullPtr = ((IntPtr)((int)(0)));
 71 
 72     // DPAPI key initialization flags.
 73     private const int CRYPTPROTECT_UI_FORBIDDEN  = 0x1;
 74     private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;
 75 
 76     /// <summary>
 77     /// Initializes empty prompt structure.
 78     /// </summary>
 79     /// <param name="ps">
 80     /// Prompt parameter (which we do not actually need).
 81     /// </param>
 82     private static void InitPrompt(ref CRYPTPROTECT_PROMPTSTRUCT ps)
 83     {
 84         ps.cbSize       = Marshal.SizeOf(
 85                                   typeof(CRYPTPROTECT_PROMPTSTRUCT));
 86         ps.dwPromptFlags= 0;
 87         ps.hwndApp      = NullPtr;
 88         ps.szPrompt     = null;
 89     }
 90 
 91     /// <summary>
 92     /// Initializes a BLOB structure from a byte array.
 93     /// </summary>
 94     /// <param name="data">
 95     /// Original data in a byte array format.
 96     /// </param>
 97     /// <param name="blob">
 98     /// Returned blob structure.
 99     /// </param>
100     private static void InitBLOB(byte[] data, ref DATA_BLOB blob)
101     {
102         // Use empty array for null parameter.
103         if (data == null)
104             data = new byte[0];
105             
106         // Allocate memory for the BLOB data.
107         blob.pbData = Marshal.AllocHGlobal(data.Length);
108 
109         // Make sure that memory allocation was successful.
110         if (blob.pbData == IntPtr.Zero)
111             throw new Exception(
112                 "Unable to allocate data buffer for BLOB structure.");
113 
114         // Specify number of bytes in the BLOB.
115         blob.cbData = data.Length;
116 
117         // Copy data from original source to the BLOB structure.
118         Marshal.Copy(data, 0, blob.pbData, data.Length);
119     }
120 
121     // Flag indicating the type of key. DPAPI terminology refers to
122     // key types as user store or machine store.
123     public enum KeyType {UserKey = 1, MachineKey};
124 
125     // It is reasonable to set default key type to user key.
126     private static KeyType defaultKeyType = KeyType.UserKey;
127 
128     /// <summary>
129     /// Calls DPAPI CryptProtectData function to encrypt a plaintext
130     /// string value with a user-specific key. This function does not
131     /// specify data description and additional entropy.
132     /// </summary>
133     /// <param name="plainText">
134     /// Plaintext data to be encrypted.
135     /// </param>
136     /// <returns>
137     /// Encrypted value in a base64-encoded format.
138     /// </returns>
139     public static string Encrypt(string plainText)
140     {
141         return Encrypt(defaultKeyType, plainText, String.Empty,
142                         String.Empty);
143     }
144 
145     /// <summary>
146     /// Calls DPAPI CryptProtectData function to encrypt a plaintext
147     /// string value. This function does not specify data description
148     /// and additional entropy.
149     /// </summary>
150     /// <param name="keyType">
151     /// Defines type of encryption key to use. When user key is
152     /// specified, any application running under the same user account
153     /// as the one making this call, will be able to decrypt data.
154     /// Machine key will allow any application running on the same
155     /// computer where data were encrypted to perform decryption.
156     /// Note: If optional entropy is specifed, it will be required
157     /// for decryption.
158     /// </param>
159     /// <param name="plainText">
160     /// Plaintext data to be encrypted.
161     /// </param>
162     /// <returns>
163     /// Encrypted value in a base64-encoded format.
164     /// </returns>
165     public static string Encrypt(KeyType keyType, string plainText)
166     {
167         return Encrypt(keyType, plainText, String.Empty,
168                         String.Empty);
169     }
170 
171     /// <summary>
172     /// Calls DPAPI CryptProtectData function to encrypt a plaintext
173     /// string value. This function does not specify data description.
174     /// </summary>
175     /// <param name="keyType">
176     /// Defines type of encryption key to use. When user key is
177     /// specified, any application running under the same user account
178     /// as the one making this call, will be able to decrypt data.
179     /// Machine key will allow any application running on the same
180     /// computer where data were encrypted to perform decryption.
181     /// Note: If optional entropy is specifed, it will be required
182     /// for decryption.
183     /// </param>
184     /// <param name="plainText">
185     /// Plaintext data to be encrypted.
186     /// </param>
187     /// <param name="entropy">
188     /// Optional entropy which - if specified - will be required to
189     /// perform decryption.
190     /// </param>
191     /// <returns>
192     /// Encrypted value in a base64-encoded format.
193     /// </returns>
194     public static string Encrypt(KeyType keyType,
195                                  string  plainText,
196                                  string  entropy)
197     {
198         return Encrypt(keyType, plainText, entropy, String.Empty);
199     }
200 
201     /// <summary>
202     /// Calls DPAPI CryptProtectData function to encrypt a plaintext
203     /// string value.
204     /// </summary>
205     /// <param name="keyType">
206     /// Defines type of encryption key to use. When user key is
207     /// specified, any application running under the same user account
208     /// as the one making this call, will be able to decrypt data.
209     /// Machine key will allow any application running on the same
210     /// computer where data were encrypted to perform decryption.
211     /// Note: If optional entropy is specifed, it will be required
212     /// for decryption.
213     /// </param>
214     /// <param name="plainText">
215     /// Plaintext data to be encrypted.
216     /// </param>
217     /// <param name="entropy">
218     /// Optional entropy which - if specified - will be required to
219     /// perform decryption.
220     /// </param>
221     /// <param name="description">
222     /// Optional description of data to be encrypted. If this value is
223     /// specified, it will be stored along with encrypted data and
224     /// returned as a separate value during decryption.
225     /// </param>
226     /// <returns>
227     /// Encrypted value in a base64-encoded format.
228     /// </returns>
229     public static string Encrypt(KeyType keyType,
230                                  string  plainText,
231                                  string  entropy,
232                                  string  description)
233     {
234         // Make sure that parameters are valid.
235         if (plainText == null) plainText = String.Empty;
236         if (entropy   == null) entropy   = String.Empty;
237 
238         // Call encryption routine and convert returned bytes into
239         // a base64-encoded value.
240         return Convert.ToBase64String(
241                 Encrypt(keyType,
242                         Encoding.UTF8.GetBytes(plainText),
243                         Encoding.UTF8.GetBytes(entropy),
244                         description));
245     }
246 
247     /// <summary>
248     /// Calls DPAPI CryptProtectData function to encrypt an array of
249     /// plaintext bytes.
250     /// </summary>
251     /// <param name="keyType">
252     /// Defines type of encryption key to use. When user key is
253     /// specified, any application running under the same user account
254     /// as the one making this call, will be able to decrypt data.
255     /// Machine key will allow any application running on the same
256     /// computer where data were encrypted to perform decryption.
257     /// Note: If optional entropy is specifed, it will be required
258     /// for decryption.
259     /// </param>
260     /// <param name="plainTextBytes">
261     /// Plaintext data to be encrypted.
262     /// </param>
263     /// <param name="entropyBytes">
264     /// Optional entropy which - if specified - will be required to
265     /// perform decryption.
266     /// </param>
267     /// <param name="description">
268     /// Optional description of data to be encrypted. If this value is
269     /// specified, it will be stored along with encrypted data and
270     /// returned as a separate value during decryption.
271     /// </param>
272     /// <returns>
273     /// Encrypted value.
274     /// </returns>
275     public static byte[] Encrypt(KeyType keyType,
276                                  byte[]  plainTextBytes,
277                                  byte[]  entropyBytes,
278                                  string  description)
279     {
280         // Make sure that parameters are valid.
281         if (plainTextBytes == null) plainTextBytes = new byte[0];
282         if (entropyBytes   == null) entropyBytes   = new byte[0];
283         if (description    == null) description    = String.Empty;
284 
285         // Create BLOBs to hold data.
286         DATA_BLOB plainTextBlob  = new DATA_BLOB();
287         DATA_BLOB cipherTextBlob = new DATA_BLOB();
288         DATA_BLOB entropyBlob    = new DATA_BLOB();
289 
290         // We only need prompt structure because it is a required
291         // parameter.
292         CRYPTPROTECT_PROMPTSTRUCT prompt =
293                                   new CRYPTPROTECT_PROMPTSTRUCT();
294         InitPrompt(ref prompt);
295 
296         try
297         {
298             // Convert plaintext bytes into a BLOB structure.
299             try
300             {
301                 InitBLOB(plainTextBytes, ref plainTextBlob);
302             }
303             catch (Exception ex)
304             {
305                 throw new Exception(
306                     "Cannot initialize plaintext BLOB.", ex);
307             }
308 
309             // Convert entropy bytes into a BLOB structure.
310             try
311             {
312                 InitBLOB(entropyBytes, ref entropyBlob);
313             }
314             catch (Exception ex)
315             {
316                 throw new Exception(
317                     "Cannot initialize entropy BLOB.", ex);
318             }
319 
320             // Disable any types of UI.
321             int flags = CRYPTPROTECT_UI_FORBIDDEN;
322 
323             // When using machine-specific key, set up machine flag.
324             if (keyType == KeyType.MachineKey)
325                 flags |= CRYPTPROTECT_LOCAL_MACHINE;
326 
327             // Call DPAPI to encrypt data.
328             bool success = CryptProtectData(ref plainTextBlob,
329                                                 description,
330                                             ref entropyBlob,
331                                                 IntPtr.Zero,
332                                             ref prompt,
333                                                 flags,
334                                             ref cipherTextBlob);
335             // Check the result.
336             if (!success)
337             {
338                 // If operation failed, retrieve last Win32 error.
339                 int errCode = Marshal.GetLastWin32Error();
340 
341                 // Win32Exception will contain error message corresponding
342                 // to the Windows error code.
343                 throw new Exception(
344                     "CryptProtectData failed.", new Win32Exception(errCode));
345             }
346 
347             // Allocate memory to hold ciphertext.
348             byte[] cipherTextBytes = new byte[cipherTextBlob.cbData];
349 
350             // Copy ciphertext from the BLOB to a byte array.
351             Marshal.Copy(cipherTextBlob.pbData,
352                             cipherTextBytes,
353                             0,
354                             cipherTextBlob.cbData);
355 
356             // Return the result.
357             return cipherTextBytes;
358         }
359         catch (Exception ex)
360         {
361             throw new Exception("DPAPI was unable to encrypt data.", ex);
362         }
363         // Free all memory allocated for BLOBs.
364         finally
365         {
366             if (plainTextBlob.pbData != IntPtr.Zero)
367                 Marshal.FreeHGlobal(plainTextBlob.pbData);
368 
369             if (cipherTextBlob.pbData != IntPtr.Zero)
370                 Marshal.FreeHGlobal(cipherTextBlob.pbData);
371 
372             if (entropyBlob.pbData != IntPtr.Zero)
373                 Marshal.FreeHGlobal(entropyBlob.pbData);
374         }
375     }
376 
377     /// <summary>
378     /// Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
379     /// This function does not use additional entropy and does not
380     /// return data description.
381     /// </summary>
382     /// <param name="cipherText">
383     /// Encrypted data formatted as a base64-encoded string.
384     /// </param>
385     /// <returns>
386     /// Decrypted data returned as a UTF-8 string.
387     /// </returns>
388     /// <remarks>
389     /// When decrypting data, it is not necessary to specify which
390     /// type of encryption key to use: user-specific or
391     /// machine-specific; DPAPI will figure it out by looking at
392     /// the signature of encrypted data.
393     /// </remarks>
394     public static string Decrypt(string cipherText)
395     {
396         string description;
397 
398         return Decrypt(cipherText, String.Empty, out description);
399     }
400 
401     /// <summary>
402     /// Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
403     /// This function does not use additional entropy.
404     /// </summary>
405     /// <param name="cipherText">
406     /// Encrypted data formatted as a base64-encoded string.
407     /// </param>
408     /// <param name="description">
409     /// Returned description of data specified during encryption.
410     /// </param>
411     /// <returns>
412     /// Decrypted data returned as a UTF-8 string.
413     /// </returns>
414     /// <remarks>
415     /// When decrypting data, it is not necessary to specify which
416     /// type of encryption key to use: user-specific or
417     /// machine-specific; DPAPI will figure it out by looking at
418     /// the signature of encrypted data.
419     /// </remarks>
420     public static string Decrypt(    string cipherText,
421                                  out string description)
422     {
423         return Decrypt(cipherText, String.Empty, out description);
424     }
425 
426     /// <summary>
427     /// Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
428     /// </summary>
429     /// <param name="cipherText">
430     /// Encrypted data formatted as a base64-encoded string.
431     /// </param>
432     /// <param name="entropy">
433     /// Optional entropy, which is required if it was specified during
434     /// encryption.
435     /// </param>
436     /// <param name="description">
437     /// Returned description of data specified during encryption.
438     /// </param>
439     /// <returns>
440     /// Decrypted data returned as a UTF-8 string.
441     /// </returns>
442     /// <remarks>
443     /// When decrypting data, it is not necessary to specify which
444     /// type of encryption key to use: user-specific or
445     /// machine-specific; DPAPI will figure it out by looking at
446     /// the signature of encrypted data.
447     /// </remarks>
448     public static string Decrypt(    string cipherText,
449                                      string entropy,
450                                  out string description)
451     {
452         // Make sure that parameters are valid.
453         if (entropy == null) entropy = String.Empty;
454 
455         return Encoding.UTF8.GetString(
456                     Decrypt(    Convert.FromBase64String(cipherText),
457                                 Encoding.UTF8.GetBytes(entropy),
458                             out description));
459     }
460 
461     /// <summary>
462     /// Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
463     /// </summary>
464     /// <param name="cipherTextBytes">
465     /// Encrypted data.
466     /// </param>
467     /// <param name="entropyBytes">
468     /// Optional entropy, which is required if it was specified during
469     /// encryption.
470     /// </param>
471     /// <param name="description">
472     /// Returned description of data specified during encryption.
473     /// </param>
474     /// <returns>
475     /// Decrypted data bytes.
476     /// </returns>
477     /// <remarks>
478     /// When decrypting data, it is not necessary to specify which
479     /// type of encryption key to use: user-specific or
480     /// machine-specific; DPAPI will figure it out by looking at
481     /// the signature of encrypted data.
482     /// </remarks>
483     public static byte[] Decrypt(    byte[] cipherTextBytes,
484                                      byte[] entropyBytes,
485                                  out string description)
486     {
487         // Create BLOBs to hold data.
488         DATA_BLOB plainTextBlob  = new DATA_BLOB();
489         DATA_BLOB cipherTextBlob = new DATA_BLOB();
490         DATA_BLOB entropyBlob    = new DATA_BLOB();
491 
492         // We only need prompt structure because it is a required
493         // parameter.
494         CRYPTPROTECT_PROMPTSTRUCT prompt =
495                                   new CRYPTPROTECT_PROMPTSTRUCT();
496         InitPrompt(ref prompt);
497 
498         // Initialize description string.
499         description = String.Empty;
500 
501         try
502         {
503             // Convert ciphertext bytes into a BLOB structure.
504             try
505             {
506                 InitBLOB(cipherTextBytes, ref cipherTextBlob);
507             }
508             catch (Exception ex)
509             {
510                 throw new Exception(
511                     "Cannot initialize ciphertext BLOB.", ex);
512             }
513 
514             // Convert entropy bytes into a BLOB structure.
515             try
516             {
517                 InitBLOB(entropyBytes, ref entropyBlob);
518             }
519             catch (Exception ex)
520             {
521                 throw new Exception(
522                     "Cannot initialize entropy BLOB.", ex);
523             }
524 
525             // Disable any types of UI. CryptUnprotectData does not
526             // mention CRYPTPROTECT_LOCAL_MACHINE flag in the list of
527             // supported flags so we will not set it up.
528             int flags = CRYPTPROTECT_UI_FORBIDDEN;
529 
530             // Call DPAPI to decrypt data.
531             bool success = CryptUnprotectData(ref cipherTextBlob,
532                                               ref description,
533                                               ref entropyBlob,
534                                                   IntPtr.Zero,
535                                               ref prompt,
536                                                   flags,
537                                               ref plainTextBlob);
538 
539             // Check the result.
540             if (!success)
541             {
542                 // If operation failed, retrieve last Win32 error.
543                 int errCode = Marshal.GetLastWin32Error();
544 
545                 // Win32Exception will contain error message corresponding
546                 // to the Windows error code.
547                 throw new Exception(
548                     "CryptUnprotectData failed.", new Win32Exception(errCode));
549             }
550 
551             // Allocate memory to hold plaintext.
552             byte[] plainTextBytes = new byte[plainTextBlob.cbData];
553 
554             // Copy ciphertext from the BLOB to a byte array.
555             Marshal.Copy(plainTextBlob.pbData,
556                          plainTextBytes,
557                          0,
558                          plainTextBlob.cbData);
559 
560             // Return the result.
561             return plainTextBytes;
562         }
563         catch (Exception ex)
564         {
565             throw new Exception("DPAPI was unable to decrypt data.", ex);
566         }
567         // Free all memory allocated for BLOBs.
568         finally
569         {
570             if (plainTextBlob.pbData != IntPtr.Zero)
571                 Marshal.FreeHGlobal(plainTextBlob.pbData);
572 
573             if (cipherTextBlob.pbData != IntPtr.Zero)
574                 Marshal.FreeHGlobal(cipherTextBlob.pbData);
575 
576             if (entropyBlob.pbData != IntPtr.Zero)
577                 Marshal.FreeHGlobal(entropyBlob.pbData);
578         }
579     }
580 }
581 
582 /// <summary>
583 /// Demonstrates the use of DPAPI functions to encrypt and decrypt data.
584 /// </summary>
585 public class DPAPITest
586 {
587     /// <summary>
588     /// The main entry point for the application.
589     /// </summary>
590     [STAThread]
591     static void Main(string[] args)
592     {
593         try
594         {
595             string text    = "Hello, world!";
596             string entropy = null;
597             string description;
598 
599             Console.WriteLine("Plaintext: {0}\r\n", text);
600 
601             // Call DPAPI to encrypt data with user-specific key.
602             string encrypted = DPAPI.Encrypt( DPAPI.KeyType.UserKey,
603                                               text,
604                                               entropy,
605                                               "My Data");
606             Console.WriteLine("Encrypted: {0}\r\n", encrypted);
607 
608             // Call DPAPI to decrypt data.
609             string decrypted = DPAPI.Decrypt(   encrypted,
610                                                 entropy,
611                                             out description);
612             Console.WriteLine("Decrypted: {0} <<<{1}>>>\r\n",
613                                decrypted, description);
614         }
615         catch (Exception ex)
616         {
617             while (ex != null)
618             {
619                 Console.WriteLine(ex.Message);
620                 ex = ex.InnerException;
621             }
622         }
623     }
624 }
625 //
626 // END OF FILE
627 ///////////////////////////////////////////////////////////////////////////////
原文地址:https://www.cnblogs.com/jobs2/p/2807072.html