Java 中的維吉尼亞密碼
一、簡介
在本文中,我們將研究維吉尼亞密碼。我們將了解該密碼是如何運作的,然後,我們將學習如何在 Java 中實作和反向它。
2.什麼是維吉尼亞密碼?
維吉尼亞密碼是經典凱撒密碼的變體,只是將每個字母移動不同的量。
在凱撒密碼中,我們將明文中的每個字母移動相同的數量。例如,如果我們將每個字母移動三位,那麼字串“BAELDUNG”
將變成“EDHOGXQJ”
:
反轉這個密碼只是將字母往相反方向移動相同的量。
維吉尼亞密碼完全相同,只是我們將每個字母移動不同的數量。這意味著我們需要一種方法來表示密鑰——每個字母移動的數量。這通常由另一串字母表示,每個字母根據其在字母表中的位置對應於要移動的量。
例如,鍵“HELLO”
意味著將第一個字母移動八個字符,將第二個字母移動五個字符,依此類推。因此,使用此鍵,字串“BAELDUNG”
現在將變為“JFQXSCSS”
:
3. 實施**維吉尼亞密碼**
現在我們已經了解了維吉尼亞密碼的工作原理,接下來讓我們看看如何在 Java 中實現它。
我們將從將成為我們的密碼的方法開始:
public String encode(String input, String key) {
String result = "";
for (char c : input.toCharArray()) {
result += c;
}
return result;
}
正如所寫,這種方法不是很有用。它只是建立一個與輸入字串相同的輸出字串。
我們要做的下一件事是從密鑰中獲取用於每個輸入字元的字元。我們將為接下來要使用的關鍵位置設定一個計數器,然後在每次傳遞時遞增該計數器:
int keyPosition = 0;
for (char c : input.toCharArray()) {
char k = key.charAt(keyPosition % key.length());
keyPosition++;
.....
}
這看起來有點可怕,所以讓我們解決它。首先,我們應用密鑰位置與總密鑰長度的模,這僅僅意味著當它到達字串末尾時它會迴繞。然後我們獲取該位置的字符,這是輸入字串該位置的關鍵字符。
最後,我們需要根據關鍵字元調整輸入字元:
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
.....
int charIndex = characters.indexOf(c);
int keyIndex = characters.indexOf(k);
int newCharIndex = (charIndex + keyIndex + 1) % characters.length();
c = characters.charAt(newCharIndex);
在這裡,我們有一個包含我們可以支援的所有字元的字串。然後,我們在該字串中找到輸入字元和關鍵字元的索引。然後我們可以簡單地將它們加在一起以獲得新的角色位置。
請注意,我們還要為此添加 1,因為我們希望密鑰字母是一索引而不是零索引。也就是說,鍵中的“A”
應該充當移動一個字母的作用,而不是移動零個字母。我們還對可能的字元數進行了另一個取模,以便再次環繞。
然而,這並不完全有效。特別是,只有當輸入字串和金鑰中的每個字元都在我們支援的列表中時,這才能正確運作。為了解決這個問題,我們只需要在輸入字元受支援的情況下增加鍵位置,並且僅在輸入字元和鍵字元都受支援時才產生新字元:
if (charIndex >= 0) {
if (keyIndex >= 0) {
int newCharIndex = (charIndex + keyIndex + 1) % characters.length();
c = characters.charAt(newCharIndex);
}
keyPosition++;
}
至此,我們的密碼就完成了並且可以工作了。
3.1.工作示範
現在我們已經有了密碼的有效實現,讓我們看看它的實際效果。我們將使用金鑰“BAELDUNG”
對字串“VIGENERE CIPHER IN JAVA”
進行編碼。
我們從keyPosition
0 開始。我們的第一次迭代給出:
-
c
=“V”
-
k
=“B”
-
charIndex
= 21 -
keyIndex
= 1 -
newCharIndex
= 23
這給了我們結果字元“X”
。
我們的第二次迭代將是:
-
keyPosition
= 1 -
c
=“I”
-
k
=“A”
-
charIndex
= 8 -
keyIndex
= 0 -
newCharIndex
= 9
這給了我們一個結果字元“ J
”。
讓我們繼續前進。看看第九個字符:
-
keyPosition
= 9 -
c
=” “
-
k
=“B”
。 -
charIndex
= -1 -
keyIndex
= 1
這裡有兩件有趣的事。首先,我們注意到我們的關鍵位置已經超過了關鍵的長度,所以我們再次回到起點。其次,我們的輸入字元不受支持,因此我們將跳過對該字元的編碼。
如果我們繼續下去,我們最終會得到“XJLQRZFL EJUTIM WU LBAM”
。
4. 解碼**維吉尼亞密碼**
現在我們可以使用維吉尼亞密碼對事物進行編碼,我們也需要能夠對其進行解碼。
也許毫不奇怪,絕大多數解碼演算法與編碼相同。畢竟,我們只是根據正確的關鍵位置將字元移動一定的量。
不同的部分是我們需要移動的方向。我們的newCharIndex
需要使用減法而不是加法來計算。但是,我們還需要手動進行模計算,因為它在這個方向上無法正常工作:
int newCharIndex = charIndex - keyIndex - 1;
if (newCharIndex < 0) {
newCharIndex = characters.length() + newCharIndex;
}
有了這個演算法版本,我們現在可以成功地逆向密碼了。例如,讓我們嘗試使用鍵“BAELDUNG”
解碼之前的字串“XJLQRZFL EJUTIM WU LBAM”
。
和以前一樣,我們的關鍵位置從 0 開始。我們的第一個輸入字元是“X”
,即字元索引 23,第一個關鍵字元是“B”
,即字元索引 1。
我們的新字元索引是 23 – 1 – 1,即 21。轉換回來後就是一個“V”
。
如果我們繼續處理整個字串,我們將得到“VIGENERE CIPHER IN JAVA”
的結果。
5.**維吉尼亞密碼調整**
現在我們已經了解如何在 Java 中實作維吉尼亞密碼,讓我們來看看可以進行的一些調整。我們不打算在這裡實際實現它們——這是留給讀者的練習。
對密碼所做的任何更改都需要在編碼和解碼端同等完成,否則,編碼的訊息將不可讀。這也意味著任何不知道我們所做的更改的攻擊者將更難攻擊任何編碼訊息。
可以進行的第一個也是最明顯的變更是使用的字元集。我們可以添加更多受支援的字元——帶有重音符號、空格等的字元。然後,這將允許在輸入字串中使用更廣泛的字元集,並且它將調整所有字元在輸出字串中的表示方式。例如,如果我們簡單地添加一個空格作為允許的字符,則使用鍵“BAELDUNG”
編碼“VIGENERE CIPHER IN JAVA”
會得到“XJLQRZELBDNALZEGKOEVEPO”
。
我們也可以更改映射字串中字元的順序。如果它們不嚴格按字母順序排列,一切仍然有效,但結果會有所不同。例如,如果我們將字串更改為“JQFVHPWORZSLNMKYCGBUXIEDTA”
,則使用鍵“HELLO”
對字串“BAELDUNG”
進行編碼現在會產生“DERDPTZV”
而不是“JFQXSCSS”
。
我們可以進行大量其他更改,但這些更改往往會使我們遠離真正的維吉尼亞密碼。例如,我們可以交換編碼和解碼演算法——透過在字母表中向後移動來編碼,透過向前移動來解碼。這通常稱為變體 Beaufort 。
六,結論
在這裡,我們了解了維吉尼亞密碼及其工作原理。我們也了解如何在 Java 中自行實作此功能以對訊息進行編碼和解碼。最後,我們看到了可以對演算法進行一些調整以提高其安全性。為什麼不嘗試自己實現其中一些呢?
與往常一樣,本文的完整程式碼可在 GitHub 上取得。