38

I am trying to calculate a SHA-1 Hash from a string, but when I calculate the string using php's sha1 function I get something different than when I try it in C#. I need C# to calculate the same string as PHP (since the string from php is calculated by a 3rd party that I cannot modify). How can I get C# to generate the same hash as PHP? Thanks!!!

String = s934kladfklada@a.com

C# Code (Generates d32954053ee93985f5c3ca2583145668bb7ade86)

        string encode = secretkey + email;
        UnicodeEncoding UE = new UnicodeEncoding();
        byte[] HashValue, MessageBytes = UE.GetBytes(encode);
        SHA1Managed SHhash = new SHA1Managed();
        string strHex = "";

        HashValue = SHhash.ComputeHash(MessageBytes);
        foreach(byte b in HashValue) {
            strHex += String.Format("{0:x2}", b);
        }

PHP Code (Generates a9410edeaf75222d7b576c1b23ca0a9af0dffa98)

sha1();
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
Anand Capur
  • 808
  • 1
  • 9
  • 18
  • 9
    maybe the text is handled as Unicode on one system and as ASCII on the other? – Aziz Apr 26 '09 at 04:30
  • 2
    Amazing what simple encoding does ;). That fixed it, I just changed UnicodeEncoding to ASCIIEncoding and it worked perfectly... Thanks so much! – Anand Capur Apr 26 '09 at 04:43

6 Answers6

40

Use ASCIIEncoding instead of UnicodeEncoding. PHP uses ASCII charset for hash calculations.

Andrew Moore
  • 93,497
  • 30
  • 163
  • 175
  • 1
    Since someone deleted their answer below, UTF8Encoding will also work in your test case but will not work when you have UTF8 characters such as 艾. The proper encoding to use when working with PHP IS ASCIIEncoding. – Andrew Moore Apr 26 '09 at 04:49
  • 1
    Since it took me some time to find it out: the input to the hash-function must also be converted usig ASCII! And thanks for the answers! SO is just great! – Mario The Spoon Dec 17 '11 at 10:11
  • 1
    Worth noting also that .NET uses Unicode by default if you don't specify the encoding. – Hades Apr 10 '13 at 10:06
8

This method in .NET is equivalent to sha1 in php:

string sha1Hash(string password)
{
    return string.Join("", SHA1CryptoServiceProvider.Create().ComputeHash(Encoding.UTF8.GetBytes(password)).Select(x => x.ToString("x2")));
}
thorn0
  • 9,362
  • 3
  • 68
  • 96
OmarElsherif
  • 177
  • 1
  • 4
  • Just courious: Why the `string.Join`? – Spontifixus Sep 20 '12 at 14:31
  • 1
    The Select(...) method outputs an array of strings, each one represents a single byte from the bytes returned by ComputeHash() method, these strings needs to be put together, one after another. The join method has this prototype: String.Join(string separator, params string[] value), see (http://msdn.microsoft.com/en-us/library/57a79xd0.aspx). – OmarElsherif Sep 22 '12 at 21:43
  • Ahh... didn't scroll to the right, und thus didn't see the select. Forget that question. ;) – Spontifixus Sep 24 '12 at 07:27
  • This is unreadable long and also is not the equivalent of the PHP sha1 function as PHP uses ASCII encoding, not UTF8 – Enes Sadık Özbek Oct 13 '19 at 18:59
6

I had this problem also. The following code will work.

string dataString = "string to hash";
SHA1 hash = SHA1CryptoServiceProvider.Create();
byte[] plainTextBytes = Encoding.ASCII.GetBytes(dataString);
byte[] hashBytes = hash.ComputeHash(plainTextBytes);
string localChecksum = BitConverter.ToString(hashBytes)
.Replace("-", "").ToLowerInvariant();
Paul Johnson
  • 1,417
  • 1
  • 17
  • 26
2

Had the same problem. This code worked for me:

