27、维吉尼亚密码破解全流程解析

维吉尼亚密码破解全流程解析

1. 确定最可能的密钥长度

在破解维吉尼亚密码时,首先要确定密钥的长度。可以使用 getMostCommonFactors() 函数来筛选出最可能的密钥长度。以下是相关代码及解析:

factorsByCount = []
for factor in factorCounts:
    # 排除大于 MAX_KEY_LENGTH 的因子
    if factor <= MAX_KEY_LENGTH:
        # factorsByCount 是一个元组列表:(因子, 因子计数)
        factorsByCount.append( (factor, factorCounts[factor]) )
# 对 factorsByCount 进行排序
factorsByCount.sort(key=getItemAtIndexOne, reverse=True)
return factorsByCount

上述代码中,首先遍历 factorCounts 中的每个因子,将小于等于 MAX_KEY_LENGTH 的因子及其计数组成元组添加到 factorsByCount 列表中。然后使用 sort() 方法对 factorsByCount 列表进行排序,按照因子的计数降序排列。最后返回排序后的列表,该列表表明哪些因子出现的频率最高,因此最有可能是维吉尼亚密码的密钥长度。

kasiskiExamination() 函数可以返回给定密文最可能的密钥长度列表,具体步骤如下:
1. 调用 findRepeatSequencesSpacings(ciphertext) 函数,找出密文中重复序列之间的间距,返回一个字典,键为序列字符串,值为间距整数列表。
2. 扩展 repeatedSeqSpacings 字典,将间距的因子存储在 seqFactors 字典中。具体代码如下:

seqFactors = {}
for seq in repeatedSeqSpacings:
    seqFactors[seq] = []
    for spacing in repeatedSeqSpacings[seq]:
        seqFactors[seq].extend(getUsefulFactors(spacing))
  1. 调用 getMostCommonFactors(seqFactors) 函数,返回一个二元整数元组列表,第一个整数表示因子,第二个整数表示该因子在 seqFactors 中出现的频率。
  2. factorsByCount 中提取因子,存储在 allLikelyKeyLengths 列表中。代码如下:
allLikelyKeyLengths = []
for twoIntTuple in factorsByCount:
    allLikelyKeyLengths.append(twoIntTuple[0])
return allLikelyKeyLengths
2. 获取使用相同子密钥加密的字母

为了提取密文中使用相同子密钥加密的字母,需要编写 getNthSubkeysLetters() 函数。该函数的步骤如下:
1. 使用正则表达式对象及其 sub() 方法,去除消息中的非字母字符。代码如下:

message = NONLETTERS_PATTERN.sub('', message)
  1. 构建一个字符串,将字母字符串添加到列表中,然后使用 join() 方法将列表合并为一个字符串。代码如下:
i = nth - 1
letters = []
while i < len(message):
    letters.append(message[i])
    i += keyLength
return ''.join(letters)
3. 尝试使用可能的密钥长度进行解密

attemptHackWithKeyLength() 函数用于尝试使用给定的密钥长度对密文进行解密。具体步骤如下:
1. 将密文转换为大写形式,存储在 ciphertextUp 变量中。

ciphertextUp = ciphertext.upper()
  1. 初始化 allFreqScores 列表,用于存储频率匹配分数。
allFreqScores = []
  1. 遍历每个子密钥,调用 getNthSubkeysLetters() 函数获取该子密钥对应的字母。
for nth in range(1, mostLikelyKeyLength + 1):
    nthLetters = getNthSubkeysLetters(nth, mostLikelyKeyLength, ciphertextUp)
  1. 对每个子密钥的字母进行解密,尝试所有 26 种可能的子密钥,计算解密后的文本与英语字母频率的匹配分数。
freqScores = []
for possibleKey in LETTERS:
    decryptedText = vigenereCipher.decryptMessage(possibleKey, nthLetters)
    keyAndFreqMatchTuple = (possibleKey, freqAnalysis.englishFreqMatchScore(decryptedText))
    freqScores.append(keyAndFreqMatchTuple)
  1. freqScores 列表进行排序,按照英语频率匹配分数降序排列。
freqScores.sort(key=getItemAtIndexOne, reverse=True)
  1. 将每个子密钥的前 NUM_MOST_FREQ_LETTERS 个元组添加到 allFreqScores 列表中。
allFreqScores.append(freqScores[:NUM_MOST_FREQ_LETTERS])
4. print() 函数的 end 关键字参数

print() 函数的 end 关键字参数可以指定在字符串末尾打印的内容,而不是换行符。例如:

def printStuff():
    print('Hello', end='\n')
    print('Howdy', end='')
    print('Greetings', end='XYZ')
    print('Goodbye')
printStuff()

上述代码中, print('Hello', end='\n') 正常打印字符串并换行; print('Howdy', end='') 打印字符串后不换行; print('Greetings', end='XYZ') 打印字符串后添加 XYZ 而不是换行符。

5. 程序的静默模式或向用户打印信息

如果 SILENT_MODE 常量设置为 False ,则会将 allFreqScores 中的值打印到屏幕上,显示每个子密钥的前三个候选字母。代码如下:

if not SILENT_MODE:
    for i in range(len(allFreqScores)):
        print('Possible letters for letter %s of the key: ' % (i + 1), end='')
        for freqScore in allFreqScores[i]:
            print('%s ' % freqScore[0], end='')
        print()
6. 寻找子密钥的可能组合

为了找到完整的密钥,需要尝试不同的子密钥组合。可以使用 itertools.product() 函数生成所有可能的子密钥组合。以下是相关代码及解析:

for indexes in itertools.product(range(NUM_MOST_FREQ_LETTERS), repeat=mostLikelyKeyLength):
    possibleKey = ''
    for i in range(mostLikelyKeyLength):
        possibleKey += allFreqScores[i][indexes[i]][0]
    if not SILENT_MODE:
        print('Attempting with key: %s' % (possibleKey))
    # 解密密文并检查是否为可读英语
    # ...

上述代码中, itertools.product(range(NUM_MOST_FREQ_LETTERS), repeat=mostLikelyKeyLength) 生成所有可能的索引组合,每个组合表示一个可能的密钥。然后根据索引从 allFreqScores 中提取子密钥字母,构建完整的密钥。如果 SILENT_MODE False ,则打印尝试的密钥。

7. 打印正确大小写的解密文本

由于解密后的文本为大写形式,需要将其转换为与原文相同的大小写形式。可以通过遍历解密后的文本,根据原文的大小写情况进行转换。

以下是整个流程的 mermaid 流程图:

graph TD;
    A[确定最可能的密钥长度] --> B[获取使用相同子密钥加密的字母];
    B --> C[尝试使用可能的密钥长度进行解密];
    C --> D[打印信息或使用静默模式];
    D --> E[寻找子密钥的可能组合];
    E --> F[打印正确大小写的解密文本];

通过以上步骤,可以逐步破解维吉尼亚密码,找到最可能的密钥并解密出原文。在实际应用中,可以根据具体情况调整参数和方法,提高破解的效率和准确性。

维吉尼亚密码破解全流程解析(续)

8. itertools.product() 函数详解

itertools.product() 函数是 Python 中一个非常有用的工具,它可以生成列表、字符串或元组等可迭代对象中元素的所有可能组合,这种组合被称为笛卡尔积。以下是一些使用示例:

import itertools
# 示例 1:使用字符串生成组合
result1 = itertools.product('ABC', repeat=4)
print(list(result1))

# 示例 2:使用 range 对象生成组合
result2 = itertools.product(range(8), repeat=5)
print(list(result2))

在示例 1 中, itertools.product('ABC', repeat=4) 会生成包含四个字符的所有可能组合,每个字符可以是 ‘A’、’B’ 或 ‘C’,最终会得到一个包含 $3^4 = 81$ 个元组的列表。在示例 2 中, itertools.product(range(8), repeat=5) 会生成包含五个整数的所有可能组合,每个整数的范围是 0 到 7,最终会得到一个包含 $8^5$ 个元组的列表。

在维吉尼亚密码破解中,我们不能直接将潜在的子密钥字母列表传递给 itertools.product() 函数,因为每个子密钥的潜在字母可能不同。我们需要通过索引来访问 allFreqScores 中的子密钥字母。由于 NUM_MOST_FREQ_LETTERS 常量设置为 4, itertools.product(range(NUM_MOST_FREQ_LETTERS), repeat=mostLikelyKeyLength) 会生成所有可能的索引组合,每个组合表示一个可能的密钥。

9. 访问 allFreqScores 中的子密钥

allFreqScores 是一个存储每个子密钥最可能字母及其频率匹配分数的列表。以下是一个假设的 allFreqScores 值示例:

allFreqScores = [
    [('A', 9), ('E', 5), ('O', 4), ('P', 4)],
    [('S', 10), ('D', 4), ('G', 4), ('H', 4)],
    [('I', 11), ('V', 4), ('X', 4), ('B', 3)],
    [('M', 10), ('Z', 5), ('Q', 4), ('A', 3)],
    [('O', 11), ('B', 4), ('Z', 4), ('A', 3)],
    [('V', 10), ('I', 5), ('K', 5), ('Z', 4)]
]

我们可以通过索引来访问 allFreqScores 中的具体值。例如:
- allFreqScores[0] 表示第一个子密钥的可能字母及其频率匹配分数列表。
- allFreqScores[1][0] 表示第二个子密钥最可能的字母及其频率匹配分数元组。
- allFreqScores[1][0][0] 表示第二个子密钥最可能的字母。

以下是一个表格总结不同索引访问的含义:
| 索引示例 | 含义 |
| ---- | ---- |
| allFreqScores[i] | 第 i+1 个子密钥的可能字母及其频率匹配分数列表 |
| allFreqScores[i][j] | 第 i+1 个子密钥中第 j+1 个可能的字母及其频率匹配分数元组 |
| allFreqScores[i][j][0] | 第 i+1 个子密钥中第 j+1 个可能的字母 |

10. 构建完整密钥并尝试解密

在获取了所有可能的子密钥组合后,我们需要构建完整的密钥并尝试解密密文。以下是具体的代码和步骤:

for indexes in itertools.product(range(NUM_MOST_FREQ_LETTERS), repeat=mostLikelyKeyLength):
    possibleKey = ''
    for i in range(mostLikelyKeyLength):
        possibleKey += allFreqScores[i][indexes[i]][0]
    if not SILENT_MODE:
        print('Attempting with key: %s' % (possibleKey))
    # 解密密文
    decryptedText = vigenereCipher.decryptMessage(possibleKey, ciphertextUp)
    # 检查是否为可读英语
    if freqAnalysis.englishFreqMatchScore(decryptedText) > THRESHOLD:
        # 恢复原文大小写
        originalCaseText = ''
        for i in range(len(ciphertext)):
            if ciphertext[i].isupper():
                originalCaseText += decryptedText[i].upper()
            elif ciphertext[i].islower():
                originalCaseText += decryptedText[i].lower()
            else:
                originalCaseText += ciphertext[i]
        print('Possible decrypted message:')
        print(originalCaseText)

上述代码中,首先通过 itertools.product() 生成所有可能的索引组合,然后根据索引从 allFreqScores 中提取子密钥字母,构建完整的密钥。接着使用该密钥解密密文,并计算解密后文本与英语字母频率的匹配分数。如果匹配分数超过阈值 THRESHOLD ,则认为解密成功,恢复原文的大小写并打印解密后的消息。

11. 总结与优化建议

通过以上步骤,我们可以完成维吉尼亚密码的破解。整个流程可以总结为以下几个关键步骤:
1. 确定最可能的密钥长度。
2. 获取使用相同子密钥加密的字母。
3. 尝试使用可能的密钥长度进行解密。
4. 打印信息或使用静默模式。
5. 寻找子密钥的可能组合。
6. 打印正确大小写的解密文本。

为了提高破解的效率和准确性,我们可以考虑以下优化建议:
- 调整阈值 :根据实际情况调整 THRESHOLD 的值,避免误判或漏判。
- 增加样本量 :如果可能的话,获取更多的密文样本,以提高密钥长度和子密钥的判断准确性。
- 优化频率分析方法 :可以使用更复杂的频率分析方法,如双字母频率分析或三字母频率分析,提高解密的准确性。

以下是整个优化流程的 mermaid 流程图:

graph TD;
    A[确定最可能的密钥长度] --> B[获取使用相同子密钥加密的字母];
    B --> C[尝试使用可能的密钥长度进行解密];
    C --> D{是否满足阈值};
    D -- 是 --> E[恢复原文大小写并打印];
    D -- 否 --> F[调整参数或增加样本量];
    F --> A;

通过不断优化和调整,我们可以更高效地破解维吉尼亚密码,获取原文信息。在实际应用中,还需要考虑安全性和合法性等问题,确保破解行为符合相关规定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值