在文章《别怕,我们的聊天消息,没人能偷看》中,我们对一段中文进行加密,有这样一个段代码:
msg = '今晚8点,老地方碰头'
encryptd_msg = rsa.encrypt(msg.encode(), public_key)
其中,msg.encode()
把中文信息转换为了 bytes 型数据。我们来看一下这段 bytes 型数据型数据长什么样:
看起来一长串的十六进制值,似乎很难读懂。不过没关系,我们把这个 bytes 型的数据再转成列表看看:
这就变成了一个包含数字的列表。并且,如果你多次测试,你会发现,这些数字的范围是0-255.恰好对应了十六进制的00
到ff
。
现在我们就可以对数字进行加密了。由于00
-ff
对应了8位的二进制数,所以我们假设现在密钥是45,它的二进制值为00101101
,我们把这个列表里面的每一个数字对45取异或,得到一个新的数字列表:
注意,这里实际上当你使用 for 循环展开时,不用提前把 bytes 型字符串转换为列表,直接循环展开效果一致,如下图所示:
现在,我们再来把这段包含数字的列表转换为 bytes 型数据:
需要注意的是,并非所有 bytes 都能重新转换回字符串,现在新的 bytes 型密文就已经无法转换回去了:
所以,这个时候我们就需要使用 base64来把它编码为字符串:
base64的b64encode
方法返回的也是一个 bytes 型数据,但是这次可以成功转回普通字符串了,并且普通字符串跟它的 bytes 形式完全一样:
现在,你可以把这一段密文通过公开的聊天软件发给你的朋友。
你的朋友只需要把整个过程反向操作,就能解析出正确的信息,我们来写一段代码:
import base64
code = 'yZany7S3FcqvlMKRocWtrMixncu7lMqPnciJmQ=='
encrypted_msg = base64.b64decode(code.encode())
bytes_list = [x ^ 45 for x in encrypted_msg]
msg_bytes = bytes(bytes_list)
msg = msg_bytes.decode()
print(msg)
运行效果如下图所示:
成功解密。
其中的数字45
就是密码。异或操作有一个性质:
A xor B xor B = A
一个数字A,先对另一个数字 B 执行异或操作得到密文 C,然后 C 再对 B 进行一次异或操作,又能还原为 A。
我们正是使用了这样一个性质,实现了加密和解密。
可能有同学会问,为什么这里你选择异或,而不是列表里面的所有数字同时乘以或者加上某个数来加密呢?这是因为,如果要把一个包含数字的列表转成 bytes 型数据,那么列表里面的所有数字都必须在0-255的范围内,否则就会导致报错,如下图所示:
我们无法保证加法或者乘法执行以后的数字仍然在0-255这个范围内,但用其他复杂的算法又不一定可逆。所以我们选择了异或算法。
从本文可以看到,对中文进行加密,本质上还是对数字加密。