Premise:
I'm writing a php app that runs alongside ASP .NET site. I want to utilize the existing users and roles tables in the MSSQL server and can, except for one thing- matching the hashed passwords in the database. I found a blog that shows how .NET does the hashing (with salt) so I can try to recreate it in PHP.

A developer on Twitter sent me this link which shows the .NET membership procedure for developing the hashes for passwords.

  1. private static HashAlgorithm passwordHasher = HashAlgorithm.Create("SHA1");
    2.
  2. private bool ValidateUser(string username, string password)
  3. {
  4. var user = GlobalApplication.Database.Users.FirstOrDefault(u => u.UserName == username);
  5. if (user == null) return false;
    7.
  6. byte[] saltBytes = Convert.FromBase64String(user.Membership.PasswordSalt);
  7. byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
  8. byte[] bytesToHash = new byte[saltBytes.Length + passwordBytes.Length];
  9. saltBytes.CopyTo(bytesToHash, 0);
  10. passwordBytes.CopyTo(bytesToHash, saltBytes.Length);
  11. byte[] hash = passwordHasher.ComputeHash(bytesToHash);
  12. string base64Hash = Convert.ToBase64String(hash);
  13. return user.Membership.Password == base64Hash
  14. }

This was very useful is seeing what is done in C# and clues me into the procedures needed to replicate it in PHP
I've gleemed over search results that SHA1 is the hash algorithm used (and PHP has implementations of this).

However a couple of hurdles I've run into:
1. converting the UTF-8 password into bytes in PHP comes back as a string of 1's and 0's and the salt unpacks as true binary (returning +7ª\ætR<_9deji|Ï)
2. not sure the "copyTo()" method is easily replaced by straight out concatenation

Thoughts?

my PHP code version of above:

<?php
$hash_password = "bgT8AutbQgtlec0VNhhtmAXdXxvI0V/96Vj48KRz26E=";
$salt = "KzeqXOZ0UjwYOWRlaml8zw==";

$password = "church";

$salt = base64_decode($salt); //convert salt back to it's binary state

$passwordBytes = bstr2bin(utf8_encode($password)); //convert password to utf8 then binary

echo "$salt<br>"; //prints "+7ª\ætR<9deji|Ï"
echo "$passwordBytes<br>"; //prints 11000110110100001110101011100100110001101101000

$bytesToHash = $salt + $passwordBytes; //combine the 2 binary objs
$hash = sha1($bytesToHash, true); //sha1 hash it
$hashedpassword = base64_encode($hash); //base64 encode it into a string

echo "$hashedpassword<br>$hash_password";
?>
last "echo" prints:
2mOfuA7gRcDEYNJF9fjN83em+Jw=
bgT8AutbQgtlec0VNhhtmAXdXxvI0V/96Vj48KRz26E=

    Second thing that leaps out at me is that "$salt + $passwordBytes" should be "$salt . $passwordBytes".

    Thirdly, I'm not 100% sure about the UTF-8 stuff: CLS characters are all two bytes long, while PHP's characters are still one byte (PHP6 will address this): any characters like € that aren't standard ASCII stand a good chance of being encoded differently, resulting in different byte sequences. (Depending on where the PHP string came from it might already be UTF-8 encoded (if it had been submitted a page that is served with a UTF-8 encoding for example), in which case encoding it again is unnecessary); on the other hand it might be latin-1/ISO-8859-1, which would need to be encoded; on the gripping hand if the submitted password only uses plain ASCII characters then all three encodings (UTF-8, latin-1, and ASCII) are all equivalent and re-encoding as UTF-8 would have no effect.

      2 months later

      ok, updated the code, but still no direct answer:

      my PHP code version of above:

      $hash_password = "bgT8AutbQgtlec0VNhhtmAXdXxvI0V/96Vj48KRz26E=";
      $salt = "KzeqXOZ0UjwYOWRlaml8zw==";

      $password = "church";

      $salt = base64_decode($salt); //convert salt back to it's binary state

      $passwordBytes = bstr2bin(utf8_encode($password)); //convert password to utf8 then binary

      echo "$salt"; //prints "+7ª\ætR<9deji|Ï"
      echo "$passwordBytes"; //prints 11000110110100001110101011100100110001101101000

      $bytesToHash = $salt . $passwordBytes; //combine the 2 binary objs
      $hash = sha1($bytesToHash, true); //sha1 hash it
      $hashedpassword = base64_encode($hash); //base64 encode it into a string

      echo "$hashedpassword
      $hash_password";
      function bstr2bin($input)
      // Binary representation of a binary-string
      {
      if (!is_string($input)) return null; // Sanity check
      // Unpack as a hexadecimal string
      $value = unpack('H', $input);
      // Output binary representation
      return base_convert($value[1], 16, 2);
      }
      function bin2bstr($input)
      // Convert a binary expression (e.g., "100111") into a binary-string
      {
      if (!is_string($input)) return null; // Sanity check
      // Pack into a string
      return pack('H
      ', base_convert($input, 2, 16));

      }

      last "echo" prints:
      2mOfuA7gRcDEYNJF9fjN83em+Jw=
      bgT8AutbQgtlec0VNhhtmAXdXxvI0V/96Vj48KRz26E=

        5 months later

        How you solve the issue ? pls help me

          No solution was found for this issue. I think the roadblock is the unicode issue- Weedpacket's response:

          Thirdly, I'm not 100% sure about the UTF-8 stuff: CLS characters are all two bytes long, while PHP's characters are still one byte (PHP6 will address this): any characters like € that aren't standard ASCII stand a good chance of being encoded differently, resulting in different byte sequences. (Depending on where the PHP string came from it might already be UTF-8 encoded (if it had been submitted a page that is served with a UTF-8 encoding for example), in which case encoding it again is unnecessary); on the other hand it might be latin-1/ISO-8859-1, which would need to be encoded; on the gripping hand if the submitted password only uses plain ASCII characters then all three encodings (UTF-8, latin-1, and ASCII) are all equivalent and re-encoding as UTF-8 would have no effect.

            Write a Reply...