用\ 0正确填充HMAC SHA256秘密密钥是否应该返回相同的哈希值?

时间:2018-08-16 18:11:38

标签: security sha256 hmac zero-padding

在使用JavaX HMAC / SHA256哈希库时,如果我用非零字节填充私钥,则同一条消息的哈希将有所不同;如预期的那样。

hmacSHA256digest(  "secret".getBytes("UTF-8"), msg) = "244d9c89069406d40803722ec6a793e5e04c55234d9ca03039a7b505cb3f8f00"
hmacSHA256digest("secret\1".getBytes("UTF-8"), msg) = "4f94305c91ca9d8dec13ffcff7e455d6f0c49373e1bbc4035da2b500b11063fb" 

但是,如果我用任意数量的\ 0字节右键填充私钥,则对于不同的字节数组,像下面这样返回哈希值:

  • “秘密”
  • “秘密\ 0”
  • “秘密\ 0 \ 0”

因此,即使从秘密的getBytes(“ UTF-8”)返回的 byte []数组仅有几个其他的零,JavaX HMAC SHA256也会返回相同的哈希最后(因此这不是UTF-8问题)

hmacSHA256digest(   "secret".getBytes("UTF-8"), msg) 
= "244d9c89069406d40803722ec6a793e5e04c55234d9ca03039a7b505cb3f8f00"

hmacSHA256digest(   "secret\0".getBytes("UTF-8"), msg) 
= "244d9c89069406d40803722ec6a793e5e04c55234d9ca03039a7b505cb3f8f00"

hmacSHA256digest(   "secret\0\0".getBytes("UTF-8"), msg) 
= "244d9c89069406d40803722ec6a793e5e04c55234d9ca03039a7b505cb3f8f00"

在将额外的\ 0附加到机密时,对MD5和纯SHA256的其他JavaX方法的调用不会返回相同的哈希,因此它们通过了我们的安全性测试案例,以确保跨不同机密的哈希唯一性。带有MAC / SHA256的零填充秘密案例的失败是否可能是攻击的载体?

这是示例代码:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

static void testRightZeroPaddedSecretsHaveDifferentHashes() {
    try {
        byte[] msg = "msg".getBytes("UTF-8");

        // HMAC SHA256
        byte[] b3 = hmacSHA256digest(msg, "secret".getBytes("UTF-8"));
        byte[] b4 = hmacSHA256digest(msg, "secret\0".getBytes("UTF-8"));

        // Plain SHA256
        byte[] b5 = SHA256digest(msg, "secret".getBytes("UTF-8"));
        byte[] b6 = SHA256digest(msg, "secret\0".getBytes("UTF-8"));

        boolean same34 = Arrays.equals(b3, b4);
        boolean same56 = Arrays.equals(b5, b6);
        System.out.println(
                "\n" + Arrays.toString(b3) +
                "\n" + Arrays.toString(b4) +
                "\nHMAC SHA256 - identical hash results? = " + same34 +
                "\n" +
                "\n" + Arrays.toString(b5) +
                "\n" + Arrays.toString(b6) +
                "\nPlain SHA256 - identical hash results? = " + same56
        );
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

static byte[] hmacSHA256digest(byte[] msg, byte[] secret) {
    try {
        SecretKeySpec keySpec = new SecretKeySpec(secret, "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(keySpec);
        byte[] hmac = mac.doFinal(msg);
        return hmac;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
    return null;
}

static byte[] SHA256digest(byte[] msg, byte[] secret) {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        digest.update(msg);
        byte[] hash = digest.digest(secret);
        return hash;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return null;
}

和示例输出:

[-2, 79, -100, 65, -113, 104, 63, 3, 79, 106, -7, 13, 29, -43, -72, 106, -64, 53, 93, -39, 99, 50, -59, -100, -57, 69, -104, -48, 115, 97, 7, -10] 
[-2, 79, -100, 65, -113, 104, 63, 3, 79, 106, -7, 13, 29, -43, -72, 106, -64, 53, 93, -39, 99, 50, -59, -100, -57, 69, -104, -48, 115, 97, 7, -10] 
HMAC SHA256 - identical hash results? = true

[-88, 92, 89, -29, -65, -48, -127, 51, 125, -120, 78, -38, 25, 57, -91, 91, -50, 111, -33, 40, -3, 0, -95, 89, -50, -88, 39, 118, 101, -56, 91, 126] 
[-40, 39, 49, -64, 58, 40, 124, 64, 110, -100, 50, 115, -32, 114, -107, 24, -73, -17, -37, 11, 67, -26, -48, -65, 109, -24, 119, 45, 74, -31, -81, 119]
Plain SHA256 - identical hash results? = false

由于JavaX HMAC SHA256无法通过针对上述普通SHA256 / MD5算法通过的零填充秘密测试用例,因此谁能解释这种行为的差异,是否可以加以利用?

1 个答案:

答案 0 :(得分:1)

根据设计,这是HMAC构造的正确行为。

理想情况下,密钥的大小应为基础哈希算法的块大小。对于SHA-256,块大小为512位,因此您的密钥应为64字节。

RFC 2104开始,如果密钥的长度大于块大小,则可以通过将其传递给哈希函数并使用哈希作为密钥来缩短密钥。 如果密钥短于块大小,则将通过添加零来扩展密钥。

这是HMAC算法的第一步:

  

(1)在K的末尾附加零以创建B字节字符串   (例如,如果K的长度为20个字节,且B = 64,则K将附加44个零字节0x00)

RFC的建议是使用至少为哈希函数输出大小的键,在您的情况下为32个字节。即使这仍然会使测试用例失败,该测试用例可以用0填充键并产生相同的HMAC。