使用整数作为键和值的HashMap需要很长时间才能完成工作

时间:2018-11-23 22:53:35

标签: java hashmap

我正在对2DES进行中间相遇攻击。我已经实现了DES加密/解密,并且可以正常工作。我试图实现此目的的方法是在for循环中存储中间密码(作为HashMap的密钥)和可能的密钥(作为HashMap的值)。两者均为整数-以byte []开头,但发现键不能为数组。但是,在此for循环中,我还想确保可能的键是唯一的,即我有一个while循环,以确保HashMap的大小为2 ^ 20(仅DES键的20位有效)。在后端,我尝试通过使用foreach遍历HashMap来找到具有匹配的中间密文的密钥,然后将加密中的每个中间密文与解密中的中间密文进行比较。

但是,我找不到匹配项,因为它需要很长时间才能完成。我一直在等20分钟而没有任何结果。

while (intermediateCipher.size() < Math.pow(2, 20)) {
            byte[] key = generateDesKey();

            intermediateCipher.put(ByteBuffer.wrap(encrypt(key, plainText)).getInt() , ByteBuffer.wrap(key).getInt());
        } 

        int count = 0;
        for (Entry<Integer, Integer> arr : intermediateCipher.entrySet()) {
            byte[] k2 = ByteBuffer.allocate(8).putInt(arr.getValue()).array();
            int temp = ByteBuffer.wrap(decrypt(k2, cipherText2)).getInt();
            if (intermediateCipher.containsKey(temp)) {
                count++;
                byte[] k1 = ByteBuffer.allocate(8).putInt(intermediateCipher.get(temp)).array();

                if (encrypt(k2, encrypt(k1, plainText)) == cipherText2) {
                    System.out.println("Key is " + k1 + " " + k2);
                }
            }
        }

intermediateCipher是我的HashMap。

PlainText是8字节的byte [],cipherText2是plainText上2DES的加密结果,generateDesKey是生成64位的方法,其中跳过奇偶校验位以确保20位有效,并根据DES的要求将这些位转换为byte []

2 个答案:

答案 0 :(得分:1)

阅读您的代码后,我有几点建议可以对其进行优化:

  1. 最重要:如果您只能访问一次,则不要浪费时间访问两次地图:
    if (intermediateCipher.containsKey(temp)) {
        byte[] k1 = intermediateCipher.get(temp);
        ...
    }

...缩小为:

    byte[] k1 = intermediateCipher.get(temp);
    if (k1!=null) {
        ...
    }
  1. 循环中分配了太多的内存,因为创建仅用于临时操作的新ByteBuffer然后丢弃它(无用的GC过多工作)是没有用的。如果确定使用的字节缓冲区长度为8(或更短),则可以在第一个循环中使用单个缓冲区

    ByteBuffer tempBuffer=ByteBuffer.allocate(8);
    while (intermediateCipher.size() < Math.pow(2, 20)) {
        // Note: A call to rewind() must be done before each put/get operation on the buffer:
        byte[] key = generateDesKey();
        tempBuffer.rewind();
        tempBuffer.put(encrypt(key, plainText));
        tempBuffer.rewind();
        int mapKey=tempBuffer.getInt();
        tempBuffer.rewind();
        tempBuffer.put(key);
        tempBuffer.rewind();
        int mapValue=tempBuffer.getInt();
        intermediateCipher.put(mapKey, mapValue);
    }
    

在第二个循环中,可以完成类似的转换。

  1. 正如@Thilo在评论中建议的那样,始终以期望的最大大小为函数对地图进行预先大小调整始终是一个好习惯。它应该像这样: Map<...> intermediateCipher=new HashMap<...>((int)(1.7d * expectedSize));

  2. 循环while (intermediateCipher.size() < Math.pow(2, 20))应该简化为 int len=Math.pow(2, 20); for (int i=0;i<len;i++)

更新

  1. 如果调用generateDesKey()在每次迭代中返回相同的值,则可以在循环之前 进行设置。

答案 1 :(得分:0)

有些事情您可以尝试缩短运行时间。

首先,从HashMap切换到TreeMap。当HashMap变得太大(如2 ^ 20)时,由于哈希表中的所有时隙都充满了大量条目,因此在最坏的情况下,搜索预计会从O(1)到O(n)。但是,TreeMap搜索始终始终在O(lg n)中运行。

第二,从Map<Integer, Integer>切换到Map<Integer, byte[]>。您只需要将Map键转换为整数即可;您可以将值保留为字节数组,从而大大减少byte[] -> ByteBuffer -> int的转换。

Map<Integer, byte[]> intermediateCipher = new TreeMap<>();

while (intermediateCipher.size() < Math.pow(2, 20)) {
    byte[] key = generateDesKey();
    int encrypted = ByteBuffer.wrap(encrypt(key, plainText)).getInt();
    intermediateCipher.put(encrypted, key);
} 

int count = 0;
for (Entry<Integer, byte[]> arr : intermediateCipher.entrySet()) {
    byte[] k2 = arr.getValue();
    int temp = ByteBuffer.wrap(decrypt(k2, cipherText2)).getInt();

    if (intermediateCipher.containsKey(temp)) {
        count++;
        byte[] k1 = intermediateCipher.get(temp);
        if (encrypt(k2, encrypt(k1, plainText)) == cipherText2) {
            System.out.println("Key is " + k1 + " " + k2);
        }
    }
}