Search code examples
c#php.netpbkdf2rfc2898

Why is the output for hash_pbkdf2 (PHP) different than the .NET / C# implementation


In a small project of mine i need to calculate the hash of a function.

I have a working example of PHP hash

$pass = "123456";
$mysalt = strrev($pass);
echo hash_pbkdf2('sha1', $pass, $mysalt, 1000, 32); //using the PHP inbuilt function

echo "</br>";
include_once('PasswordHash.php');
echo pbkdf2('sha1', $pass, $mysalt, 1000, 16);  //using external code

Both of them has same output : 523d904c8f2df96634d9eed3b444838e

Now i need my code to be backward be compatible with C# generated as the password has will be verified by a PHP server. and the Request is to be sent by a C# application.

Here is what i tried : output = 8e59ead5f90c6af11cf80641d51c241c

public static class Program
{
    public static string ReverseString(this string s)
    {
        char[] arr = s.ToCharArray();
        Array.Reverse(arr);
        return new string(arr);
    }

    static void Main(string[] args)
    {
        var pass = "123456";
        byte[] salt = Encoding.ASCII.GetBytes(pass.ReverseString());


        //https://github.com/defuse/password-hashing/blob/master/PasswordHash.cs
        //was getting error salt not 8 byte,
        //http://stackoverflow.com/questions/1647481/what-is-the-c-sharp-equivalent-of-the-php-pack-function
        salt = Pack(pass.ReverseString());
        var hash = PasswordHash.PBKDF2(pass, salt, 1000, 16);
        Console.WriteLine(BitConverter.ToString(hash).Replace("-", string.Empty).ToLower());

        Console.ReadKey();

    }

    public static byte[] Pack(string salt)
    {
        using (var ms = new MemoryStream())
        {
            using (var bw = new BinaryWriter(ms))
            {
                var data = Encoding.ASCII.GetBytes(salt);
                bw.Write(data.Length + 4); // Size of ASCII string + length (4 byte int)
                bw.Write(data);
            }

            return ms.ToArray();
        }
    }
}

Solution

  • The problem here is with your salt. It is only 6 bytes long and PHP handles this different then your c# code. If you update the code to the following:

    <?php
    $pass = "1234567890";
    $mysalt = strrev($pass);
    echo hash_pbkdf2('sha1', $pass, $mysalt, 1000, 32);
    ?>
    

    your output is: 42e8bfc7fc5fd4686915d49d5a29bc1e

    Then adjust your c# code to:

    var pass = "1234567890";
    byte[] salt = Encoding.ASCII.GetBytes(pass.ReverseString());
    
    //DISABLE YOUR PACK METHOD
    //salt = Pack(pass.ReverseString());
    
    var hash = PasswordHash.PBKDF2(pass, salt, 1000, 16);
    Console.WriteLine(BitConverter.ToString(hash).Replace("-", string.Empty).ToLower());
    
    Console.ReadKey();
    

    The output is: 42e8bfc7fc5fd4686915d49d5a29bc1e

    The difference comes from your Pack method, it randomly adds 4 bytes to the salt. You can see that easily in the inspector in VS.

    Salt inspector

    So the easy fix is to use a salt that has atleast 8 chars (minimum for Rfc2898DeriveBytes which is used by your C# code) and dont use your Pack method

    If you look at the php docs there is a "Request for comments" that mentions that the salt must be atleast 8bytes (64bit). So using less leads to conflicts, like you already encountered.

    UPDATE

    Now if you realy want to use the less secure salt with <8 bytes, you can look at the following stackoverflow question PBKDF2 implementation in C# with Rfc2898DeriveBytes for a c# version that doesnt require a minimum length.