Today I was working with
RFC2195: IMAP/POP AUTHorize Extension for Simple Challenge/Response and need to implement
RFC2104: HMAC: Keyed-Hashing for Message Authentication. I thought that this would be as simple as using the .NET Frameworks' class
System.Security.Cryptography.HMACMD5, but I was wrong. I couldn't get these two to match within the few minutes that I wrote the code. So I implemented my own version, which consquently works for key sizes <= 64.
List<byte> sharedkey = new List<byte>(Convert.FromBase64String("PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+"));
List<byte> secret = new List<byte>(64);
secret.AddRange(System.Text.ASCIIEncoding.ASCII.GetBytes("tanstaaftanstaaf"));
while (secret.Count < 64) secret.Add(0);
HMAC hmac = new HMACMD5(sharedkey.ToArray());
List<byte> dotNet = new List<byte>(hmac.ComputeHash(secret.ToArray()));
List<byte> opad = new List<byte>(128);
List<byte> ipad = new List<byte>(64 + sharedkey.Count);
for (int i = 0; i < 64; i++)
{
opad.Add((byte)(0x5c ^ secret[i]));
ipad.Add((byte)(0x36 ^ secret[i]));
}
ipad.AddRange(sharedkey);
MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
opad.AddRange(md5.ComputeHash(ipad.ToArray()));
List<byte> tmp = new List<byte>(md5.ComputeHash(opad.ToArray()));
string s1 = "";
string s2 = "";
tmp.ForEach(delegate(byte b) { s1 += b.ToString("x"); });
dotNet.ForEach(delegate(byte b) { s2 += b.ToString("x"); });
tmp.InsertRange(0, System.Text.ASCIIEncoding.ASCII.GetBytes("tim "));
string encodedKeyedHash = Convert.ToBase64String(tmp.ToArray());
Output:
s1=b913a62c7eda7a495b4e6e7334d3890
s2=f641695cabad9c59dde20daf93483db
encodedKeyedHash=dGltILkTpgLH7aeklbTm5zNNOJA=