本文主要介绍密码学中常见的两种加密算法—对称加密和非对称加密。

1. 散列(摘要)算法

在学习加密算法之前,我们先来了解一下散列算法。散列算法是通过一定方式对原文进行计算,产生一个哈希值,不管原始数据是什么样的,得到的哈希值都是固定长度的,其作用只是为了验证数据的完整性和唯一性,无法通过摘要解密得到原始数据。而对于加密算法来说,我们可以通过“秘钥”或者“解密算法”将加密后的结果还原成原文。

常见的散列算法有:SM3、MD5、SHA-1

1.1 Example1

我们可以设计一个简单的对整数进行散列的算法

1
2
3
4
5
6
7
8
def integer_hash(x):
return x % 16


if __name__ == "__main__":
print(integer_hash(16), integer_hash(17), integer_hash(32))

>>> 0 1 0

我们采用对16取余的方案,可以看到对16和17进行hash后得到的摘要是不一样的。在实际传输过程中,我们可以通过同时传输原文和散列后的摘要,接收方通过对原文进行相同散列,比较得到的摘要和接收的摘要是否相同来验证文本的正确性。但是通过hash后的结果“0”或“1”并不能得到原文,而且不同的原文hash后的结果也可能相同,如16和32的hash结果都是0。当然,我们这里的hash算法过于简单,一个优秀的hash算法应该保证散列的随机性,尽量减少冲突。

1.2 Example2

1
2
3
4
5
6
7
8
9
10
11
12
import hashlib


str = "This text is hashed via md5"

hl = hashlib.md5()
hl.update(str.encode(encoding='utf-8'))

print("MD5散列前:" + str)
print("MD5散列后:" + hl.hexdigest())
>>> MD5散列前:This text is hashed via md5
>>> MD5散列后:5d3a325f6a7fd4c8e4d4dad3879cfcdd

我们常常可以看到网上有工具可以对MD5算法加密后的结果进行还原,那是由于MD5算法设计足够优秀,可以保证无冲突,因此我们可以保留很多“摘要-原文”字典,通过hash值来暴力求取原文。由于是通过字典暴力求解,所以一般缓存的都是常用的、较短的字符串,本例中的文本较长且不常见,因此将本例中的摘要通过工具进行解密往往不能成功。如果加“hello”的MD5结果加以解密,则很快能得到结果。很多网站常常用MD5来保存散列后的密码来避免明文存储,这时候可以通过加盐的方式进一步提高安全性。所谓加盐就是通过特定的方法将某一个固定的字符串或随机的字符串(需要保存)插入到原文中去,对原文进行改造。

2. 对称加密

对称加密指的是加、解密使用的同是一串秘钥,所以被称作对称加密。常见的对称加密算法有DES、AES等。对称算法又可分为两类。一次只对明文中的单个位(有时对字节)运算的算法称为序列算法或序列密码。另一类算法是对明文的一组位进行运算,这些位组称为分组,相应的算法称为分组算法或分组密码。现代计算机密码算法的典型分组长度为64位――这个长度既考虑到分析破译密码的难度,又考虑到使用的方便性。后来,随着破译能力的发展,分组长度又提高到128位或更长。(摘录自参考文献2,建议阅读原文,写得比较详细)

  • 优点:加解密效率高,加密速度快。
  • 缺点:秘钥保管困难,网络传输秘钥不安全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import binascii
from pyDes import des, CBC, PAD_PKCS5


def des_encrypt(str, secret_key):

k = des(secret_key, CBC, secret_key, pad=None, padmode=PAD_PKCS5)
en = k.encrypt(str, padmode=PAD_PKCS5)
return binascii.b2a_hex(en)


def des_descrypt(str, secret_key):

k = des(secret_key, CBC, secret_key, pad=None, padmode=PAD_PKCS5)
de = k.decrypt(binascii.a2b_hex(str), padmode=PAD_PKCS5)
return de


if __name__ == "__main__":

str_en = des_encrypt('hello world', "20200407")
print(str_en.decode('utf-8'))
str_de = des_descrypt(str_en, "20200407")
print("正确的key解密结果:" + str_de.decode('utf-8'))
str_de = des_descrypt(str_en, "20210407")
print("错误的key解密结果:" + str_de.decode('utf-8'))


>>> 886a9aca4d78c90016a81d3a296544ca
>>> 正确的key解密结果:hello world
>>> 错误的key解密结果:helmo world

3.非对称加密

