ManagedProtection.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. //
  2. // ManagedProtection.cs -
  3. // Protect (encrypt) data without (user involved) key management
  4. //
  5. // Author:
  6. // Sebastien Pouliot <[email protected]>
  7. //
  8. // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.IO;
  31. using System.Runtime.InteropServices;
  32. using System.Security;
  33. using System.Security.Cryptography;
  34. using System.Security.Permissions;
  35. namespace Mono.Security.Cryptography {
  36. // Managed Protection Implementation
  37. //
  38. // Features
  39. // * Separate RSA 1536 bits keypairs for each user and the computer
  40. // * AES 128 bits encryption (separate key for each data protected)
  41. // * SHA256 digest to ensure integrity
  42. #if !NET_2_0
  43. internal enum DataProtectionScope {
  44. CurrentUser,
  45. LocalMachine
  46. }
  47. #endif
  48. internal static class ManagedProtection {
  49. // FIXME [KeyContainerPermission (SecurityAction.Assert, KeyContainerName = "DAPI",
  50. // Flags = KeyContainerPermissionFlags.Open | KeyContainerPermissionFlags.Create)]
  51. public static byte[] Protect (byte[] userData, byte[] optionalEntropy, DataProtectionScope scope)
  52. {
  53. if (userData == null)
  54. throw new ArgumentNullException ("userData");
  55. Rijndael aes = Rijndael.Create ();
  56. aes.KeySize = 128;
  57. byte[] encdata = null;
  58. using (MemoryStream ms = new MemoryStream ()) {
  59. ICryptoTransform t = aes.CreateEncryptor ();
  60. using (CryptoStream cs = new CryptoStream (ms, t, CryptoStreamMode.Write)) {
  61. cs.Write (userData, 0, userData.Length);
  62. cs.Close ();
  63. encdata = ms.ToArray ();
  64. }
  65. }
  66. byte[] key = null;
  67. byte[] iv = null;
  68. byte[] secret = null;
  69. byte[] header = null;
  70. SHA256 hash = SHA256.Create ();
  71. try {
  72. key = aes.Key;
  73. iv = aes.IV;
  74. secret = new byte[1 + 1 + 16 + 1 + 16 + 1 + 32];
  75. byte[] digest = hash.ComputeHash (userData);
  76. if ((optionalEntropy != null) && (optionalEntropy.Length > 0)) {
  77. // the same optionalEntropy will be required to get the data back
  78. byte[] mask = hash.ComputeHash (optionalEntropy);
  79. for (int i = 0; i < 16; i++) {
  80. key[i] ^= mask[i];
  81. iv[i] ^= mask[i + 16];
  82. }
  83. secret[0] = 2; // entropy
  84. } else {
  85. secret[0] = 1; // without entropy
  86. }
  87. secret[1] = 16; // key size
  88. Buffer.BlockCopy (key, 0, secret, 2, 16);
  89. secret[18] = 16; // iv size
  90. Buffer.BlockCopy (iv, 0, secret, 19, 16);
  91. secret[35] = 32; // digest size
  92. Buffer.BlockCopy (digest, 0, secret, 36, 32);
  93. RSAOAEPKeyExchangeFormatter formatter = new RSAOAEPKeyExchangeFormatter (GetKey (scope));
  94. header = formatter.CreateKeyExchange (secret);
  95. }
  96. finally {
  97. if (key != null) {
  98. Array.Clear (key, 0, key.Length);
  99. key = null;
  100. }
  101. if (secret != null) {
  102. Array.Clear (secret, 0, secret.Length);
  103. secret = null;
  104. }
  105. if (iv != null) {
  106. Array.Clear (iv, 0, iv.Length);
  107. iv = null;
  108. }
  109. aes.Clear ();
  110. hash.Clear ();
  111. }
  112. byte[] result = new byte[header.Length + encdata.Length];
  113. Buffer.BlockCopy (header, 0, result, 0, header.Length);
  114. Buffer.BlockCopy (encdata, 0, result, header.Length, encdata.Length);
  115. return result;
  116. }
  117. // FIXME [KeyContainerPermission (SecurityAction.Assert, KeyContainerName = "DAPI",
  118. // Flags = KeyContainerPermissionFlags.Open | KeyContainerPermissionFlags.Decrypt)]
  119. public static byte[] Unprotect (byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
  120. {
  121. if (encryptedData == null)
  122. throw new ArgumentNullException ("encryptedData");
  123. byte[] decdata = null;
  124. Rijndael aes = Rijndael.Create ();
  125. RSA rsa = GetKey (scope);
  126. int headerSize = (rsa.KeySize >> 3);
  127. bool valid1 = (encryptedData.Length >= headerSize);
  128. if (!valid1)
  129. headerSize = encryptedData.Length;
  130. byte[] header = new byte[headerSize];
  131. Buffer.BlockCopy (encryptedData, 0, header, 0, headerSize);
  132. byte[] secret = null;
  133. byte[] key = null;
  134. byte[] iv = null;
  135. bool valid2 = false;
  136. bool valid3 = false;
  137. bool valid4 = false;
  138. SHA256 hash = SHA256.Create ();
  139. try {
  140. try {
  141. RSAOAEPKeyExchangeDeformatter deformatter = new RSAOAEPKeyExchangeDeformatter (rsa);
  142. secret = deformatter.DecryptKeyExchange (header);
  143. valid2 = (secret.Length == 68);
  144. }
  145. catch {
  146. valid2 = false;
  147. }
  148. if (!valid2)
  149. secret = new byte[68];
  150. // known values for structure (version 1 or 2)
  151. valid3 = ((secret[1] == 16) && (secret[18] == 16) && (secret[35] == 32));
  152. key = new byte [16];
  153. Buffer.BlockCopy (secret, 2, key, 0, 16);
  154. iv = new byte [16];
  155. Buffer.BlockCopy (secret, 19, iv, 0, 16);
  156. if ((optionalEntropy != null) && (optionalEntropy.Length > 0)) {
  157. // the decrypted data won't be valid if the entropy isn't
  158. // the same as the one used to protect (encrypt) it
  159. byte[] mask = hash.ComputeHash (optionalEntropy);
  160. for (int i = 0; i < 16; i++) {
  161. key[i] ^= mask[i];
  162. iv[i] ^= mask[i + 16];
  163. }
  164. valid3 &= (secret[0] == 2); // with entropy
  165. } else {
  166. valid3 &= (secret[0] == 1); // without entropy
  167. }
  168. using (MemoryStream ms = new MemoryStream ()) {
  169. ICryptoTransform t = aes.CreateDecryptor (key, iv);
  170. using (CryptoStream cs = new CryptoStream (ms, t, CryptoStreamMode.Write)) {
  171. try {
  172. cs.Write (encryptedData, headerSize, encryptedData.Length - headerSize);
  173. cs.Close ();
  174. }
  175. catch {
  176. // whatever, we keep going
  177. }
  178. }
  179. decdata = ms.ToArray ();
  180. }
  181. byte[] digest = hash.ComputeHash (decdata);
  182. valid4 = true;
  183. for (int i=0; i < 32; i++) {
  184. if (digest [i] != secret [36 + i])
  185. valid4 = false;
  186. }
  187. }
  188. finally {
  189. if (key != null) {
  190. Array.Clear (key, 0, key.Length);
  191. key = null;
  192. }
  193. if (secret != null) {
  194. Array.Clear (secret, 0, secret.Length);
  195. secret = null;
  196. }
  197. if (iv != null) {
  198. Array.Clear (iv, 0, iv.Length);
  199. iv = null;
  200. }
  201. aes.Clear ();
  202. hash.Clear ();
  203. }
  204. // single point of error (also limits timing informations)
  205. if (!valid1 || !valid2 || !valid3 || !valid4) {
  206. if (decdata != null) {
  207. Array.Clear (decdata, 0, decdata.Length);
  208. decdata = null;
  209. }
  210. throw new CryptographicException (Locale.GetText ("Invalid data."));
  211. }
  212. return decdata;
  213. }
  214. // private stuff
  215. private static RSA user;
  216. private static RSA machine;
  217. private static RSA GetKey (DataProtectionScope scope)
  218. {
  219. switch (scope) {
  220. case DataProtectionScope.CurrentUser:
  221. if (user == null) {
  222. CspParameters csp = new CspParameters ();
  223. csp.KeyContainerName = "DAPI";
  224. user = new RSACryptoServiceProvider (1536, csp);
  225. }
  226. return user;
  227. case DataProtectionScope.LocalMachine:
  228. if (machine == null) {
  229. CspParameters csp = new CspParameters ();
  230. csp.KeyContainerName = "DAPI";
  231. csp.Flags = CspProviderFlags.UseMachineKeyStore;
  232. machine = new RSACryptoServiceProvider (1536, csp);
  233. }
  234. return machine;
  235. default:
  236. throw new CryptographicException (Locale.GetText ("Invalid scope."));
  237. }
  238. }
  239. }
  240. }