Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HEX representation of byte array

I feel pretty stupid asking this, but as I don't know the answer, I'm going ahead anyway.

I"m trying out some authentication code and want to know why the byte array I get from Rfc2898DeriveBytes needs to be converted to HEX and back to a byte array again for it to correctly initialise my HMACSHA1 object. I feel like I am doing something silly, or simply missing something obvious.

My client code is a javascript function based on crypto-js;

var key256bit = Crypto.PBKDF2(passwordEntered, saltBytes, 32, { iterations: 1000 }); 
var hmacBytes = Crypto.HMAC(Crypto.SHA1, url, key256bit, { asBytes: true });
var base64Hash = Crypto.util.bytesToBase64(hmacBytes);

My Server side code is as follows;

    Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password,
                                              encoding.GetBytes(salt), 1000);
    byte[] key = rfc2898.GetBytes(32);

    // Don't think I should need to do this. 
    // However, it wont work if I initialise HMACSHA1 
    // with the rfc2898.GetBytes(32)
    string test = ByteArrayToString(key); 

    HMACSHA1 hmacSha1 = new HMACSHA1(encoding.GetBytes(test));
    
    byte[] computedHash = hmacSha1.ComputeHash(encoding.GetBytes(requestUri));
    string computedHashString = Convert.ToBase64String(computedHash);

My ByteArrayToString method which I nabbed from the web is;

private static string ByteArrayToString(byte[] ba)
{
    StringBuilder hex = new StringBuilder(ba.Length * 2);
    foreach (byte b in ba)
        hex.AppendFormat("{0:x2}", b);
    return hex.ToString();
}

So I can see that I get 32 bytes from my call to rfc2898.GetBytes(32). I converted that to HEX using the ByteArrayToString method to confirm it matches what I am seeing in my Javascript variable key256bit. Now my test variable is a string of length 64 and when I pass that to HMACSHA1's constuctor using encoding.GetBytes(test) it is a byte array of length 64.

The doco on crypto-js is a bit lacking, but I thought the call to Crypto.PBKDF2 with a param of 32 and it was creating a key of 32 bytes long (or 256 bits).

Any clarification is much appreciated.

like image 766
Mr Moose Avatar asked Apr 03 '12 08:04

Mr Moose


1 Answers

I suspect this is the root of the issue, in PBKDF2.js:

return options && options.asBytes ? derivedKeyBytes :
       options && options.asString ? Binary.bytesToString(derivedKeyBytes) :
       util.bytesToHex(derivedKeyBytes);

Because you haven't provided options for either asBytes or asString, it's converting the key to a hex representation - just as your C# code does. So at the moment you are using a 512 bit key, precisely because you're generating 2 bytes of "used key" from each byte of "original key".

I suspect that if you specify the asBytes option in the Javascript, it will just work without the extra hex part in the C# code.

Then again, I've never seen PBKDF2 before, so I could be way off base...

like image 178
Jon Skeet Avatar answered Jan 04 '23 07:01

Jon Skeet