597 lines
20 KiB
C#
597 lines
20 KiB
C#
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace System.Security.Cryptography
|
|
{
|
|
/// <summary>
|
|
/// Provides cryptographic functions ready-to-use.
|
|
/// </summary>
|
|
public class CryptographyHelper
|
|
{
|
|
private static readonly int saltLength = 8;
|
|
|
|
private readonly string masterKeyFile;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="CryptographyHelper"/> class.
|
|
/// </summary>
|
|
/// <param name="keyFile">The (absolute) path to the crypto key file. On <c>null</c> the file 'crypto.key' at the executing assembly location will be used.</param>
|
|
public CryptographyHelper(string keyFile = null)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(keyFile))
|
|
keyFile = "crypto.key";
|
|
|
|
if (!Path.IsPathRooted(keyFile))
|
|
{
|
|
string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
keyFile = Path.Combine(dir, keyFile);
|
|
}
|
|
masterKeyFile = keyFile;
|
|
|
|
string pw = File.Exists(masterKeyFile) ? File.ReadAllText(masterKeyFile) : null;
|
|
if (string.IsNullOrWhiteSpace(pw))
|
|
File.WriteAllText(masterKeyFile, GetRandomString(64));
|
|
}
|
|
|
|
#region Instance methods
|
|
|
|
#region AES
|
|
|
|
/// <summary>
|
|
/// Decrypts data using the AES algorithm and a password.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When the <paramref name="password"/> parameter is <c>null</c>, the key from the file (set on initialize) is used instead.
|
|
/// </remarks>
|
|
/// <param name="cipher">The encrypted data (cipher).</param>
|
|
/// <param name="password">The password to use for decryption (optional).</param>
|
|
/// <returns>The decrypted data.</returns>
|
|
public byte[] DecryptAes(byte[] cipher, string password = null)
|
|
{
|
|
if (password == null)
|
|
password = File.ReadAllText(masterKeyFile);
|
|
|
|
return AesDecrypt(cipher, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts data using the AES algorithm and a password.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When the <paramref name="password"/> parameter is <c>null</c>, the key from the file (set on initialize) is used instead.
|
|
/// </remarks>
|
|
/// <param name="plain">The data to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption (optional).</param>
|
|
/// <returns>The encrypted data (cipher).</returns>
|
|
public byte[] EncryptAes(byte[] plain, string password = null)
|
|
{
|
|
if (password == null)
|
|
password = File.ReadAllText(masterKeyFile);
|
|
|
|
return AesEncrypt(plain, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypts a Base64 string using the AES algorithm and a password to an UTF-8 string.
|
|
/// </summary>
|
|
/// <param name="cipherStr">The encrypted Base64 encoded string.</param>
|
|
/// <param name="password">The password to use for decryption (optional).</param>
|
|
/// <returns>The decrypted UTF-8 string string.</returns>
|
|
public string DecryptAes(string cipherStr, string password = null)
|
|
{
|
|
byte[] cipher = Convert.FromBase64String(cipherStr);
|
|
byte[] plain = DecryptAes(cipher, password);
|
|
return Encoding.UTF8.GetString(plain);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts an UTF-8 string using the AES algorithm and a password to a Base64 string.
|
|
/// </summary>
|
|
/// <param name="plainStr">The UTF-8 string to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption (optional).</param>
|
|
/// <returns>The encrypted Base64 encoded string.</returns>
|
|
public string EncryptAes(string plainStr, string password = null)
|
|
{
|
|
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
|
|
byte[] cipher = EncryptAes(plain, password);
|
|
return Convert.ToBase64String(cipher);
|
|
}
|
|
|
|
#endregion AES
|
|
|
|
#region Triple DES
|
|
|
|
/// <summary>
|
|
/// Decrypts data using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When the <paramref name="password"/> parameter is <c>null</c>, the key from the file (set on initialize) is used instead.
|
|
/// </remarks>
|
|
/// <param name="cipher">The encrypted data (cipher).</param>
|
|
/// <param name="password">The password to use for decryption (optional).</param>
|
|
/// <returns>The decrypted data.</returns>
|
|
public byte[] DecryptTripleDes(byte[] cipher, string password = null)
|
|
{
|
|
if (password == null)
|
|
password = File.ReadAllText(masterKeyFile);
|
|
|
|
return TripleDesDecrypt(cipher, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts data using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When the <paramref name="password"/> parameter is <c>null</c>, the key from the file (set on initialize) is used instead.
|
|
/// </remarks>
|
|
/// <param name="plain">The data to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption (optional).</param>
|
|
/// <returns>The encrypted data (cipher).</returns>
|
|
public byte[] EncryptTripleDes(byte[] plain, string password = null)
|
|
{
|
|
if (password == null)
|
|
password = File.ReadAllText(masterKeyFile);
|
|
|
|
return TripleDesEncrypt(plain, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypts a Base64 encoded string using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When the <paramref name="password"/> parameter is <c>null</c>, the key from the file (set on initialize) is used instead.
|
|
/// </remarks>
|
|
/// <param name="cipherStr">The encrypted Base64 encoded string.</param>
|
|
/// <param name="password">The password to use for decryption (optional).</param>
|
|
/// <returns>The decrypted UTF-8 string.</returns>
|
|
public string DecryptTripleDes(string cipherStr, string password = null)
|
|
{
|
|
byte[] cipher = Convert.FromBase64String(cipherStr);
|
|
byte[] plain = DecryptTripleDes(cipher, password);
|
|
return Encoding.UTF8.GetString(plain);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts an UTF-8 string using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When the <paramref name="password"/> parameter is <c>null</c>, the key from the file (set on initialize) is used instead.
|
|
/// </remarks>
|
|
/// <param name="plainStr">The UTF-8 string to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption (optional).</param>
|
|
/// <returns>The encrypted Base64 encoded string.</returns>
|
|
public string EncryptTripleDes(string plainStr, string password = null)
|
|
{
|
|
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
|
|
byte[] cipher = EncryptTripleDes(plain, password);
|
|
return Convert.ToBase64String(cipher);
|
|
}
|
|
|
|
#endregion Triple DES
|
|
|
|
#endregion Instance methods
|
|
|
|
#region Static methods
|
|
|
|
#region Encryption
|
|
|
|
#region AES
|
|
|
|
/// <summary>
|
|
/// Decrypts data using the AES algorithm and a password.
|
|
/// </summary>
|
|
/// <param name="cipher">The encrypted data (cipher).</param>
|
|
/// <param name="password">The password to use for decryption.</param>
|
|
/// <returns>The decrypted data.</returns>
|
|
public static byte[] AesDecrypt(byte[] cipher, string password)
|
|
{
|
|
byte[] salt = new byte[saltLength];
|
|
Array.Copy(cipher, salt, saltLength);
|
|
|
|
using var gen = new Rfc2898DeriveBytes(password, salt);
|
|
using var aes = Aes.Create();
|
|
|
|
aes.Mode = CipherMode.CBC;
|
|
aes.Padding = PaddingMode.PKCS7;
|
|
aes.Key = gen.GetBytes(aes.KeySize / 8);
|
|
aes.IV = gen.GetBytes(aes.BlockSize / 8);
|
|
|
|
using var ms = new MemoryStream();
|
|
using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);
|
|
|
|
cs.Write(cipher, saltLength, cipher.Length - saltLength);
|
|
cs.FlushFinalBlock();
|
|
|
|
return ms.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts data using the AES algorithm and a password.
|
|
/// </summary>
|
|
/// <param name="plain">The data to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption.</param>
|
|
/// <returns>The encrypted data (cipher).</returns>
|
|
public static byte[] AesEncrypt(byte[] plain, string password)
|
|
{
|
|
byte[] salt = GetRandomBytes(saltLength);
|
|
|
|
using var gen = new Rfc2898DeriveBytes(password, salt);
|
|
using var aes = Aes.Create();
|
|
|
|
aes.Mode = CipherMode.CBC;
|
|
aes.Padding = PaddingMode.PKCS7;
|
|
aes.Key = gen.GetBytes(aes.KeySize / 8);
|
|
aes.IV = gen.GetBytes(aes.BlockSize / 8);
|
|
|
|
using var ms = new MemoryStream();
|
|
using var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
|
|
|
|
ms.Write(salt, 0, salt.Length);
|
|
cs.Write(plain, 0, plain.Length);
|
|
cs.FlushFinalBlock();
|
|
|
|
return ms.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypts a Base64 string using the AES algorithm and a password to an UTF-8 string.
|
|
/// </summary>
|
|
/// <param name="cipherStr">The encrypted Base64 encoded string.</param>
|
|
/// <param name="password">The password to use for decryption.</param>
|
|
/// <returns>The decrypted UTF-8 string string.</returns>
|
|
public static string AesDecrypt(string cipherStr, string password)
|
|
{
|
|
byte[] cipher = Convert.FromBase64String(cipherStr);
|
|
byte[] plain = AesDecrypt(cipher, password);
|
|
return Encoding.UTF8.GetString(plain);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts an UTF-8 string using the AES algorithm and a password to a Base64 string.
|
|
/// </summary>
|
|
/// <param name="plainStr">The UTF-8 string to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption.</param>
|
|
/// <returns>The encrypted Base64 encoded string.</returns>
|
|
public static string AesEncrypt(string plainStr, string password)
|
|
{
|
|
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
|
|
byte[] cipher = AesEncrypt(plain, password);
|
|
return Convert.ToBase64String(cipher);
|
|
}
|
|
|
|
#endregion AES
|
|
|
|
#region Triple DES
|
|
|
|
/// <summary>
|
|
/// Decrypts data using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <param name="cipher">The encrypted data (cipher).</param>
|
|
/// <param name="password">The password to use for decryption.</param>
|
|
/// <returns>The decrypted data.</returns>
|
|
public static byte[] TripleDesDecrypt(byte[] cipher, string password)
|
|
{
|
|
byte[] salt = new byte[saltLength];
|
|
Array.Copy(cipher, salt, saltLength);
|
|
|
|
using var gen = new Rfc2898DeriveBytes(password, salt);
|
|
using var tdes = TripleDES.Create();
|
|
|
|
tdes.Mode = CipherMode.CBC;
|
|
tdes.Padding = PaddingMode.PKCS7;
|
|
tdes.Key = gen.GetBytes(tdes.KeySize / 8);
|
|
tdes.IV = gen.GetBytes(tdes.BlockSize / 8);
|
|
|
|
using var ms = new MemoryStream();
|
|
using var cs = new CryptoStream(ms, tdes.CreateDecryptor(), CryptoStreamMode.Write);
|
|
|
|
cs.Write(cipher, saltLength, cipher.Length - saltLength);
|
|
cs.FlushFinalBlock();
|
|
|
|
return ms.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts data using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <param name="plain">The data to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption.</param>
|
|
/// <returns>The encrypted data (cipher).</returns>
|
|
public static byte[] TripleDesEncrypt(byte[] plain, string password)
|
|
{
|
|
byte[] salt = GetRandomBytes(saltLength);
|
|
|
|
using var gen = new Rfc2898DeriveBytes(password, salt);
|
|
using var tdes = TripleDES.Create();
|
|
|
|
tdes.Mode = CipherMode.CBC;
|
|
tdes.Padding = PaddingMode.PKCS7;
|
|
tdes.Key = gen.GetBytes(tdes.KeySize / 8);
|
|
tdes.IV = gen.GetBytes(tdes.BlockSize / 8);
|
|
|
|
using var ms = new MemoryStream();
|
|
using var cs = new CryptoStream(ms, tdes.CreateEncryptor(), CryptoStreamMode.Write);
|
|
|
|
ms.Write(salt, 0, salt.Length);
|
|
cs.Write(plain, 0, plain.Length);
|
|
cs.FlushFinalBlock();
|
|
|
|
return ms.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decrypts an Base64 encoded string using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <param name="cipherStr">The encrypted Base64 encoded string.</param>
|
|
/// <param name="password">The password to use for decryption.</param>
|
|
/// <returns>The decrypted UTF-8 string.</returns>
|
|
public static string TripleDesDecrypt(string cipherStr, string password)
|
|
{
|
|
byte[] cipher = Convert.FromBase64String(cipherStr);
|
|
byte[] plain = TripleDesDecrypt(cipher, password);
|
|
return Encoding.UTF8.GetString(plain);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encrypts an UTF-8 string using the triple DES algorithm and a password.
|
|
/// </summary>
|
|
/// <param name="plainStr">The UTF-8 string to encrypt.</param>
|
|
/// <param name="password">The password to use for encryption.</param>
|
|
/// <returns>The encrypted Base64 encoded string.</returns>
|
|
public static string TripleDesEncrypt(string plainStr, string password)
|
|
{
|
|
byte[] plain = Encoding.UTF8.GetBytes(plainStr);
|
|
byte[] cipher = TripleDesEncrypt(plain, password);
|
|
return Convert.ToBase64String(cipher);
|
|
}
|
|
|
|
#endregion Triple DES
|
|
|
|
#endregion Encryption
|
|
|
|
#region Hashing
|
|
|
|
#region MD5
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a string using the MD5 algorithm.
|
|
/// </summary>
|
|
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
|
|
/// <returns>The MD5 hash value, in hexadecimal notation.</returns>
|
|
public static string Md5(string str)
|
|
{
|
|
return Md5(Encoding.UTF8.GetBytes(str));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a file using the MD5 algorithm.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to read.</param>
|
|
/// <returns>The MD5 hash value, in hexadecimal notation.</returns>
|
|
public static string Md5File(string fileName)
|
|
{
|
|
using var md5 = MD5.Create();
|
|
using var fs = new FileStream(fileName, FileMode.Open);
|
|
return md5.ComputeHash(fs).BytesToHex();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash from a byte array value using the MD5 algorithm.
|
|
/// </summary>
|
|
/// <param name="bytes">The byte array.</param>
|
|
/// <returns>The MD5 hash value, in hexadecimal notation.</returns>
|
|
public static string Md5(byte[] bytes)
|
|
{
|
|
using var md5 = MD5.Create();
|
|
return md5.ComputeHash(bytes).BytesToHex();
|
|
}
|
|
|
|
#endregion MD5
|
|
|
|
#region SHA-1
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a string using the SHA-1 algorithm.
|
|
/// </summary>
|
|
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
|
|
/// <returns>The SHA-1 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha1(string str)
|
|
{
|
|
return Sha1(Encoding.UTF8.GetBytes(str));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a file using the SHA-1 algorithm.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to read.</param>
|
|
/// <returns>The SHA-1 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha1File(string fileName)
|
|
{
|
|
using var sha1 = SHA1.Create();
|
|
using var fs = new FileStream(fileName, FileMode.Open);
|
|
return sha1.ComputeHash(fs).BytesToHex();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash from a byte array value using the SHA-1 algorithm.
|
|
/// </summary>
|
|
/// <param name="bytes">The byte array.</param>
|
|
/// <returns>The SHA-1 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha1(byte[] bytes)
|
|
{
|
|
using var sha1 = SHA1.Create();
|
|
return sha1.ComputeHash(bytes).BytesToHex();
|
|
}
|
|
|
|
#endregion SHA-1
|
|
|
|
#region SHA-256
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a string using the SHA-256 algorithm.
|
|
/// </summary>
|
|
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
|
|
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha256(string str)
|
|
{
|
|
return Sha256(Encoding.UTF8.GetBytes(str));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a file using the SHA-256 algorithm.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to read.</param>
|
|
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha256File(string fileName)
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
using var fs = new FileStream(fileName, FileMode.Open);
|
|
return sha256.ComputeHash(fs).BytesToHex();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash from a byte array value using the SHA-256 algorithm.
|
|
/// </summary>
|
|
/// <param name="bytes">The byte array.</param>
|
|
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha256(byte[] bytes)
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
return sha256.ComputeHash(bytes).BytesToHex();
|
|
}
|
|
|
|
#endregion SHA-256
|
|
|
|
#region SHA-512
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a string using the SHA-512 algorithm.
|
|
/// </summary>
|
|
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
|
|
/// <returns>The SHA-512 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha512(string str)
|
|
{
|
|
return Sha512(Encoding.UTF8.GetBytes(str));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash value from a file using the SHA-512 algorithm.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to read.</param>
|
|
/// <returns>The SHA-512 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha512File(string fileName)
|
|
{
|
|
using var sha512 = SHA512.Create();
|
|
using var fs = new FileStream(fileName, FileMode.Open);
|
|
return sha512.ComputeHash(fs).BytesToHex();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes a hash from a byte array value using the SHA-512 algorithm.
|
|
/// </summary>
|
|
/// <param name="bytes">The byte array.</param>
|
|
/// <returns>The SHA-512 hash value, in hexadecimal notation.</returns>
|
|
public static string Sha512(byte[] bytes)
|
|
{
|
|
using var sha512 = SHA512.Create();
|
|
return sha512.ComputeHash(bytes).BytesToHex();
|
|
}
|
|
|
|
#endregion SHA-512
|
|
|
|
#endregion Hashing
|
|
|
|
#region Random
|
|
|
|
/// <summary>
|
|
/// Generates an array with random (non-zero) bytes.
|
|
/// </summary>
|
|
/// <param name="count">The number of bytes to generate.</param>
|
|
/// <returns></returns>
|
|
public static byte[] GetRandomBytes(int count)
|
|
{
|
|
using var gen = RandomNumberGenerator.Create();
|
|
byte[] bytes = new byte[count];
|
|
gen.GetNonZeroBytes(bytes);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a string with random characters.
|
|
/// </summary>
|
|
/// <param name="length">The length of the string to generate.</param>
|
|
/// <param name="pool">The characters to use (Default: [a-zA-Z0-9]).</param>
|
|
/// <returns></returns>
|
|
public static string GetRandomString(int length, string pool = null)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(pool))
|
|
pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
|
|
|
var sb = new StringBuilder(length);
|
|
int multiply = sizeof(int) / sizeof(byte);
|
|
int len = length * multiply;
|
|
byte[] bytes = GetRandomBytes(len);
|
|
for (int i = 0; i < bytes.Length; i += multiply)
|
|
{
|
|
uint number = BitConverter.ToUInt32(bytes, i);
|
|
sb.Append(pool[(int)(number % pool.Length)]);
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
#endregion Random
|
|
|
|
#region Probing security
|
|
|
|
/// <summary>
|
|
/// Determines whether two strings are equal in constant time. This method does not stop
|
|
/// early if a difference was detected, unless the length differs.
|
|
/// </summary>
|
|
/// <param name="a">The first string.</param>
|
|
/// <param name="b">The second string.</param>
|
|
/// <returns>true, if both strings are equal; otherwise, false.</returns>
|
|
public static bool SecureEquals(string a, string b)
|
|
{
|
|
if ((a == null) != (b == null))
|
|
return false;
|
|
if (a.Length != b.Length)
|
|
return false;
|
|
|
|
int differentBits = 0;
|
|
for (int i = 0; i < a.Length; i++)
|
|
{
|
|
differentBits |= a[i] ^ b[i];
|
|
}
|
|
return differentBits == 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether two byte arrays are equal in constant time. This method does not stop
|
|
/// early if a difference was detected, unless the length differs.
|
|
/// </summary>
|
|
/// <param name="a">The first array.</param>
|
|
/// <param name="b">The second array.</param>
|
|
/// <returns>true, if both arrays are equal; otherwise, false.</returns>
|
|
public static bool SecureEquals(byte[] a, byte[] b)
|
|
{
|
|
if ((a == null) != (b == null))
|
|
return false;
|
|
if (a.Length != b.Length)
|
|
return false;
|
|
|
|
int differentBits = 0;
|
|
for (int i = 0; i < a.Length; i++)
|
|
{
|
|
differentBits |= a[i] ^ b[i];
|
|
}
|
|
return differentBits == 0;
|
|
}
|
|
|
|
#endregion Probing security
|
|
|
|
#endregion Static methods
|
|
}
|
|
}
|