Search code examples
c#phphashportingtranslate

Problems while porting php to C# with hash_hmac


I'm trying to port a script from php to C#. The script is to create a key for authentication for my school intranet. I got the most part but the output of the two differ.

Code in PHP:

/**
 * Generate authorization headers
 *
 * @param string $username
 * @param string $password
 * @param string $prefix
 * @param string $hashAlgorithm
 *
 * @return array(
 *          'X-gr-AuthDate': uncrypted date
 *          'Authorization': encrypted token
 * )
 */

function generateHeaders($username, $password, $prefix, $hashAlgorithm = 'sha1')
{
    $rfc_1123_date = gmdate('D, d M Y H:i:s T', time());
    $xgrdate = utf8_encode($rfc_1123_date);
    $userPasswd = base64_encode(hash($hashAlgorithm, $password, true));

    $signature = base64_encode(hash_hmac($hashAlgorithm, $userPasswd, $xgrdate));
    $auth = $prefix . " " . base64_encode($username) . ":" . $signature;
    $headers = array(
                   'X-gr-AuthDate' => $xgrdate,
                   'Authorization' => $auth
    );

    return $headers;
}

$headers = generateHeaders("rest-example", "topsecret", "gr001");

header('Content-Type: application/json');

echo json_encode($headers);

Code in C#:

    public static void Main (string[] args)
    {
        // Get data from php function
        WebClient wc = new WebClient ();
        byte[] res = wc.DownloadData ("http://localhost/test.php");
        JObject obj = JObject.Parse (System.Text.Encoding.UTF8.GetString (res));
        Console.WriteLine ((string)obj["Authorization"]);

        // Generate auth data
        string[] test = ConstructHeaders ("rest-example", "topsecret", "gr001");
        Console.WriteLine (test [0]);
    }

    public static string[] ConstructHeaders(string username, string password, string prefix)
    {
        Encoding utf8 = Encoding.UTF8;
        SHA1 sha = new SHA1CryptoServiceProvider(); 

        // Get current date
        DateTime now = DateTime.Now;
        string rfc_date = now.ToString ("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; // TODO: Automatically add timezone


        string userPasswd = Convert.ToBase64String(sha.ComputeHash (utf8.GetBytes(password)));
        string signature = Convert.ToBase64String(utf8.GetBytes(EncodeHMac (userPasswd, rfc_date)));

        string auth = prefix + " " +  Convert.ToBase64String(utf8.GetBytes(username)) + ":" + signature;
        return new string[] {auth, rfc_date};
    }

    static string EncodeHMac(string message, string key)
    {
        var keyByte = Encoding.UTF8.GetBytes(key);
        using (var hmacsha1 = new HMACSHA1(keyByte))
        {
            hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(message));

            return ByteToString(hmacsha1.Hash);
        }
    }
    static string ByteToString(byte[] buff)
    {
        string sbinary = "";
        for (int i = 0; i < buff.Length; i++)
            sbinary += buff[i].ToString("X2"); /* hex format */
        return sbinary;
    }

The output produces for example:

gr001 cmVzdC1leGFtcGxl:NmY4ZTgzMWI4NGRhYmExYTEzMjdmMzQ3Nzg3ZWU0MDNjZWQ1ZTFhOQ==
gr001 cmVzdC1leGFtcGxl:MTUzMzlEMTQyNjRBNzZEQ0I2QkJERUE4MjEyMkNCQ0Q4ODhCQjE2MQ==

The first one (the correct one) doesn't match the second one in C#. What I've debugged so far is that userPasswd is generated correctly but as soon as I get to the hmac hashing part it doesn't match anymore.

Also here is all I got from the documentation:

X-gr-AuthDate: date
Authorization: prefix encoded_username:signature



where

    date = according to RFC 822, updated by RFC 1123, e.g 'Sun, 06 Nov 1994 08:49:37 GMT'
    prefix = gr001 (provided with your account's security credentials)
    encoded_username = Base64(username)
    signature = Base64(HMAC-Sha1(secret-token, date))



    where

        secret-token = hash(user-password), hash function to use is provided with your account's security credentials (SHA-1)

Solution

  • I figured it out!

    I made two mistakes:

    1. I used DateTime.Now instead of DateTime.UtcNow so the dates of the two functions didn't match. (I don't know how I missed that...)

    2. While the hash_hmac function in php returns the hex string as lowercase the C# function returns in uppercase and therefore I get a different result in Convert.ToBase64String

    Thanks for the help anyways.