【问题标题】:Integrity of a hashmap sent over network通过网络发送的哈希图的完整性
【发布时间】:2026-02-13 18:40:02
【问题描述】:

我的 servlet 加密并发送一个 HashMap 及其 MD5 哈希。
客户端然后接收它们,并将 MD5 与它从 HashMap 中计算出来的 MD5 进行比较。

这有时有效,但有时无效,例如,如果 HashMap 是:

    HashMap<String, Object> result = new HashMap<String, Object>();
    result.put("error", 0);
    result.put("coun", 0);

有效

但是,如果是:

    HashMap<String, Object> result = new HashMap<String, Object>();
    result.put("error", 0);
    result.put("count", 0);

它不起作用 - 两个 MD5 哈希值不同。 (唯一的区别是键 'count' 而不是 'coun')

该程序发送类似的 HashMaps,所有包含的键/值都只是字符串或整数,这是我第一次看到这样奇怪的东西。

HashMap/MD5 实际发送方式的详细信息 -

服务器会:

    //Work out MD5 of the HashMap result (convert it to bytes with objectOutputStream, and MD5 the bytes)
    MessageDigest messageDigest = MessageDigest.getInstance("MD5");
    ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
    ObjectOutputStream out = new ObjectOutputStream(bos) ;
    out.writeObject(result);
    out.close();
    byte[] md5 = messageDigest.digest(bos.toByteArray();

    //Encrypt the httpURLConnection response stream, and send the HashMap result and the md5 over the stream
    Cipher symmetricCipher = Cipher.getInstance("DES");
    symmetricCipher.init(Cipher.ENCRYPT_MODE, symmetricKey);
    CipherOutputStream cipherOutput = new CipherOutputStream(response.getOutputStream(), symmetricCipher);
    BufferedOutputStream bufferedOutput = new BufferedOutputStream(cipherOutput);
    ObjectOutputStream objectOutput = new ObjectOutputStream(out);
    objectOutput.writeObject(result);
    objectOutput.writeObject(md5);
    objectOutput.flush();

客户这样做:

    //Decrypt the httpURLConnection response stream
    Cipher symmetricCipher = Cipher.getInstance("DES");
    symmetricCipher.init(Cipher.DECRYPT_MODE, symmetricKey);
    CipherInputStream cipherInput = new CipherInputStream(httpInput, symmetricCipher);
    BufferedInputStream bufferedInput = new BufferedInputStream(cipherInput);           

    //read HashMap and MD5
    ObjectInputStream objectInput = new ObjectInputStream(in);
    HashMap<String, Object> result = (HashMap<String, Object>) objectInput.readObject();
    byte[] hash1 = (byte[]) objectInput.readObject();

    //workout hash of the Hashmap received.
    MessageDigest messageDigest = MessageDigest.getInstance("MD5");
    ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
    ObjectOutputStream out = new ObjectOutputStream(bos) ;
    out.writeObject(result);
    out.close();
    byte[] hash2 = messageDigest.digest(bos.toByteArray();

    // Compare two hashes
    if (!Arrays.equals(hash1, hash2)) {
        System.out.println("Result received does not match hash, stopping list operation");
        return;
    }

使用相同类型的inputStreams解密,以相同的方式计算出hashmap的md5,然后比较使用:

    if (!Arrays.equals(hash1, hash2)) {
            System.out.println("Result received does not match hash, stopping get operation");
            return;
    }

我不明白为什么这对于发送我尝试过的所有 HashMap 都有效,但现在无法使用此计数键。 我已经测试了比较客户端和 servlet 上 HashMap 中的各个键/值对,它们是相同的,但是在比较整个 HashMap 的两个 MD5 时,它们不匹配。

另外,我不确定我是否在流链的正确部分使用缓冲流?

【问题讨论】:

  • 你说“这两个 MD5 哈希是不同的”,你这里说的是哪两个 MD5 哈希,具体来说

标签: security stream hashmap equals digest


【解决方案1】:

不能保证 Java 的两个副本会产生与一个对象的序列化完全相同的字节 - 只是它们会产生一个语义上等效的对象。

您可以通过对键和值运行摘要来完成类似的工作,但您需要决定如何散列 Object 值。

如果您可以呈现为 XML,并将其规范化,那么您可以从那里开始。

您可以查看各种网络服务安全标准,而不是自行制定。

建议:将哈希映射复制到具有顺序的 TreeMap 中,然后尝试相同的技巧。

【讨论】:

  • thx,只是假设有一个保证,因为到目前为止它一直适用于许多不同的哈希图 - 认为没关系,因为它们只包含整数和字符串......知道为什么这个特定的哈希图是它停止工作的那个吗?必须自己动手,这是为了完成一项任务
  • 不保证,是不是按照未定义的顺序在地图上行走。
  • 好的,我会尝试树图,否则我只会对哈希图中的内容进行 MD5,而不是序列化字节...谢谢
【解决方案2】:

可能只是从一个 JVM 发送带有键值对的 JSON,在接收方,您可以反序列化并构造另一个 HashMap

【讨论】: