published on in Python

密码篇・对称密钥加密

返回教程主页

上篇 密码篇・密码学简介

维基百科: 对称密钥算法(英语:Symmetric-key algorithm)又称为对称加密、私钥加密、共享密钥加密,是密码学中的一类加密算法。这类算法在加密和解密时使用相同的密钥,或是使用两个可以简单地相互推算的密钥。

文本内容的数字化

在Python当中文本字符串可以被轻易转换为字节形式,转换后的字节序列的每一个元素都是一个数值,我们可以将其看作是一个数组,而这个数组就是原本字符串内容的数位形式。

使用字符串的encode方法可以将字符串转换为字节数组,使用字符串的format格式化方法可以将其打印成0、1组合的二进制格式:

text = 'hello world'
text_bytes = text.encode()
for b in text_bytes:
    print('{:08b}'.format(b))

运行结果:

01101000
01100101
01101100
01101100
01101111
00100000
01110111
01101111
01110010
01101100
01100100

加密操作的数字化

有一种特殊的运算方式可以非常简单的实现数字化的加密操作那便是“按位异或运算”^:

k = 49
a = 1234
b = a ^ k
c = b ^ k
print(k, a, b, c)

运行结果:

49 1234 1251 1234

我们首先对ak进行按位异或运算得到b,然后对bk进行按位异或运算得到c,发现ca相等,如果你愿意尝试更多的数字就会发现规律,a ^ k ^ k会等于a,那么这时我们可以将一个数字k作为密钥,对某个数字a进行加密得到密文数字b,然后使用作为密钥的数字k来解密。

先前我们知道了字符串可以转换成一串数字,那么我们完全可以使用按位异或的方法对字符串进行加密:

src = input('文本: ').strip()
key = int(input('密钥: ').strip())
dst = []
print('key={:08b}({})'.format(key, key))
for a in src.encode():
    b = a ^ key
    dst.append(b)
    print('{:08b} -> {:08b}'.format(a, b))
# bytes函数可以将数字列表转换为字节序列
print('密文:', bytes(dst))

src_bytes = []
for b in dst:
    a = b ^ key
    src_bytes.append(a)
# 使用字节序列的decode方法可以将其转回字符串
print('明文:', bytes(src_bytes).decode())

运行效果:

文本: hello world
密钥: 49
key=00110001(49)
01101000 -> 01011001
01100101 -> 01010100
01101100 -> 01011101
01101100 -> 01011101
01101111 -> 01011110
00100000 -> 00010001
01110111 -> 01000110
01101111 -> 01011110
01110010 -> 01000011
01101100 -> 01011101
01100100 -> 01010101
密文: b'YT]]^\x11F^C]U'
明文: hello world

不难发现使用按位异或加密在加密过程和解密过程中所使用的密钥都是一样的,因此该方法也被归入对称密钥加密。

子密钥

为了增加密码破译难度,我们可以使用多个密钥对明文进行加密。为了方便,通常我们会使用一个母密钥去生成多个子密钥,而不是每次去输入一堆密钥。

字符串可以被转换成一系列的数字,我们可以将字符串作为母密钥用来生成一系列的数字作为子密钥:

key = input('密钥: ').strip()
sub_keys = key.encode()

使用子密钥进行加密的Python实现:

src = input('文本: ').strip()
key = input('密钥: ').strip()
sub_keys = key.encode()
dst = []
for a in src.encode():
    b = a
    for k in sub_keys:
        b = b ^ k
    dst.append(b)
print('密文:', bytes(dst))

src_bytes = []
for b in dst:
    a = b
    # [::-1] 用于将序列对象反向,例如: 1, 2, 3 -> 3, 2, 1
    for k in sub_keys[::-1]:
        a = a ^ k
    src_bytes.append(a)
print('明文:', bytes(src_bytes).decode())

运行效果:

文本: hello world
密钥: password
密文: b'wzssp?hpms{'
明文: hello world

需要注意的是由于对称式加密的特点,当我们输入的母密钥中存在对称重复的时候情况就会变得糟糕,例如我们输入"abba"作为母密钥,那么算法将完全失效:

文本: hello world
密钥: abba
密文: b'hello world'
明文: hello world

显然我们需要对加密程序进行优化,通过制定更加科学的子密钥生成标准以及结合“费斯妥密码”算法架构就能有效地处理这类问题。

维基百科: 在密码学中,费斯妥密码(英语:Feistel cipher)是用于构造分组密码的对称结构,以德国出生的物理学家和密码学家霍斯特·费斯妥(Horst Feistel)命名,他在美国IBM工作期间完成了此项开拓性研究。通常也称为费斯妥网络(Feistel network)。大部分分组密码使用该方案,包括数据加密标准(DES)。费斯妥结构的优点在于加密和解密操作非常相似,在某些情况下甚至是相同的,只需要逆转密钥编排。

想要将对称式加密技术运用于商业活动,还有许多内容需要掌握,如果你足够感兴趣可以自己扩展学习更多的知识,例如: 费斯妥密码、ECB模式、CBC模式、DES标准、AES标准……

下篇 密码篇・公开密钥加密