[英]Hash attack: find strings of length 2^N with same hashCode()
Robert Sedgewick和Kevin Wayne的閱讀算法第四版我發現了以下問題:
散列攻擊:找到2 ^ N個字符串,每個長度為2 ^ N,具有相同的hashCode()值,假設String的hashCode()實現如下:
public int hashCode() {
int hash = 0;
for (int i = 0; i < length(); i++)
hash = (hash * 31) + charAt(i);
return hash;
}
強烈提示:Aa和BB具有相同的值。
我想到的是生成所有可能的長度為2 ^ N的字符串並比較它們的hashCodes。 然而,對於大N而言,這是非常昂貴的,我懷疑它是正確的解決方案。 你能給我一些提示嗎?
. 安德烈亞斯和Glains的答案都是正確的,但他們都不太你所需要的,如果你的目標是產生2個N個不同的長度為2 的字符串。
相反,更簡單的方法是構建僅由Aa
和BB
的級聯序列組成的字符串。 對於長度2×1,你有{ Aa
, BB
}; 對於長度2×2,你有{ AaAa
, AaBB
, BBAa
, BBBB
},長度為2×3你有{ AaAaAa
, AAaaBB
, AaBBAa
, AaBBBB
, BBAaAa
, BBAaBB
, BBBBAa
, BBBBBB
}; 等等。
; but if it is indeed asking for length 2 N , then you can simply drop elements as you proceed.) (注意:你引用的文字說的是字符串的長度應該是2 N.我猜你錯了,實際上它要求長度為2 ;但是如果確實要求長度為2 N ,那么你可以只需在繼續操作時刪除元素。)
“強烈暗示”解釋道。
強烈提示:Aa和BB具有相同的值。
在ASCII / Unicode中, B
的值比A
高A
。 由於這些是第二個最后一個字符,因此該值乘以31
,因此當您將xxxxAa
更改為xxxxBa
時,哈希碼將增加31
。
要抵消這一點,您需要將最后一個字符偏移-31
。 由於小寫字母比大寫字母高32,因此將a
更改為A
為-32
,將一個字母更改為B
則為-31
。
因此,它獲得相同的哈希碼,將倒數第二個字母更改為下一個字母(例如A
到B
),並將最后一個字母從小寫更改為下一個大寫(例如a
到B
)。
您現在可以使用該提示生成最多26個具有相同哈希碼的字符串。
讓我們看一下hashCode()
實現和給定的提示:
public int hashCode() {
int hash = 0;
for (int i = 0; i < length(); i++)
hash = (hash * 31) + charAt(i);
return hash;
}
我們知道Aa
和BB
產生相同的hash
,我們可以很容易地驗證:
(65 * 31) + 97 = 2112
(66 * 31) + 66 = 2112
從這里開始,兩個輸入的hash
值相同。 也就是說,我們可以輕松地將任意數量的字符附加到兩個字符串,並且您將始終獲得相同的值。
一個例子可能是:
hashCode("AaTest") = 1953079538
hashCode("BBTest") = 1953079538
因此,您可以通過向兩個字符串附加相同的字符序列來生成足夠的哈希值,更正式地:
hashCode("Aa" + x") = hashCode("BB" + x)
關於您的想法的另一個注意事項,即生成所有可能的字符串並搜索重復項。 看看bithday悖論 ,你會注意到為不同的輸入找到重復的哈希值需要更少的時間。
仔細看看哈希函數,它就像一個數字系統(例如十六進制),其中數字的權重是31.也就是說,把它想象為將數字轉換為基數31,這使得你的最終哈希碼類似於hashCode = (31^n) * first-char + (31^n-1) * second-char + ..... + (31^0) * last-char
第二個觀察是大寫字母和小寫字母之間的ASCII距離是32.根據哈希函數來解釋,這意味着當你用一個小寫字母替換一個大寫字母時,這意味着你要向更高的字母添加1個以上的大寫字母。數字和1到您當前的數字。 例如:
BB = (31)(B) + (31^0)B
也等於(31)*(B - 1) + (31^0)*(31 + B)
注意我剛從較高的一個單位拿走了一個單位數字並添加到較低位而不改變整體值。 最后的等式等於(31)*(A) + (a) == Aa
因此,要生成給定哈希碼的所有可能的字符串,請從初始字符串開始,並通過將大字符替換為大寫字符從右向左移動字符,同時從較高位置(如果適用)減少一個字符。 你可以在O(1)中運行它
希望這可以幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.