Calculating CRC-32 in C# and .NET

August 8th 2006 • .NET (, , , ) • 22,639 views • 13 responses

Just a few days ago I found myself needing to calculate a CRC-32 in .NET. With so many facilities available I was a little shocked that there was nothing built-in to do it so knocked up something myself.

Because unsigned ints aren’t CLS compliant it won’t play well with VB.Net and implementing the HashAlgorithm might lead people to believe it’s suitable for signing – it isn’t. CRC-32’s are only any good for checksums along the lines of WinZIP, RAR etc. and certainly shouldn’t come near a password and instead consider SHA-512 or similar.

As well as using it as a HashAlgorithm with block processing you can also access the static method Compute although there is an overhead with every call building the table with that. If neither option suits you needs cut ‘n splice it to something that does.

Updated: Fix for repeated calls, cache look-up table for the default polynomial and additional static Compute methods with defaults.

using System;
using System.Security.Cryptography;

public class Crc32 : HashAlgorithm {
    public const UInt32 DefaultPolynomial = 0xedb88320;
    public const UInt32 DefaultSeed = 0xffffffff;

    private UInt32 hash;
    private UInt32 seed;
    private UInt32[] table;
    private static UInt32[] defaultTable;

    public Crc32() {
        table = InitializeTable(DefaultPolynomial);
        seed = DefaultSeed;
        Initialize();
    }

    public Crc32(UInt32 polynomial, UInt32 seed) {
        table = InitializeTable(polynomial);
        this.seed = seed;
        Initialize();
    }

    public override void Initialize() {
        hash = seed;
    }

    protected override void HashCore(byte[] buffer, int start, int length) {
        hash = CalculateHash(table, hash, buffer, start, length);
    }

    protected override byte[] HashFinal() {
        byte[] hashBuffer = UInt32ToBigEndianBytes(~hash);
        this.HashValue = hashBuffer;
        return hashBuffer;
    }

    public override int HashSize {
        get { return 32; }
    }

    public static UInt32 Compute(byte[] buffer) {
        return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
    }

    public static UInt32 Compute(UInt32 seed, byte[] buffer) {
        return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
    }

    public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer) {
        return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
    }

    private static UInt32[] InitializeTable(UInt32 polynomial) {
        if (polynomial == DefaultPolynomial && defaultTable != null)
            return defaultTable;

        UInt32[] createTable = new UInt32[256];
        for (int i = 0; i < 256; i++) {
            UInt32 entry = (UInt32)i;
            for (int j = 0; j < 8; j++)
                if ((entry & 1) == 1)
                    entry = (entry >> 1) ^ polynomial;
                else
                    entry = entry >> 1;
            createTable[i] = entry;
        }

        if (polynomial == DefaultPolynomial)
            defaultTable = createTable;

        return createTable;
    }

    private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size) {
        UInt32 crc = seed;
        for (int i = start; i < size; i++)
            unchecked {
                crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
            }
        return crc;
    }

    private byte[] UInt32ToBigEndianBytes(UInt32 x) {
        return new byte[] {
			(byte)((x >> 24) & 0xff),
			(byte)((x >> 16) & 0xff),
			(byte)((x >> 8) & 0xff),
			(byte)(x & 0xff)
		};
    }
}

To compute the hash for a file simply:

Crc32 crc32 = new Crc32();
String hash = String.Empty;

using (FileStream fs = File.Open("c:\\myfile.txt", FileMode.Open))
	foreach (byte b in crc32.ComputeHash(fs)) hash += b.ToString("x2").ToLower();

Console.WriteLine("CRC-32 is {0}", hash);

[)amien

Related content

