6

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.

Community
  • 1
  • 1
Mr Moose
  • 5,946
  • 7
  • 34
  • 69
  • Please specify what language(s) you are using when you post code snippets. It's not immediately obvious that you are using JavaScript in the client and Java on the server. I mean, you could be using Dalvik or C# for all I know. – Old Pro Apr 23 '12 at 04:02
  • @OldPro: It's definitely C#, not Java. Look at the casing of "string", the method names etc. – Jon Skeet Apr 23 '12 at 05:51
  • Yup. Definitely C#. I didn't add tags regarding language as my question already contains 4! Also, I kind of suspected that the root cause of my issue is my lack of understanding of the conversions going on in the code. I'll do some investigation in the coming days and report back. – Mr Moose Apr 23 '12 at 06:29
  • @Jon, I don't know C# at all. Didn't catch the subtle differences in my cursory read, which was my point. Mr Moose, I'm not saying you need to add the languages in the tags, but just as you said "My client side code is a javascript function based on crypto-js" I would have appreciated it if you had said "My server side code is C# as follows". That's what I was asking for. Sorry for not being clearer. – Old Pro Apr 23 '12 at 06:41

1 Answers1

3

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...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • That looks right to me. Weirdness in the Java code to make up for mistake in the JavaScript code. Good catch. – Old Pro Apr 23 '12 at 04:00
  • @Jon. Thanks for your suggestion. I'll take a look in the coming days and report back. I suspect you are right though, just taking a look at the code for [HMAC.js](http://code.google.com/p/crypto-js/source/browse/branches/2.x/src/HMAC.js), it converts the key to bytes if it's passed in as a string. I should have looked closer from the outset. – Mr Moose Apr 23 '12 at 06:41