Crypter.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Security.Cryptography;
  5. using System.IO;
  6. namespace FastReport.Utils
  7. {
  8. /// <summary>
  9. /// Contains methods used to crypt/decrypt a data.
  10. /// </summary>
  11. public static class Crypter
  12. {
  13. private static string FDefaultPassword = typeof(Crypter).FullName;
  14. /// <summary>
  15. /// Sets the password that is used to crypt connection strings stored in a report.
  16. /// </summary>
  17. /// <remarks>
  18. /// See the <see cref="FastReport.Data.DataConnectionBase.ConnectionString"/> property for more details.
  19. /// </remarks>
  20. public static string DefaultPassword
  21. {
  22. set { FDefaultPassword = value; }
  23. }
  24. /// <summary>
  25. /// Crypts a stream using specified password.
  26. /// </summary>
  27. /// <param name="dest">The destination stream that will receive the crypted data.</param>
  28. /// <param name="password">The password.</param>
  29. /// <returns>The stream that you need to write to.</returns>
  30. /// <remarks>
  31. /// Pass the stream you need to write to, to the <b>dest</b> parameter. Write your data to the
  32. /// stream that this method returns. When you close this stream, the <b>dest</b> stream will be
  33. /// closed too and contains the crypted data.
  34. /// </remarks>
  35. public static Stream Encrypt(Stream dest, string password)
  36. {
  37. ICryptoTransform encryptor = null;
  38. #if DOTNET_4
  39. using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt")))
  40. #else
  41. PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt"));
  42. #endif
  43. {
  44. RijndaelManaged rm = new RijndaelManaged();
  45. rm.Padding = PaddingMode.ISO10126;
  46. encryptor = rm.CreateEncryptor(pdb.GetBytes(16), pdb.GetBytes(16));
  47. }
  48. // write "rij" signature
  49. dest.Write(new byte[] { 114, 105, 106 }, 0, 3);
  50. return new CryptoStream(dest, encryptor, CryptoStreamMode.Write);
  51. }
  52. /// <summary>
  53. /// Decrypts a stream using specified password.
  54. /// </summary>
  55. /// <param name="source">Stream that contains crypted data.</param>
  56. /// <param name="password">The password.</param>
  57. /// <returns>The stream that contains decrypted data.</returns>
  58. /// <remarks>
  59. /// You should read from the stream that this method returns.
  60. /// </remarks>
  61. public static Stream Decrypt(Stream source, string password)
  62. {
  63. ICryptoTransform decryptor = null;
  64. #if DOTNET_4
  65. using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt")))
  66. #else
  67. PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt"));
  68. #endif
  69. {
  70. RijndaelManaged rm = new RijndaelManaged();
  71. rm.Padding = PaddingMode.ISO10126;
  72. decryptor = rm.CreateDecryptor(pdb.GetBytes(16), pdb.GetBytes(16));
  73. }
  74. var encrypted = IsStreamEncryptedPrivate(source);
  75. if (encrypted)
  76. return new CryptoStream(source, decryptor, CryptoStreamMode.Read);
  77. source.Position -= 3;
  78. return null;
  79. }
  80. /// <summary>
  81. /// Checks if the stream contains a crypt signature.
  82. /// </summary>
  83. /// <param name="stream">Stream to check.</param>
  84. /// <returns><b>true</b> if stream is crypted.</returns>
  85. public static bool IsStreamEncrypted(Stream stream)
  86. {
  87. var result = IsStreamEncryptedPrivate(stream);
  88. stream.Position -= 3;
  89. return result;
  90. }
  91. private static bool IsStreamEncryptedPrivate(Stream stream)
  92. {
  93. // check "rij" signature
  94. int byte1 = stream.ReadByte();
  95. int byte2 = stream.ReadByte();
  96. int byte3 = stream.ReadByte();
  97. return byte1 == 114 && byte2 == 105 && byte3 == 106;
  98. }
  99. /// <summary>
  100. /// Encrypts the string using the default password.
  101. /// </summary>
  102. /// <param name="data">String to encrypt.</param>
  103. /// <returns>The encrypted string.</returns>
  104. /// <remarks>
  105. /// The password used to encrypt a string can be set via <see cref="DefaultPassword"/> property.
  106. /// You also may use the <see cref="EncryptString(string, string)"/> method if you want to
  107. /// specify another password.
  108. /// </remarks>
  109. public static string EncryptString(string data)
  110. {
  111. return EncryptString(data, FDefaultPassword);
  112. }
  113. /// <summary>
  114. /// Encrypts the string using specified password.
  115. /// </summary>
  116. /// <param name="data">String to encrypt.</param>
  117. /// <param name="password">The password.</param>
  118. /// <returns>The encrypted string.</returns>
  119. public static string EncryptString(string data, string password)
  120. {
  121. if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(password))
  122. return data;
  123. using (MemoryStream stream = new MemoryStream())
  124. {
  125. using (Stream cryptedStream = Encrypt(stream, password))
  126. {
  127. byte[] bytes = Encoding.UTF8.GetBytes(data);
  128. cryptedStream.Write(bytes, 0, bytes.Length);
  129. }
  130. return "rij" + Convert.ToBase64String(stream.ToArray());
  131. }
  132. }
  133. /// <summary>
  134. /// Decrypts the string using the default password.
  135. /// </summary>
  136. /// <param name="data">String to decrypt.</param>
  137. /// <returns>The decrypted string.</returns>
  138. /// <remarks>
  139. /// The password used to decrypt a string can be set via <see cref="DefaultPassword"/> property.
  140. /// You also may use the <see cref="DecryptString(string, string)"/> method if you want to
  141. /// specify another password.
  142. /// </remarks>
  143. public static string DecryptString(string data)
  144. {
  145. return DecryptString(data, FDefaultPassword);
  146. }
  147. /// <summary>
  148. /// Decrypts the string using specified password.
  149. /// </summary>
  150. /// <param name="data">String to decrypt.</param>
  151. /// <param name="password">The password.</param>
  152. /// <returns>The decrypted string.</returns>
  153. public static string DecryptString(string data, string password)
  154. {
  155. if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(password) || !data.StartsWith("rij"))
  156. return data;
  157. data = data.Substring(3);
  158. using (Stream stream = Converter.FromString(typeof(Stream), data) as Stream)
  159. {
  160. using (Stream decryptedStream = Decrypt(stream, password))
  161. {
  162. byte[] bytes = new byte[data.Length];
  163. int bytesRead = 0;
  164. while (bytesRead < bytes.Length)
  165. {
  166. int n = decryptedStream.Read(bytes, bytesRead, bytes.Length - bytesRead);
  167. if (n == 0)
  168. break;
  169. bytesRead += n;
  170. }
  171. return Encoding.UTF8.GetString(bytes, 0, bytesRead);
  172. }
  173. }
  174. }
  175. /// <summary>
  176. /// Computes hash of specified stream. Initial position in stream will be saved.
  177. /// </summary>
  178. /// <param name="input">Initial stream</param>
  179. /// <returns></returns>
  180. public static string ComputeHash(Stream input)
  181. {
  182. byte[] buff = new byte[input.Length];
  183. input.Read(buff, 0, buff.Length);
  184. return ComputeHash(buff);
  185. }
  186. /// <summary>
  187. /// Computes hash of specified array.
  188. /// </summary>
  189. /// <param name="input">Initial array</param>
  190. /// <returns></returns>
  191. public static string ComputeHash(byte[] input)
  192. {
  193. byte[] hash = new Murmur3().ComputeHash(input);
  194. return BitConverter.ToString(hash).Replace("-", String.Empty);
  195. }
  196. /// <summary>
  197. /// Computes hash of specified array.
  198. /// </summary>
  199. /// <param name="input">Initial array</param>
  200. /// <returns></returns>
  201. public static string ComputeHash(string input)
  202. {
  203. return ComputeHash(Encoding.UTF8.GetBytes(input));
  204. }
  205. }
  206. /// <summary>
  207. /// MurmurHash is a non-cryptographic hash function suitable for general hash-based lookup.
  208. /// It was created by Austin Appleby in 2008 and is currently hosted on Github along with its test suite named 'SMHasher'.
  209. /// It also exists in a number of variants, all of which have been released into the public domain.
  210. /// The name comes from two basic operations, multiply (MU) and rotate (R), used in its inner loop.
  211. /// https://en.wikipedia.org/wiki/MurmurHash
  212. /// Implementation of Murmur3 Hash by Adam Horvath
  213. /// http://blog.teamleadnet.com/2012/08/murmurhash3-ultra-fast-hash-algorithm.html
  214. /// </summary>
  215. [CLSCompliantAttribute(true)]
  216. public class Murmur3
  217. {
  218. // 128 bit output, 64 bit platform version
  219. /// <summary>
  220. /// READ_SIZE
  221. /// </summary>
  222. [CLSCompliantAttribute(false)]
  223. public static ulong READ_SIZE = 16;
  224. private const ulong C1 = 0x87c37b91114253d5L;
  225. private const ulong C2 = 0x4cf5ad432745937fL;
  226. private ulong length;
  227. private readonly uint seed = 0; // if want to start with a seed, create a constructor
  228. ulong h1;
  229. ulong h2;
  230. private void MixBody(ulong k1, ulong k2)
  231. {
  232. unchecked
  233. {
  234. h1 ^= MixKey1(k1);
  235. h1 = (h1 << 27) | (h1 >> 37);
  236. h1 += h2;
  237. h1 = h1* 5 + 0x52dce729;
  238. h2 ^= MixKey2(k2);
  239. h2 = (h2 << 31) | (h2 >> 33);
  240. h2 += h1;
  241. h2 = h2* 5 + 0x38495ab5;
  242. }
  243. }
  244. private static ulong MixKey1(ulong k1)
  245. {
  246. unchecked
  247. {
  248. k1 *= C1;
  249. k1 = (k1 << 31) | (k1 >> 33);
  250. k1 *= C2;
  251. }
  252. return k1;
  253. }
  254. private static ulong MixKey2(ulong k2)
  255. {
  256. unchecked
  257. {
  258. k2 *= C2;
  259. k2 = (k2 << 33) | (k2 >> 31);
  260. k2 *= C1;
  261. }
  262. return k2;
  263. }
  264. private static ulong MixFinal(ulong k)
  265. {
  266. unchecked
  267. {
  268. // avalanche bits
  269. k ^= k >> 33;
  270. k *= 0xff51afd7ed558ccdL;
  271. k ^= k >> 33;
  272. k *= 0xc4ceb9fe1a85ec53L;
  273. k ^= k >> 33;
  274. }
  275. return k;
  276. }
  277. /// <summary>
  278. /// ComputeHash function
  279. /// </summary>
  280. /// <param name="bb"></param>
  281. /// <returns></returns>
  282. public byte[] ComputeHash(byte[] bb)
  283. {
  284. ProcessBytes(bb);
  285. return Hash;
  286. }
  287. private void ProcessBytes(byte[] bb)
  288. {
  289. h1 = seed;
  290. this.length = 0L;
  291. int pos = 0;
  292. int npos;
  293. ulong remaining = (ulong)bb.Length;
  294. // read 128 bits, 16 bytes, 2 longs in eacy cycle
  295. while (remaining >= READ_SIZE) unchecked
  296. {
  297. npos = pos;
  298. ulong k1 = (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
  299. pos += 8;
  300. npos = pos;
  301. ulong k2 = (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
  302. pos += 8;
  303. length += READ_SIZE;
  304. remaining -= READ_SIZE;
  305. MixBody(k1, k2);
  306. }
  307. // if the input MOD 16 != 0
  308. if (remaining > 0)
  309. ProcessBytesRemaining(bb, remaining, pos);
  310. }
  311. private void ProcessBytesRemaining(byte[] bb, ulong remaining, int pos)
  312. {
  313. ulong k1 = 0;
  314. ulong k2 = 0;
  315. length += remaining;
  316. // little endian (x86) processing
  317. unchecked
  318. {
  319. switch (remaining)
  320. {
  321. case 15:
  322. k2 ^= (ulong)bb[pos + 14] << 48; // fall through
  323. goto case 14;
  324. case 14:
  325. k2 ^= (ulong)bb[pos + 13] << 40; // fall through
  326. goto case 13;
  327. case 13:
  328. k2 ^= (ulong)bb[pos + 12] << 32; // fall through
  329. goto case 12;
  330. case 12:
  331. k2 ^= (ulong)bb[pos + 11] << 24; // fall through
  332. goto case 11;
  333. case 11:
  334. k2 ^= (ulong)bb[pos + 10] << 16; // fall through
  335. goto case 10;
  336. case 10:
  337. k2 ^= (ulong)bb[pos + 9] << 8; // fall through
  338. goto case 9;
  339. case 9:
  340. k2 ^= (ulong)bb[pos + 8]; // fall through
  341. goto case 8;
  342. case 8:
  343. int npos = pos;
  344. k1 ^= (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
  345. break;
  346. case 7:
  347. k1 ^= (ulong)bb[pos + 6] << 48; // fall through
  348. goto case 6;
  349. case 6:
  350. k1 ^= (ulong)bb[pos + 5] << 40; // fall through
  351. goto case 5;
  352. case 5:
  353. k1 ^= (ulong)bb[pos + 4] << 32; // fall through
  354. goto case 4;
  355. case 4:
  356. k1 ^= (ulong)bb[pos + 3] << 24; // fall through
  357. goto case 3;
  358. case 3:
  359. k1 ^= (ulong)bb[pos + 2] << 16; // fall through
  360. goto case 2;
  361. case 2:
  362. k1 ^= (ulong)bb[pos + 1] << 8; // fall through
  363. goto case 1;
  364. case 1:
  365. k1 ^= (ulong)bb[pos]; // fall through
  366. break;
  367. default:
  368. throw new Exception("Something went wrong with remaining bytes calculation.");
  369. }
  370. h1 ^= MixKey1(k1);
  371. h2 ^= MixKey2(k2);
  372. }
  373. }
  374. /// <summary>
  375. /// Gets the Hash
  376. /// </summary>
  377. public byte[] Hash
  378. {
  379. get
  380. {
  381. unchecked
  382. {
  383. h1 ^= length;
  384. h2 ^= length;
  385. h1 += h2;
  386. h2 += h1;
  387. h1 = Murmur3.MixFinal(h1);
  388. h2 = Murmur3.MixFinal(h2);
  389. h1 += h2;
  390. h2 += h1;
  391. }
  392. byte[] hash = new byte[Murmur3.READ_SIZE];
  393. Array.Copy(BitConverter.GetBytes(h1), 0, hash, 0, 8);
  394. Array.Copy(BitConverter.GetBytes(h2), 0, hash, 8, 8);
  395. return hash;
  396. }
  397. }
  398. }
  399. }