不知道密钥的凯撒密码
Caesar cipher without knowing the Key
嘿伙计们,如果你看看我下面的代码,你将能够看到我能够创建一个可以打开文件的程序解码文件的内容并将其保存到另一个文件中,但我需要输入每次都是密钥,但是如果我不知道他们的密钥怎么办我如何使用频率分析来计算密钥。
import sys
def decrypt(cipher, key):
plain = ""
for index in range(len(cipher)):
if cipher[index].isalpha():
if cipher[index].isupper():
plain = plain + chr((ord(cipher[index]) - 64 - key) % 26 + 64)
elif cipher[index].islower():
plain = plain + chr((ord(cipher[index]) - 96 - key) % 26 + 96)
else:
plain = plain + cipher[index]
return plain
in_filename = sys.argv[1]
key = int(sys.argv[2])
out_filename = sys.argv[3]
with open(in_filename, "r") as f:
encrypted = f.read()
decrypted = decrypt(encrypted, key)
with open(out_filename, "w+") as f:
f.write(decrypted)
凯撒密码是一种线性代换密码。
解释:
让 p 成为你的明文。
将 k 作为我们的数字键(为了便于解释,<26)。
设c为p中的一个字符。
令 I(c) 为 c 在 p 中的索引。
让 fc(i) 成为一个函数,它将索引 i 映射到字母表中的字母。
让 e(c) 成为 c 的 'encrypted' 字符。
然后:
e(c) = fc( I(c) + k)
或者用简单的英语来说:每个字符移动 k 的值。因此它是一个线性替换,因为它总是被移动相同的量并且字符被替换。
这种加密算法的问题在于,您并没有真正对明文进行加扰或添加熵。
(用严厉的话来说,凯撒密码更接近于编码而不是加密)。
鉴于此,我们知道密文中的所有匹配字母都替代了完全相同的明文字符。
这意味着,您将能够运行对您的密文进行统计分析,并简单地分析字母分布。不同的 sounds/letters 出现的次数不同。
那么我们如何应用这个技术呢?
根据你的名字,我猜你是德国人,我假设你的密文也是。
在德语中,最常见的字母是 E 和 I。
我们快到了!
现在,您可以简单地找到密文中最常见的字母,并计算该字母与 E 之间的差异(当然是它们索引之间的差异)。这将为您提供密钥!
这是一个代码示例:
"""
This module aims to break Caesar-ciphers from German plaintext
based on statistics of letter distribution.
The caesar cipher is a character substitution algorithm.
Bob chooses a number as the key n.
Bob then shifts every character of the plain-text by n, cycling
through the entire alphabet.
e.g.: if n = 3: "ABC" -> "DEF".
Since it has a very small keyspace (26^1), it can be easily broken,
and Mallory could even guess or bruteforce the key.
(Note: You could choose a number higher than 26. However, this won't
increase your keyspace since any number x will
be reduced to 1-26. See: (x - (26*(x // 26)).
Common letters in the german language are (in descending order):
"E","N","I","S","R" (subject to verification)
The statistical approach works well for long sentences since one has
a greater samplesize for distribution analysis.
If two or more letters appear the same amount of times, you have to
check which key is actually correct, either
by simply reading the outputs or running them against a
distribution_dict of that language.
"""
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
SPECIAL_CHARS = " ,.-;:_?!="
def encrypt(plain_text, key):
"""
Encrypts plaintext using a caesar.
:param plain_text: The plaintext
:param key: The key (Integer)
:return: The cipher-text
"""
cipher_text = ""
for letter in plain_text:
if letter in SPECIAL_CHARS:
cipher_text += letter
continue
index = ALPHABET.find(letter.upper())
new_index = flatten(index + key)
cipher_text += ALPHABET[new_index]
return cipher_text
def decrypt(cipher_text, key=None):
"""
This function decrypts plaintext. If no key is specified, it
will be found using distribution analysis.
:param cipher_text: The cipher-text
:param key: The key
:return: the plain-text
"""
if key is None:
key = find_key_from_cipher(cipher_text)
plain_text = ""
for letter in cipher_text:
#Skipping special characters (incomplete solution)
if letter in SPECIAL_CHARS:
plain_text += letter
continue
index = ALPHABET.find(letter.upper())
new_index = flatten(index - key)
plain_text += ALPHABET[new_index]
return plain_text
def flatten(number) :
"""
Flattens the key back to be in range(1,26)
:param number:
:return:
"""
return number - (26*(number//26))
def find_key_from_cipher(cipher_text):
index_of_most_common_letter = 4 #Index of 'e'
#Calculate distribution
distribution_dict = analyse_letter_distribution(cipher_text)
#Get common letters
common_letters = sorted(distribution_dict, key=distribution_dict.get, reverse=True)
#Use most common letter to get key
key = ALPHABET.find(common_letters[0].upper()) - index_of_most_common_letter
return key
def analyse_letter_distribution(cipher_text):
distribution_dict = {}
for letter in cipher_text:
if letter in SPECIAL_CHARS:
continue
if letter not in distribution_dict:
distribution_dict[letter] = 1
else:
distribution_dict[letter] += 1
if len(distribution_dict.values()) != len(distribution_dict.values()):
print("Multiple letters appear the same amount of times! Uh oh.")
return distribution_dict
if __name__ == "__main__":
secret = encrypt("This sentence is encrypted. Encryption is broken by using awesome encryption algorithms!",5)
print(decrypt(secret))
最后一点:由于它是统计 分析,如果捕获的密文很长,这种方法最有效。
最后一点:这个描述有点不完整(格式看起来很难看,我在移动设备上。)我强烈推荐 Klaus Schmeh 的 german book 以供进一步阅读。
嘿伙计们,如果你看看我下面的代码,你将能够看到我能够创建一个可以打开文件的程序解码文件的内容并将其保存到另一个文件中,但我需要输入每次都是密钥,但是如果我不知道他们的密钥怎么办我如何使用频率分析来计算密钥。
import sys
def decrypt(cipher, key):
plain = ""
for index in range(len(cipher)):
if cipher[index].isalpha():
if cipher[index].isupper():
plain = plain + chr((ord(cipher[index]) - 64 - key) % 26 + 64)
elif cipher[index].islower():
plain = plain + chr((ord(cipher[index]) - 96 - key) % 26 + 96)
else:
plain = plain + cipher[index]
return plain
in_filename = sys.argv[1]
key = int(sys.argv[2])
out_filename = sys.argv[3]
with open(in_filename, "r") as f:
encrypted = f.read()
decrypted = decrypt(encrypted, key)
with open(out_filename, "w+") as f:
f.write(decrypted)
凯撒密码是一种线性代换密码。 解释:
让 p 成为你的明文。 将 k 作为我们的数字键(为了便于解释,<26)。 设c为p中的一个字符。 令 I(c) 为 c 在 p 中的索引。 让 fc(i) 成为一个函数,它将索引 i 映射到字母表中的字母。 让 e(c) 成为 c 的 'encrypted' 字符。
然后:
e(c) = fc( I(c) + k)
或者用简单的英语来说:每个字符移动 k 的值。因此它是一个线性替换,因为它总是被移动相同的量并且字符被替换。
这种加密算法的问题在于,您并没有真正对明文进行加扰或添加熵。 (用严厉的话来说,凯撒密码更接近于编码而不是加密)。
鉴于此,我们知道密文中的所有匹配字母都替代了完全相同的明文字符。
这意味着,您将能够运行对您的密文进行统计分析,并简单地分析字母分布。不同的 sounds/letters 出现的次数不同。
那么我们如何应用这个技术呢?
根据你的名字,我猜你是德国人,我假设你的密文也是。
在德语中,最常见的字母是 E 和 I。
我们快到了!
现在,您可以简单地找到密文中最常见的字母,并计算该字母与 E 之间的差异(当然是它们索引之间的差异)。这将为您提供密钥!
这是一个代码示例:
"""
This module aims to break Caesar-ciphers from German plaintext
based on statistics of letter distribution.
The caesar cipher is a character substitution algorithm.
Bob chooses a number as the key n.
Bob then shifts every character of the plain-text by n, cycling
through the entire alphabet.
e.g.: if n = 3: "ABC" -> "DEF".
Since it has a very small keyspace (26^1), it can be easily broken,
and Mallory could even guess or bruteforce the key.
(Note: You could choose a number higher than 26. However, this won't
increase your keyspace since any number x will
be reduced to 1-26. See: (x - (26*(x // 26)).
Common letters in the german language are (in descending order):
"E","N","I","S","R" (subject to verification)
The statistical approach works well for long sentences since one has
a greater samplesize for distribution analysis.
If two or more letters appear the same amount of times, you have to
check which key is actually correct, either
by simply reading the outputs or running them against a
distribution_dict of that language.
"""
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
SPECIAL_CHARS = " ,.-;:_?!="
def encrypt(plain_text, key):
"""
Encrypts plaintext using a caesar.
:param plain_text: The plaintext
:param key: The key (Integer)
:return: The cipher-text
"""
cipher_text = ""
for letter in plain_text:
if letter in SPECIAL_CHARS:
cipher_text += letter
continue
index = ALPHABET.find(letter.upper())
new_index = flatten(index + key)
cipher_text += ALPHABET[new_index]
return cipher_text
def decrypt(cipher_text, key=None):
"""
This function decrypts plaintext. If no key is specified, it
will be found using distribution analysis.
:param cipher_text: The cipher-text
:param key: The key
:return: the plain-text
"""
if key is None:
key = find_key_from_cipher(cipher_text)
plain_text = ""
for letter in cipher_text:
#Skipping special characters (incomplete solution)
if letter in SPECIAL_CHARS:
plain_text += letter
continue
index = ALPHABET.find(letter.upper())
new_index = flatten(index - key)
plain_text += ALPHABET[new_index]
return plain_text
def flatten(number) :
"""
Flattens the key back to be in range(1,26)
:param number:
:return:
"""
return number - (26*(number//26))
def find_key_from_cipher(cipher_text):
index_of_most_common_letter = 4 #Index of 'e'
#Calculate distribution
distribution_dict = analyse_letter_distribution(cipher_text)
#Get common letters
common_letters = sorted(distribution_dict, key=distribution_dict.get, reverse=True)
#Use most common letter to get key
key = ALPHABET.find(common_letters[0].upper()) - index_of_most_common_letter
return key
def analyse_letter_distribution(cipher_text):
distribution_dict = {}
for letter in cipher_text:
if letter in SPECIAL_CHARS:
continue
if letter not in distribution_dict:
distribution_dict[letter] = 1
else:
distribution_dict[letter] += 1
if len(distribution_dict.values()) != len(distribution_dict.values()):
print("Multiple letters appear the same amount of times! Uh oh.")
return distribution_dict
if __name__ == "__main__":
secret = encrypt("This sentence is encrypted. Encryption is broken by using awesome encryption algorithms!",5)
print(decrypt(secret))
最后一点:由于它是统计 分析,如果捕获的密文很长,这种方法最有效。
最后一点:这个描述有点不完整(格式看起来很难看,我在移动设备上。)我强烈推荐 Klaus Schmeh 的 german book 以供进一步阅读。