好久没更新博客了,今天我们来共同学习关于恺撒密码(Caesar Cipher)项目。可能对大多数人来讲,并不知道什么是恺撒密码,其实一开始我也不懂,也是查阅了相关资料才了解了什么是Caesar Cipher,恺撒密码说白了是字母密码加密的一种方式,比如你用一段英文给对方发送一则消息,但是你又不想你的消息被第三者知道,那么就可以通过恺撒密码进行传递,将你要传递的消息字母在密钥数字的加密下把正常的一则消息变成一串“乱码”,当然你的通信对象是了解你们之间的“协议”(通信密钥数字)的。今天要讲的项目就是通过程序来将消息明文加密成密文,将密文破解成消息明文。
恺撒密码的核心是凯撒密码的密钥,是 1 到 26 之间的一个数字。如果要破解密文就必须要知道这个密钥(也就是知道用于加密消息的数字),否则无法对这个保密的代码进行解密。
前面我们讲到,恺撒密码是用字母来传递,且要用到专门设置的密钥,那么具体传递方式是什么样的呢。
首先我们将要传递的消息(用字母、数字、符号组成)写下,并用一个
“移位后的”字母来代替它,移位个数就是前面我提到的密钥。替换完成的新“消息”(实则已经是乱码)完全隐藏了我们要传递的消息 内容。例如把字母 A 移动 3格,就会得到字母 D。如果把 A 移动两格,就会得到字母 C。依次将所有的明文消息码通过“移位”变成没有可读性的密文。
如果以 3 作为一个密钥来加密明文“Howdy”,则:
● 字母“H”变成了“K”;
● 字母“o”变成了“r”;
● 字母“w”变成了“z”;
● 字母“d”变成了“g”;
● 字母“y”变成了“b”。
秘文“Howdy”用 3 作为密钥进行加密,变成了“Krzgb”。
ASCII码(American Standard Code for Information Interchange,美国标准信息交换代码)是一种代码,它将每个字符和 32 到 126 之间的一个数字进行关联。大写字母“A”到“Z”是 ASCII 数字 65 到 90。小写字母“a”到“z”是 ASCII 数字97 到 122。数字“0”到“9”的 ACSII 数字是 48 到 57。表 15-1 展示了所有的 ASCII 字符和编码。
现代计算机使用 UTF-8,而不是 ASCII。但是 UTF-8 向后兼容 ASCII,所以 ASCII 字符的 UTF-8 编码和 ASCII 的编码是一样的。
因此,如果想要把“A”移动 3 格,需要像下面这样做:
● 把“A”转换成编码(65);
● 65 加 3,得到 68;
● 把编码 68 转换回字母(“D”)。
函数 chr()(“character”的简写)接受一个整数形式的顺序编码,并且返回单个字符的字符串。ord()函数接受单个字符的字符串,并且返回整数形式的顺序编码。输入如下代码:
>>ord('Y')
89
>>chr(89)
'Y'
>>chr(ord('Y'))
'Y'
这两个函数主要用于对明文消息的加密,以及将密文消息的破解作用,具体怎么工作,接下里我们将继续讲!
加密明文的核心是先将明文的所有字母、数字、符号转化为数字,主要通过chr()函数来实现,在将数字集(转化后的明文消息)加上密钥数字(明文字母后移则对应密文字母表后面的字母,字母越靠后对应的ASCII码数字越大,因此转化后数字明文应当加上密钥数字)。得到新的密文数字,再将密文数字通过ord()函数转化为密文字母,得到的就是恺撒密码了。整体的程序设计核心是这个思路!
# Caesar Cipher
def getTranslatedMessage(mode, message, key):
if mode[0] == 'd':
key = -key # 如果是解密(d)工作模式,则密钥为负数
translated = '' # 加(解)密生成的密(明)文
for symbol in message: # 遍历密(明)文信息
if symbol.isalpha(): # 如果是字母
num = ord(symbol) # 获取字母的ASCII码,注意:ord()函数只能用于将字母转化为ASCII码的数字
num += key # 通过密钥对密文进行移位操作从而获得真实的明文字母
if symbol.isupper(): # 所有密文字母是大写
if num > ord('Z'):
num -= 26
elif num < ord('A'):
num += 26
elif symbol.islower():
if num > ord('z'):
num -= 26
elif num < ord('a'):
num += 26
translated += chr(num)
else:
translated += symbol
return translated
mode = getMode()
message = getMessage()
key = getKey()
print('Your translated text is:')
print(getTranslatedMessage(mode, message, key))
def getMode():
while True:
print('Do you wish to encrypt or decrypt a message?')
mode = input().lower()
if mode in 'encrypt e decrypt d brute b'.split():
return mode
else:
print('Enter either "encrypt" or "e" or "decrypt" or "d".')
说明:此函数主要供用户选择模式专用比较简单,就是返回一个用户输入的选项(解密还是加密)。
def getMessage():
print('Enter your message:')
return input()
def getKey():
key = 0
while True:
print('Enter the key number (1-%s)' % (MAX_KEY_SIZE))
key = int(input())
if (key >= 1 and key <= MAX_KEY_SIZE):
return key
说明:这里要要注意的密钥数字的选择范围,密钥数字为什么只能是26以内的数字,由于英文字母只有26个,因此我们的“移位”范围只能在26之内。这样才能不至于使字母被替换成了其他符号!
总的来讲项目不是太难,比较简单,而且涉及的函数不是特别多用到的Python方法也不多,都是常用的字符串方法,很适合大家练练手。就程序本身而言,还有些不足,比如密钥太少,在今天日益发达的互联网时代,26个数字的密钥太容易被破解了,只要将26个密钥数字一个个带入其中很容易破解密码!
那么接下里我们将就如何暴力破解再分析下!
我们在不知道密钥数字的情况下,可以把26个数字一个个用来试总有一款能获取到我们要的明文消息,当然我们将这项繁琐的工作交给计算机来处理。
既然是一个个的试密钥,这时候我们想到Python的循环迭代可以帮我们处理这个工作。
print('Your translated text is:')
if mode[0] != 'b':
key = getKey()
print(getTranslatedMessage(mode, message, key))
else:
for key in range(1, MAX_KEY_SIZE + 1):
print(key, getTranslatedMessage('decrypt', message, key))
说明:首先我们将用户输入密钥的方式改为for循环迭代的方式,当模式选择为解密就将密钥提供函数改为range()函数的for循环迭代,这样就可以将所有(26个数字)可能密钥添加到主体程序中试,最后得到的结果可以很快找到消息原始消息明文。
恺撒密码项目分享到此告一段落了。
——————————————
码字不易。
希望每一期的分享对大家有所帮助,也感谢大家多多支持作者。
要想学习更多有关Python知识可以点击关注作者,或者评论区留言,我会不定期更新分享有关Python学习的知识,你们的每一次点赞收藏关注都是对作者最大的鼓励。