13 responses  

  1. Ronak on October 27th, 2006

    But how do i use it in the Actual Code.
    I want to calculate CRC of file…
    How can i use this code to do it

  2. Damien Guard on October 29th, 2006

    Updated to include usage sample.

    [)amien

  3. Pingback Calculating CRC-64 in C# and .NET » DamienG on November 19th, 2007

    [...] how the CRC-32 C# class I posted some time ago continues to get lots of Google hits I thought I’d post a CRC-64 version which will no doubt be far [...]

  4. Gaurav on January 1st, 2008

    ]Dude, thanks a lot, i found a lot of dumb code out there and this is the best code written for this purpose.

    I wrote one my self but it was a conversion from Java to c# and i did not use at least some support which c# provides and there was some other code which generated wrong checksomes on a 64-bit processor.
    Although i have not tested your code on 64 bit processor but it seems it should work fine as far as my knowledge is concerned.

  5. Mario on February 28th, 2008

    Nice code, but i found a bug if you compute values for streams larger than 4096 bytes (make textfile in sample code larger than 4096). In this case the HashCore function is called more than once but the complement is build in the CalculateHash function. So you don’t get the complement for the final value but for each intermediate value which will result in a wrong value. The complement build should take place in the HashFinal function.

  6. Damien Guard on February 28th, 2008

    You’re right, multiple calls to HashCore will carry on from the final complement instead of the actual current hash value.

    Removing the ~ complement from the CalculateHash function means you can’t use that static method directly… I think complementing it in Initialize and inside HashCore might be a better approach.

    Will update the code once I’ve had chance to test it and check the other hashing algorithms I have up.

    Thanks!

    [)amien

  7. stefan on August 15th, 2008

    Did you fix the error in your code now? I would like to use it, with files longer than 4096 bytes.

  8. Alessio on November 25th, 2008

    Hi,
    I tried to execute the program under Windows XP and the CRC-hash calculated for a file of 25MB wasn’t correct, just because of in windows XP the CalculateHash was called more than once(it works with a lenght of 4096 byte at a time). I toke off the ‘~’ from “return ~crc” of CalculateHash and put it on “byte[] hashBuffer = UInt32ToBigEndianBytes(~hash)” of HashFinal, so that step was made only once at the end of cycle (without modifying the intermediate values).
    In this way the algorithm worked corrctly.

    I hope I gived a hand,
    Alessio

  9. Sph3re on January 30th, 2009

    I think that returning it as a BigEndian Byte array is a mistake since 98% of .NET code is running on windows based systems (which use the Little-Endian method).

    also, reading every byte from the result array, formatting it to “x2″ and adding it into a string is expensive and causing you to construct 5 strings (1 empty, and another one for each byte you append the formatted output).

    I think that adding something like this would benefit everyone more.. (after returning little endian byte arrays) :)

    public static string FormatCRC32Result(byte[] result) {
       if (!BitConverter.IsLittleEndian) {
          Array.Reverse(result);
       }
       return BitConverter.ToUInt32(result, 0).ToString("X8").ToLower();
    }

    PS – thx for the kickass implementation :)
    PS #2 – it works fine on Vista ultimate x64

  10. camillo on May 13th, 2009

    How to speed up calculation?
    On 20GB files it takes a lot…
    Do you think can be done something like spot calculation?
    Say 1 byte every 100 bytes?

    Camillo

  11. Damien Guard on May 19th, 2009

    You can’t really do that – the whole idea of a checksum is that if a single byte changes or is transferred incorrectly then the checksum fails – if you only checksum every 1 byte in 100 then it’s not going to catch 99% of the errors.

    [)amien

  12. Mark on November 19th, 2009

    Any chance you could update the sample usage? Things appear to have changed since it was written…

    -M

  13. Pingback ASP.NET Performance - Part 3 - Cache Busting - Karl Seguin - CodeBetter.Com - Stuff you need to Code Better! on January 11th, 2010

    [...] of our asset folder) and hash values. For my "hash" I'm using a CRC32, specifically Damien Guard's C# implementation. The nice thing about CRC32 is that we end up with pretty small values. The downside is that [...]

Leave your response

  1. (kept private)