using System; using System.Collections.Generic; using System.Security.Cryptography; namespace Masuit.Tools.Security; /// /// 实现一个32位CRC哈希算法(兼容Zip) /// /// /// Crc32仅应用于与旧文件格式和算法向后兼容。 对于新的应用程序,它不够安全。 如果需要多次调用同一数据,请使用HashAlgorithm接口 /// public sealed class Crc32 : HashAlgorithm { ~Crc32() { Dispose(); } public const uint DefaultPolynomial = 0xedb88320u; public const uint DefaultSeed = 0xffffffffu; private static uint[] _defaultTable; private readonly uint _seed; private readonly uint[] _table; private uint _hash; public override int HashSize => 32; public Crc32() : this(DefaultPolynomial, DefaultSeed) { } public Crc32(uint polynomial, uint seed) { if (!BitConverter.IsLittleEndian) { throw new PlatformNotSupportedException("Big Endian 处理程序不支持"); } _table = InitializeTable(polynomial); _seed = _hash = seed; } public override void Initialize() { _hash = _seed; } protected override void HashCore(byte[] array, int ibStart, int cbSize) { _hash = CalculateHash(_table, _hash, array, ibStart, cbSize); } protected override byte[] HashFinal() { var hashBuffer = UInt32ToBigEndianBytes(~_hash); HashValue = hashBuffer; return hashBuffer; } public static uint Compute(byte[] buffer) { return Compute(DefaultSeed, buffer); } public static uint Compute(uint seed, byte[] buffer) { return Compute(DefaultPolynomial, seed, buffer); } public static uint Compute(uint polynomial, uint seed, byte[] buffer) { return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length); } private static uint[] InitializeTable(uint polynomial) { if (polynomial == DefaultPolynomial && _defaultTable != null) { return _defaultTable; } var createTable = new uint[256]; for (uint i = 0; i < 256; i++) { var entry = i; for (var j = 0; j < 8; j++) { if ((entry & 1) == 1) { entry = (entry >> 1) ^ polynomial; } else { entry >>= 1; } } createTable[i] = entry; } if (polynomial == DefaultPolynomial) { _defaultTable = createTable; } return createTable; } private static uint CalculateHash(uint[] table, uint seed, IList buffer, int start, int size) { var hash = seed; for (var i = start; i < start + size; i++) { hash = (hash >> 8) ^ table[buffer[i] ^ hash & 0xff]; } return hash; } private static byte[] UInt32ToBigEndianBytes(uint uint32) { var result = BitConverter.GetBytes(uint32); if (BitConverter.IsLittleEndian) { Array.Reverse(result); } return result; } }