fork download
  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. ///////////////////////////////////////////////////////////////////////////////
Success #stdin #stdout 0.04s 27640KB
stdin
Standard input is empty
stdout
Plaintext: Hello, world!

DPAPI was unable to encrypt data.
crypt32.dll