string encode = secretkey + email;
SHA1 sha1 = SHA1CryptoServiceProvider.Create();
byte[] encodeBytes = Encoding.ASCII.GetBytes(encode);
byte[] encodeHashedBytes = sha1.ComputeHash(passwordBytes);
string pencodeHashed = BitConverter.
ToString(encode HashedBytes).Replace("-", "").ToLowerInvariant();
nicojs
  • 1,879
  • 2
  • 18
  • 34
1

FWIW, I had a similar issue in Java. It turned out that I had to use "UTF-8" encoding to produce the same SHA1 hashes in Java as the sha1 function produces in PHP 5.3.1 (running on XAMPP Vista).

    private static String SHA1(final String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        final MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(text.getBytes("UTF-8"));
        return new String(org.apache.commons.codec.binary.Hex.encodeHex(md.digest()));
    }
Gunnar
  • 2,264
  • 17
  • 31
-1

Try The following! I think it will work great:

public static string SHA1Encodeb64(string toEncrypt)
    {
        //Produce an array of bytes which is the SHA1 hash
        byte[] sha1Signature = new byte[40];

        byte[] sha = System.Text.Encoding.Default.GetBytes(toEncrypt);
        SHA1 sha1 = SHA1Managed.Create();
        sha1Signature = sha1.ComputeHash(sha);


        /**
        * The BASE64 encoding standard's 6-bit alphabet, from RFC 1521,
        * plus the padding character at the end.
        */
        char[] Base64Chars = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
            'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z', '0', '1', '2', '3',
            '4', '5', '6', '7', '8', '9', '+', '/',
            '='
            };
        //Algorithm to encode the SHA1 hash using Base64
        StringBuilder sb = new StringBuilder();
        int len = sha1Signature.Length;
        int i = 0;
        int ival;
        while (len >= 3)
        {
            ival = ((int)sha1Signature[i++] + 256) & 0xff;
            ival <<= 8;
            ival += ((int)sha1Signature[i++] + 256) & 0xff;
            ival <<= 8;
            ival += ((int)sha1Signature[i++] + 256) & 0xff;
            len -= 3;
            sb.Append(Base64Chars[(ival >> 18) & 63]);
            sb.Append(Base64Chars[(ival >> 12) & 63]);
            sb.Append(Base64Chars[(ival >> 6) & 63]);
            sb.Append(Base64Chars[ival & 63]);
        }
        switch (len)
        {
            case 0: // No pads needed.
                break;
            case 1: // Two more output bytes and two pads.
                ival = ((int)sha1Signature[i++] + 256) & 0xff;
                ival <<= 16;
                sb.Append(Base64Chars[(ival >> 18) & 63]);
                sb.Append(Base64Chars[(ival >> 12) & 63]);
                sb.Append(Base64Chars[64]);
                sb.Append(Base64Chars[64]);
                break;
            case 2: // Three more output bytes and one pad.
                ival = ((int)sha1Signature[i++] + 256) & 0xff;
                ival <<= 8;
                ival += ((int)sha1Signature[i] + 256) & 0xff;
                ival <<= 8;
                sb.Append(Base64Chars[(ival >> 18) & 63]);
                sb.Append(Base64Chars[(ival >> 12) & 63]);
                sb.Append(Base64Chars[(ival >> 6) & 63]);
                sb.Append(Base64Chars[64]);
                break;
        }
        //Encode the signature using Base64
        string base64Sha1Signature = sb.ToString();
        return base64Sha1Signature;
    }
shanethehat
  • 15,460
  • 11
  • 57
  • 87
Robert
  • 1
  • 1
    using `System.Text.Encoding.Default` is a **very** bad idea when dealing with strings and cryptography. Different platforms uses different defaults (UTF8, ASCII, Unicode) and using `Default` just makes it even more complicated (since it can change between systems, configuration and versions). – poupou Feb 21 '12 at 15:56
  • There is a Base64 encoder in .NET. Also I see `byte[] sha1Signature = new byte[40];`, and then `sha1Signature = sha1.ComputeHash(sha);`. You don't need to `new byte[40]`. – quantum Oct 27 '12 at 01:52