123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Security.Cryptography;
- using System.IO;
- namespace FastReport.Utils
- {
- /// <summary>
- /// Contains methods used to crypt/decrypt a data.
- /// </summary>
- public static class Crypter
- {
- private static string FDefaultPassword = typeof(Crypter).FullName;
-
- /// <summary>
- /// Sets the password that is used to crypt connection strings stored in a report.
- /// </summary>
- /// <remarks>
- /// See the <see cref="FastReport.Data.DataConnectionBase.ConnectionString"/> property for more details.
- /// </remarks>
- public static string DefaultPassword
- {
- set { FDefaultPassword = value; }
- }
-
- /// <summary>
- /// Crypts a stream using specified password.
- /// </summary>
- /// <param name="dest">The destination stream that will receive the crypted data.</param>
- /// <param name="password">The password.</param>
- /// <returns>The stream that you need to write to.</returns>
- /// <remarks>
- /// Pass the stream you need to write to, to the <b>dest</b> parameter. Write your data to the
- /// stream that this method returns. When you close this stream, the <b>dest</b> stream will be
- /// closed too and contains the crypted data.
- /// </remarks>
- public static Stream Encrypt(Stream dest, string password)
- {
- ICryptoTransform encryptor = null;
- #if DOTNET_4
- using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt")))
- #else
- PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt"));
- #endif
- {
- RijndaelManaged rm = new RijndaelManaged();
- rm.Padding = PaddingMode.ISO10126;
- encryptor = rm.CreateEncryptor(pdb.GetBytes(16), pdb.GetBytes(16));
- }
- // write "rij" signature
- dest.Write(new byte[] { 114, 105, 106 }, 0, 3);
- return new CryptoStream(dest, encryptor, CryptoStreamMode.Write);
- }
- /// <summary>
- /// Decrypts a stream using specified password.
- /// </summary>
- /// <param name="source">Stream that contains crypted data.</param>
- /// <param name="password">The password.</param>
- /// <returns>The stream that contains decrypted data.</returns>
- /// <remarks>
- /// You should read from the stream that this method returns.
- /// </remarks>
- public static Stream Decrypt(Stream source, string password)
- {
- ICryptoTransform decryptor = null;
- #if DOTNET_4
- using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt")))
- #else
- PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt"));
- #endif
- {
- RijndaelManaged rm = new RijndaelManaged();
- rm.Padding = PaddingMode.ISO10126;
- decryptor = rm.CreateDecryptor(pdb.GetBytes(16), pdb.GetBytes(16));
- }
- var encrypted = IsStreamEncryptedPrivate(source);
- if (encrypted)
- return new CryptoStream(source, decryptor, CryptoStreamMode.Read);
- source.Position -= 3;
- return null;
- }
-
- /// <summary>
- /// Checks if the stream contains a crypt signature.
- /// </summary>
- /// <param name="stream">Stream to check.</param>
- /// <returns><b>true</b> if stream is crypted.</returns>
- public static bool IsStreamEncrypted(Stream stream)
- {
- var result = IsStreamEncryptedPrivate(stream);
- stream.Position -= 3;
- return result;
- }
- private static bool IsStreamEncryptedPrivate(Stream stream)
- {
- // check "rij" signature
- int byte1 = stream.ReadByte();
- int byte2 = stream.ReadByte();
- int byte3 = stream.ReadByte();
- return byte1 == 114 && byte2 == 105 && byte3 == 106;
- }
- /// <summary>
- /// Encrypts the string using the default password.
- /// </summary>
- /// <param name="data">String to encrypt.</param>
- /// <returns>The encrypted string.</returns>
- /// <remarks>
- /// The password used to encrypt a string can be set via <see cref="DefaultPassword"/> property.
- /// You also may use the <see cref="EncryptString(string, string)"/> method if you want to
- /// specify another password.
- /// </remarks>
- public static string EncryptString(string data)
- {
- return EncryptString(data, FDefaultPassword);
- }
- /// <summary>
- /// Encrypts the string using specified password.
- /// </summary>
- /// <param name="data">String to encrypt.</param>
- /// <param name="password">The password.</param>
- /// <returns>The encrypted string.</returns>
- public static string EncryptString(string data, string password)
- {
- if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(password))
- return data;
- using (MemoryStream stream = new MemoryStream())
- {
- using (Stream cryptedStream = Encrypt(stream, password))
- {
- byte[] bytes = Encoding.UTF8.GetBytes(data);
- cryptedStream.Write(bytes, 0, bytes.Length);
- }
-
- return "rij" + Convert.ToBase64String(stream.ToArray());
- }
- }
- /// <summary>
- /// Decrypts the string using the default password.
- /// </summary>
- /// <param name="data">String to decrypt.</param>
- /// <returns>The decrypted string.</returns>
- /// <remarks>
- /// The password used to decrypt a string can be set via <see cref="DefaultPassword"/> property.
- /// You also may use the <see cref="DecryptString(string, string)"/> method if you want to
- /// specify another password.
- /// </remarks>
- public static string DecryptString(string data)
- {
- return DecryptString(data, FDefaultPassword);
- }
- /// <summary>
- /// Decrypts the string using specified password.
- /// </summary>
- /// <param name="data">String to decrypt.</param>
- /// <param name="password">The password.</param>
- /// <returns>The decrypted string.</returns>
- public static string DecryptString(string data, string password)
- {
- if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(password) || !data.StartsWith("rij"))
- return data;
- data = data.Substring(3);
- using (Stream stream = Converter.FromString(typeof(Stream), data) as Stream)
- {
- using (Stream decryptedStream = Decrypt(stream, password))
- {
- byte[] bytes = new byte[data.Length];
- int bytesRead = 0;
- while (bytesRead < bytes.Length)
- {
- int n = decryptedStream.Read(bytes, bytesRead, bytes.Length - bytesRead);
- if (n == 0)
- break;
- bytesRead += n;
- }
- return Encoding.UTF8.GetString(bytes, 0, bytesRead);
- }
- }
- }
- /// <summary>
- /// Computes hash of specified stream. Initial position in stream will be saved.
- /// </summary>
- /// <param name="input">Initial stream</param>
- /// <returns></returns>
- public static string ComputeHash(Stream input)
- {
- byte[] buff = new byte[input.Length];
- input.Read(buff, 0, buff.Length);
- return ComputeHash(buff);
- }
- /// <summary>
- /// Computes hash of specified array.
- /// </summary>
- /// <param name="input">Initial array</param>
- /// <returns></returns>
- public static string ComputeHash(byte[] input)
- {
- byte[] hash = new Murmur3().ComputeHash(input);
- return BitConverter.ToString(hash).Replace("-", String.Empty);
- }
- /// <summary>
- /// Computes hash of specified array.
- /// </summary>
- /// <param name="input">Initial array</param>
- /// <returns></returns>
- public static string ComputeHash(string input)
- {
- return ComputeHash(Encoding.UTF8.GetBytes(input));
- }
- }
- /// <summary>
- /// MurmurHash is a non-cryptographic hash function suitable for general hash-based lookup.
- /// It was created by Austin Appleby in 2008 and is currently hosted on Github along with its test suite named 'SMHasher'.
- /// It also exists in a number of variants, all of which have been released into the public domain.
- /// The name comes from two basic operations, multiply (MU) and rotate (R), used in its inner loop.
- /// https://en.wikipedia.org/wiki/MurmurHash
- /// Implementation of Murmur3 Hash by Adam Horvath
- /// http://blog.teamleadnet.com/2012/08/murmurhash3-ultra-fast-hash-algorithm.html
- /// </summary>
- [CLSCompliantAttribute(true)]
- public class Murmur3
- {
- // 128 bit output, 64 bit platform version
- /// <summary>
- /// READ_SIZE
- /// </summary>
- [CLSCompliantAttribute(false)]
- public static ulong READ_SIZE = 16;
- private const ulong C1 = 0x87c37b91114253d5L;
- private const ulong C2 = 0x4cf5ad432745937fL;
- private ulong length;
- private readonly uint seed = 0; // if want to start with a seed, create a constructor
- ulong h1;
- ulong h2;
- private void MixBody(ulong k1, ulong k2)
- {
- unchecked
- {
- h1 ^= MixKey1(k1);
- h1 = (h1 << 27) | (h1 >> 37);
- h1 += h2;
- h1 = h1* 5 + 0x52dce729;
- h2 ^= MixKey2(k2);
- h2 = (h2 << 31) | (h2 >> 33);
- h2 += h1;
- h2 = h2* 5 + 0x38495ab5;
- }
- }
- private static ulong MixKey1(ulong k1)
- {
- unchecked
- {
- k1 *= C1;
- k1 = (k1 << 31) | (k1 >> 33);
- k1 *= C2;
- }
- return k1;
- }
- private static ulong MixKey2(ulong k2)
- {
- unchecked
- {
- k2 *= C2;
- k2 = (k2 << 33) | (k2 >> 31);
- k2 *= C1;
- }
- return k2;
- }
- private static ulong MixFinal(ulong k)
- {
- unchecked
- {
- // avalanche bits
- k ^= k >> 33;
- k *= 0xff51afd7ed558ccdL;
- k ^= k >> 33;
- k *= 0xc4ceb9fe1a85ec53L;
- k ^= k >> 33;
- }
- return k;
- }
- /// <summary>
- /// ComputeHash function
- /// </summary>
- /// <param name="bb"></param>
- /// <returns></returns>
- public byte[] ComputeHash(byte[] bb)
- {
- ProcessBytes(bb);
- return Hash;
- }
- private void ProcessBytes(byte[] bb)
- {
- h1 = seed;
- this.length = 0L;
- int pos = 0;
- int npos;
- ulong remaining = (ulong)bb.Length;
- // read 128 bits, 16 bytes, 2 longs in eacy cycle
- while (remaining >= READ_SIZE) unchecked
- {
- npos = pos;
- ulong k1 = (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
- pos += 8;
- npos = pos;
- ulong k2 = (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
- pos += 8;
- length += READ_SIZE;
- remaining -= READ_SIZE;
- MixBody(k1, k2);
- }
- // if the input MOD 16 != 0
- if (remaining > 0)
- ProcessBytesRemaining(bb, remaining, pos);
- }
- private void ProcessBytesRemaining(byte[] bb, ulong remaining, int pos)
- {
- ulong k1 = 0;
- ulong k2 = 0;
- length += remaining;
- // little endian (x86) processing
- unchecked
- {
- switch (remaining)
- {
- case 15:
- k2 ^= (ulong)bb[pos + 14] << 48; // fall through
- goto case 14;
- case 14:
- k2 ^= (ulong)bb[pos + 13] << 40; // fall through
- goto case 13;
- case 13:
- k2 ^= (ulong)bb[pos + 12] << 32; // fall through
- goto case 12;
- case 12:
- k2 ^= (ulong)bb[pos + 11] << 24; // fall through
- goto case 11;
- case 11:
- k2 ^= (ulong)bb[pos + 10] << 16; // fall through
- goto case 10;
- case 10:
- k2 ^= (ulong)bb[pos + 9] << 8; // fall through
- goto case 9;
- case 9:
- k2 ^= (ulong)bb[pos + 8]; // fall through
- goto case 8;
- case 8:
- int npos = pos;
- k1 ^= (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
- break;
- case 7:
- k1 ^= (ulong)bb[pos + 6] << 48; // fall through
- goto case 6;
- case 6:
- k1 ^= (ulong)bb[pos + 5] << 40; // fall through
- goto case 5;
- case 5:
- k1 ^= (ulong)bb[pos + 4] << 32; // fall through
- goto case 4;
- case 4:
- k1 ^= (ulong)bb[pos + 3] << 24; // fall through
- goto case 3;
- case 3:
- k1 ^= (ulong)bb[pos + 2] << 16; // fall through
- goto case 2;
- case 2:
- k1 ^= (ulong)bb[pos + 1] << 8; // fall through
- goto case 1;
- case 1:
- k1 ^= (ulong)bb[pos]; // fall through
- break;
- default:
- throw new Exception("Something went wrong with remaining bytes calculation.");
- }
- h1 ^= MixKey1(k1);
- h2 ^= MixKey2(k2);
- }
- }
- /// <summary>
- /// Gets the Hash
- /// </summary>
- public byte[] Hash
- {
- get
- {
- unchecked
- {
- h1 ^= length;
- h2 ^= length;
- h1 += h2;
- h2 += h1;
- h1 = Murmur3.MixFinal(h1);
- h2 = Murmur3.MixFinal(h2);
- h1 += h2;
- h2 += h1;
- }
- byte[] hash = new byte[Murmur3.READ_SIZE];
- Array.Copy(BitConverter.GetBytes(h1), 0, hash, 0, 8);
- Array.Copy(BitConverter.GetBytes(h2), 0, hash, 8, 8);
- return hash;
- }
- }
- }
- }
|