非对称加密指的是加、解密使用不同的秘钥,一把作为公钥、另一把作为私钥。公钥加密的信息,只有私钥才能解密,且私钥加密的信息,只有公钥才能解密。非对称加密的常见算法是RSA。

  • 优点:比较安全
  • 缺点:效率较低

3.1 应用场景一(加密)

(1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。

(2)甲方获取乙方的公钥,然后用它对信息加密。

(3)乙方得到加密后的信息,用私钥解密。

3.2 应用场景二(验证签名)

(1)甲方生成两把秘钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。

(2)甲方用散列算法生成文件摘要,然后用私钥对摘要进行加密生成签名。

(3)乙方拿到文件文件后,用相同的散列算法生成摘要,用公钥对签名进行解密,比较两份摘要是否相同

数字签名可以保证甲方发给乙方的文件是完整的、没有被篡改过的。由于只有甲方拥有私钥,篡改者无法通过私钥进行加密,也就无法篡改签名。因此接收方如果通过公钥对签名进行解密得到的摘要与在接收方对原文进行散列得到的摘要不相同的话,就可以断定原文已经被修改过了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import rsa
import hashlib


def create_keys():

"""
生成秘钥对
"""

(pubkey, prikey) = rsa.newkeys(1024)
pub = pubkey.save_pkcs1()
pri = prikey.save_pkcs1()

with open('rsa.pub', 'wb+') as f:
f.write(pub)

with open('rsa.pri', 'wb+') as f:
f.write(pri)


def encrypt(original_text):
"""
公钥加密
"""
with open('rsa.pub', 'rb') as f:
pub = f.read()
pubkey = rsa.PublicKey.load_pkcs1(pub)
encrypted_text = rsa.encrypt(original_text.encode('utf-8'), pubkey)
return encrypted_text


def decrypt(encrypted_text):
"""
私钥解密
"""
with open('rsa.pri', 'rb') as f:
pri = f.read()
prikey = rsa.PrivateKey.load_pkcs1(pri)
original_text = rsa.decrypt(encrypted_text, prikey).decode("utf-8")
return original_text


def sign(message):
"""
私钥签名
"""
with open('rsa.pri', 'rb') as f:
pri = f.read()
prikey = rsa.PrivateKey.load_pkcs1(pri)
# 使用MD5所有散列算法生成摘要
signed_message = rsa.sign(message.encode('utf-8'), prikey, hash_method='MD5')
return signed_message


def verify(message, signed_message):
"""
公钥验证
"""
with open('rsa.pub', 'rb') as f:
pub = f.read()
pubkey = rsa.PublicKey.load_pkcs1(pub)
return rsa.verify(message.encode('utf-8'), signed_message, pubkey)


if __name__ == "__main__":
text = "This is encrypted by rsa"
create_keys()
text = encrypt(text)
print("加密结果:", text)
text = decrypt(text)
print("解密结果:", text)
signed_message = sign(text)
print("签名结果:", signed_message)
print("验签结果:", verify(text, signed_message))

>>> MD5加密前:This text is hashed via md5
>>> MD5加密后:5d3a325f6a7fd4c8e4d4dad3879cfcdd
>>> 签名结果: b'\x10^\xa53\x8a\xda\xf0"e}\x7f\x8dtg\x0f\x96\x08\xd2\xa2L\x16#c\xc6\xad*C\xa6\x84\x11A\xe9\t\xf9\\\xce\x03\x99\xf1\xb4\xfa\xdf\x83\xa4[\x1d\xbff\x9eO\x08\x92\xf4\x05E4"&v\xcfTD\xbbq\x87\x9f\xdb\xc0u\x9b(\x13\\\x0b<j*Y\x15\x97!\x0b\x17q1h\xcd\xb8\xe0\x98\xbb\xe7\xbb3\xbc\xb8\xef\xbf\xe1\xda$0:\xa0z\xbdP\xd9\xe65\xf4\x17X\xf5H\xc3\x15\x8f\xc67\xbb\x8e#Ba\xd8\'\x7f'
>>> 验签结果:MD5

4. 对称加密与非对称加密对比

  • 对称加密多用户通信秘钥管理困难,N个人两两之间需要保管一个秘钥,共$C_N^2$个秘钥对。
  • 非对称加密,N个用户,共有N个私钥和N个秘钥。
  • 在实际运用中可以将两种加密方式混合使用,将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。

5. 参考文献

  1. 摘要与加密的区别
  2. DES加密算法原理
  3. 非对称加密算法