当前位置: 首页 > 知识库问答 >
问题:

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

彭华皓
2023-03-14
hmacSHA256digest(  "secret".getBytes("UTF-8"), msg) = "244d9c89069406d40803722ec6a793e5e04c55234d9ca03039a7b505cb3f8f00"
hmacSHA256digest("secret\1".getBytes("UTF-8"), msg) = "4f94305c91ca9d8dec13ffcff7e455d6f0c49373e1bbc4035da2b500b11063fb" 
  • “秘密”
  • “机密\0”
  • “机密\0\0”

因此,JavaX HMAC SHA256返回的是相同的散列,尽管从getBytes(“UTF-8”)返回的byte[]数组在结尾处只有几个额外的零(所以这不是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"

对于MD5和普通SHA256的其他JavaX方法的调用,当额外的\0 s附加到秘密时,不会返回相同的哈希,因此它们通过了我们的安全测试用例,以验证不同秘密的哈希唯一性。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个答案

仲和韵
2023-03-14

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

理想情况下,密钥的大小应与基础哈希算法的块大小相同。对于SHA-256,块大小是512位,所以密钥应该是64字节。

从RFC2104来看,如果一个键长于块大小,那么它将通过哈希函数传递并使用哈希作为键来缩短。如果键短于块大小,则将通过追加零来扩展键。

 类似资料:
  • 我已经完成了一个android应用程序的开发,该应用程序使用facebook sdk与facebook集成。目前,我对这个应用程序没有任何问题需要解决,因为它功能齐全,运行良好——但尽管如此,我相信即使应用程序正在运行,我仍然不完全理解Android密钥散列是什么 1)据我所知,facebook sdk为开发者提供了一个独特的应用程序id,以了解他们与谁“交谈”,这样他们也可以控制谁是谁之类的人,

  • keytool-exportcert-alias diego-keystore“c:\users\diego\desktop\celuchat.keystore”“c:\openssl\bin\openssl.exe”sha1-binary“c:\openssl\bin\openssl.exe”base64 celuchat.keystore是我在导出签名的应用程序时使用的密钥库,当keytool

  • 我有一个哈希映射类型数组列表: 它的一些ID和名称如下: 这是用于循环的代码: 问题是在这一行中: 显示正确的ID和Name,如下所示: 但只有名称可用与条目: 总是返回文本“name”,它是键和返回值“Katie Bailey”。 我的问题是,为什么我不能在进入时拿到钥匙。getKey()?为什么每次迭代都要用“name”作为键?

  • 有可能像这样填充哈希图吗? 或者还有比这更正确(或更好)的方法吗?因为当我试图打印“地图”时,键“选项”没有任何价值。 预先感谢

  • 问题内容: 我不是在谈论String类或哈希码被覆盖的任何其他类。假设如果我只是创建该类的新对象,那么在任何情况下the或true 都将返回该对象的内存地址? 问题答案: 不必要。从文档(重点是我的): 在合理可行的范围内,由Object类定义的hashCode方法确实为不同的对象返回不同的整数。(通常通过将对象的内部地址转换为整数来 实现 , 但是JavaTM编程语言不需要这种实现技术 。)

  • 问题内容: 通过阅读的ECMAScript 5.1规范,和被区分。 那为什么要评估? 问题答案: JavaScript使用IEEE754标准表示数字。: 有符号的零 为零,具有相关的符号。在普通算术中,-0 = +0 =0。但是,在计算中,某些数字表示形式允许存在两个零,通常用 -0(负零) 和 +0(正零)表示 。这在某些带符号的整数表示形式以及大多数浮点数表示形式中都会发生。数字0通常被编码为