• Source
    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. ///////////////////////////////////////////////////////////////////////////////