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 """ 加密和解密 对称加密 - 加密和解密是同一个密钥 - DES / AES 非对称加密 - 加密和解密是不同的密钥 - RSA pip install pycrypto """ import base64from hashlib import md5from Crypto.Cipher import AESfrom Crypto import Randomfrom Crypto.PublicKey import RSAdef main (): """主函数""" key_pair = RSA.generate(1024 ) pub_key = RSA.importKey(key_pair.publickey().exportKey()) pri_key = RSA.importKey(key_pair.exportKey()) message1 = 'hello, world!' data = pub_key.encrypt(message1.encode(), None ) message2 = base64.b64encode(data[0 ]) print(message2) data = base64.b64decode(message2) message3 = pri_key.decrypt(data) print(message3.decode()) if __name__ == '__main__' : main()
fs
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 """ 哈希摘要 - 数字签名/指纹 - 单向哈希函数(没有反函数不可逆) 应用领域: 1. 数据库中的用户敏感信息保存成哈希摘要 2. 给数据生成签名验证数据没有被恶意篡改 3. 云存储服务的秒传功能(去重功能) """ class StreamHasher (): """摘要生成器""" def __init__ (self, algorithm='md5' , size=4096 ): """初始化方法 @params: algorithm - 哈希摘要算法 size - 每次读取数据的大小 """ self.size = size cls = getattr(__import__('hashlib' ), algorithm.lower()) self.hasher = cls() def digest (self, file_stream ): """生成十六进制的摘要字符串""" for data in iter(lambda : file_stream.read(self.size), b'' ): self.hasher.update(data) return self.hasher.hexdigest() def __call__ (self, file_stream ): return self.digest(file_stream) def main (): """主函数""" hasher1 = StreamHasher() hasher2 = StreamHasher('sha1' ) hasher3 = StreamHasher('sha256' ) with open('Python-3.7.2.tar.xz' , 'rb' ) as file_stream: print(hasher1.digest(file_stream)) file_stream.seek(0 , 0 ) print(hasher2.digest(file_stream)) file_stream.seek(0 , 0 ) print(hasher3(file_stream)) if __name__ == '__main__' : main()
Hashlib
一般包括常见的MD5,SHA1, SHA256等等, 属使用消息摘要算法。
消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,目前可以被解密逆向的只有CRC32算法,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。
消息摘要算法分为三类:
这三类算法的主要作用:验证数据的完整性
以MD5为例,计算出一个字符串的MD5值:
1 2 3 4 5 6 7 import hashlibmd5 = hashlib.md5() md5.update('猫' .encode('utf-8' )) print(md5.hexdigest())
当字符很长时,也可分块多次调用update(),结果一样:
1 2 3 4 5 6 7 import hashlibmd5 = hashlib.md5() md5.update('Logic will get you from A to B.' .encode('utf-8' )) md5.update(' Imagination will take you everywhere.' .encode('utf-8' )) print(md5.hexdigest())
MD5生成的结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。
简单的用户登录模块,就是把你的密码进行的md5加密后储存于数据库当中,这样即使被人恶意脱库了,Ta也不知道原始密码是什么,但实际上,有很多网站的用户密码都是明文保存的,因为要审查。
SHA的话,包括了(SHA-1,SHA-2,SHA-224,SHA-256,SHA-384,SHA-512), 返回对应固定长度的摘要信息(除了SHA1的结果是160 bit字节,用一个40位的16进制字符串表示)。
1 2 3 4 5 6 7 import hashlibsha1 = hashlib.sha1() sha1.update('Hello ' .encode('utf-8' )) sha1.update('My Majesty.' .encode('utf-8' )) print(sha1.hexdigest())
但是这样就安全了吗?很多用户喜欢用123456,888888,password或者简单的生日弱口令,于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表,即彩虹表进行反推。
加盐
更安全的办法,是对原始口令加一个复杂字符串来实现,俗称“加盐”,这个可以这么想:
盐加多了,你还能尝得出汤原来的味道吗? 所以,足够安全,除非你拿到盐,制作出的新的彩虹表。
比如,一个修改后的MD5算法实现用户登录的验证算法,取自雪峰的博客:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import hashlib, randomdef get_md5 (s ): return hashlib.md5(s.encode('utf-8' )).hexdigest() class User (object ): def __init__ (self, username, password ): self.username = username self.salt = '' .join([chr(random.randint(48 , 122 )) for i in range(20 )]) self.password = get_md5(password + self.salt) db = { 'michael' : User('michael' , '123456' ), 'bob' : User('bob' , 'abc999' ), 'alice' : User('alice' , 'alice2008' ) }
1 2 3 4 def login (username, password ): user = db[username] return user.password == get_md5(password+user.salt)
Base64
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 """ 编码和解码 - BASE64 0-9A-Za-z+/ 1100 0101 1001 0011 0111 0110 00110001 00011001 00001101 00110110 base64 b64encode / b64decode ------------------------------------- 序列化和反序列化 序列化 - 将对象变成字节序列(bytes)或者字符序列(str) - 串行化/腌咸菜 反序列化 - 把字节序列或者字符序列还原成对象 Python标准库对序列化的支持: json - 字符形式的序列化 pickle - 字节形式的序列化 dumps / loads """ import base64import jsonimport redisfrom example02 import Personclass PersonJsonEncoder (json.JSONEncoder ): def default (self, o ): return o.__dict__ def main (): cli = redis.StrictRedis(host='120.77.222.217' , port=6379 , password='123123' ) data = base64.b64decode(cli.get('guido' )) with open('guido2.jpg' , 'wb' ) as file_stream: file_stream.write(data) if __name__ == '__main__' : main()
In computer science, Base64 is a group of binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation.
-----------from wikipedia
Base64是一种用64个字符来表示二进制数据的方法, 一般用于URL、Cookie、网页中传输少量二进制数据,其转换表格如下:
Index
Binary
Char
Index
Binary
Char
Index
Binary
Char
Index
Binary
Char
0
000000
A
16
010000
Q
32
100000
g
48
110000
w
1
000001
B
17
010001
R
33
100001
h
49
110001
x
2
000010
C
18
010010
S
34
100010
i
50
110010
y
3
000011
D
19
010011
T
35
100011
j
51
110011
z
4
000100
E
20
010100
U
36
100100
k
52
110100
0
5
000101
F
21
010101
V
37
100101
l
53
110101
1
6
000110
G
22
010110
W
38
100110
m
54
110110
2
7
000111
H
23
010111
X
39
100111
n
55
110111
3
8
001000
I
24
011000
Y
40
101000
o
56
111000
4
9
001001
J
25
011001
Z
41
101001
p
57
111001
5
10
001010
K
26
011010
a
42
101010
q
58
111010
6
11
001011
L
27
011011
b
43
101011
r
59
111011
7
12
001100
M
28
011100
c
44
101100
s
60
111100
8
13
001101
N
29
011101
d
45
101101
t
61
111101
9
14
001110
O
30
011110
e
46
101110
u
62
111110
+
15
001111
P
31
011111
f
47
101111
v
63
111111
/
padding
=
Examples
base64实质是3个字符(24个bit)为一组,对应ascii码的二进制,如Lov对应01001100 01101111 01110110, 再重新划分为4组,每组有6个bit,如:010011 000110 111101 110110, 然后查表即可。
若不满足3个字符的要求,则用=补齐。
1 2 3 import base64base64.b64encode(b'Love' )
L 76
O 111
V 118
E 101
01001100
01101111
01110110
01100101
10011 ==> T
110 ==> G
111101 ==> 9
110110 ==> 2
11001 ==> Z
10000 ==> Q
=
=
1 2 base64.b64decode(b'TG92ZQ==' )
相应的,也有base32和base16的加密方式,这里不做赘述
但+和/在URL中就不能作为参数,于是就有"url safe"的base64编码,把字符+和/替换为成-和_.
这里引用一下@阮一峰(https://www.liaoxuefeng.com/wiki/1016959663602400/1017684507717184 ),错了@廖雪峰的那段代码,
1 2 3 4 5 6 base64.b64encode(b'i\xb7\x1d\xfb\xef\xff' ) base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff' ) base64.urlsafe_b64decode('abcd--__' )
然后,=若在URL、Cookie会有歧义,实际运用中会去掉,解码代码如下:
1 2 3 4 def safe_base64decode (s ): if len(s)%4 !=0 : s=s+b'=' *(4 -len(s)%4 ) return base64.urlsafe_b64decode(s)
1 2 3 4 5 6 7 8 9 '去月球Moon' .encode('utf-8' )base64.b64encode('去月球Moon' .encode('utf-8' )) assert b'\xe5\x8e\xbb\xe6\x9c\x88\xe7\x90\x83Moon' == safe_base64decode(b'5Y675pyI55CDTW9vbg' )assert b'\xe5\x8e\xbb\xe6\x9c\x88\xe7\x90\x83Moon' == base64.b64decode(b'5Y675pyI55CDTW9vbg==' )print('ok' )
Hmac
HMAC(keyed-Hash Message Authentication Code):含有密钥的散列函数算法,包含了MD和SHA两个系列的消息摘要算法, 实际上当使用我们自己随机生成的salt,譬如md5(message + salt),就是简单的Hmac算法。
包括:
MD系列:HmacMD2,HmacMD4,HmacMD5
SHA系列:HmacSHA1,HmacSHA224,HmacSHA256,HmacSHA38,HmacSHA512
Hmac算法针对所有哈希算法都通用,无论是MD5还是SHA-1。采用Hmac替代我们自己的salt算法,可以使程序算法更标准化,也更安全。
1 2 3 4 5 6 import hmacmessage = b'Bonjour le monde!' key = b'0229' h = hmac.new(key, message, digestmod='MD5' ) h.hexdigest()
此时,登陆算法,可改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import hmac, randomdef hmac_md5 (key, s ): return hmac.new(key.encode('utf-8' ), s.encode('utf-8' ), 'MD5' ).hexdigest() class User (object ): def __init__ (self, username, password ): self.username = username self.key = '' .join([chr(random.randint(48 , 122 )) for i in range(20 )]) self.password = hmac_md5(self.key, password) def login (username, password ): user = db[username] return user.password == hmac_md5(user.key, password)
REFERENCES