实施改进的HMAC

时间:2016-06-12 19:55:20

标签: c# cryptography hmac

所以我试图为了学术目的而实现HMAC function的小型修改版本。以下是Wikipedia提供的算法:

function hmac (key, message)
    if (length(key) > blocksize) then
        key = hash(key) // keys longer than blocksize are shortened
    end if
    if (length(key) < blocksize) then
        // keys shorter than blocksize are zero-padded (where ∥ is concatenation)
        key = key ∥ [0x00 * (blocksize - length(key))] // Where * is repetition.
    end if

    o_key_pad = [0x5c * blocksize] ⊕ key // Where blocksize is that of the underlying hash function
    i_key_pad = [0x36 * blocksize] ⊕ key // Where ⊕ is exclusive or (XOR)

    return hash(o_key_pad ∥ hash(i_key_pad ∥ message)) // Where ∥ is concatenation
end function

这是我写的代码(显然这给了我不恰当的结果,我检查了给定的解决方案)。 块大小为512位。

我想我已经确定了我的代码失败的一个关键原因,我没有实现密钥的填充逻辑,因为我被要求放置前导零而不是像原始算法那样尾随零

我真的不知道使用前导零填充是否会对数学产生任何影响,或者如何使用它

其次,我不确定我是否正确地进行字节数学运算。

这是我的代码:

private static string HMAC(string message)
{
    //Every charecter corresponds to 4bits.
    long hex_key = 0x0a7cb27e52;

    //padding for an 64 Bit Key. 2 Chars  = 1 Byte. Leading zeros I need (512-(10*4))/4 = 118
    string asc_key = "0a7cb27e52";
    //Did Not KnowWhere To Use
    asc_key = asc_key.PadLeft(128, '0');

    byte[] keyInBytes = Encoding.UTF8.GetBytes(asc_key);
    var hexString = BitConverter.ToString(keyInBytes);
    hexString = hexString.Replace("-", "");

    //Wikipedia Style naming

    long o_key_pad = (0x5c * 0x40) ^ hex_key;
    long i_key_pad = (0x36 * 0x40) ^ hex_key;
    return GetMD5Hash(o_key_pad.ToString() + GetMD5Hash(i_key_pad.ToString() + message));
}


public static String GetMD5Hash(String TextToHash)
{
    //Check wether data was passed
    if ((TextToHash == null) || (TextToHash.Length == 0))
    {
        return String.Empty;
    }

    //Calculate MD5 hash. This requires that the string is splitted into a byte[].
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] textToHash = Encoding.Default.GetBytes(TextToHash);
    byte[] result = md5.ComputeHash(textToHash);

    //Convert result back to string.
    return System.BitConverter.ToString(result);
}

我做错了哪里?

1 个答案:

答案 0 :(得分:1)

  

我没有为密钥实现填充逻辑,因为我是   要求放置前导零而不是原始的尾随零   算法

     

而且我真的不知道使用前导零填充是否会产生任何效果   数学的差异,或如何使用它

它不会改变数学中的任何内容,但结果会有所不同。

  

其次,我不确定我是否正确地进行字节数学运算。

你没有正确地做到这一点。您的代码存在很多问题:

  • 密钥可能有任何长度,并且密钥长度超过64位是常见的,这将超过long的大小。

  • o_key_padi_key_pad不会重复填写块大小。

  • HMACGetMD5Hash接收并传送string个数据。加密操作应该对二进制数据执行,而不是对字符串执行。

代码:

private static int BlockSize = 64;

public static byte[] HMAC(byte[] message)
{
    byte[] key = {0x0a, 0x7c, 0xb2, 0x7e, 0x52};
    if (key.Length > BlockSize) {
        key = GetMD5Hash(key);
    }
    byte[] paddedKey = new byte[BlockSize];
    key.CopyTo(paddedKey, BlockSize-key.Length);

    byte[] o_key_pad = new byte[BlockSize];
    byte[] i_key_pad = new byte[BlockSize];
    for(int i = 0; i < BlockSize; i++) {
        o_key_pad[i] = (byte)(0x5c ^ paddedKey[i]);
        i_key_pad[i] = (byte)(0x36 ^ paddedKey[i]);
    }

    byte[] inner_hash = GetMD5Hash(concat(i_key_pad, message));
    return GetMD5Hash(concat(o_key_pad, inner_hash));
}

private static byte[] concat(byte[] a1, byte[] a2) {
    byte[] res = new byte[a1.Length + a2.Length];
    a1.CopyTo(res, 0);
    a2.CopyTo(res, a1.Length);
    return res;
}

private static byte[] GetMD5Hash(byte[] ToHash)
{
    MD5 md5 = new MD5CryptoServiceProvider();
    return md5.ComputeHash(ToHash);
}

您可以围绕HMAC编写一个包装来接收并传递string