Python 第三方库学习

Crypto库

Package Description
Crypto.Cipher Modules for protecting confidentiality that is, for encrypting and decrypting data (example: AES).
Crypto.Signature Modules for assuring authenticity, that is, for creating and verifying digital signatures of messages (example: PKCS#1 v1.5).
Crypto.Hash Modules for creating cryptographic digests (example: SHA-256).
Crypto.PublicKey Modules for generating, exporting or importing public keys (example: RSA or ECC).
Crypto.Protocol Modules for faciliting secure communications between parties, in most cases by leveraging cryptograpic primitives from other modules (example: Shamir’s Secret Sharing scheme).
Crypto.IO Modules for dealing with encodings commonly used for cryptographic data (example: PEM).
Crypto.Random Modules for generating random data.
Crypto.Util General purpose routines (example: XOR for byte strings).

简而言之

常见对称密码在 Crypto.Cipher 库下,主要有: DES 3DES AES RC4 Salsa20
非对称密码在 Crypto.PublicKey 库下,主要有: RSA ECC DSA
哈希密码在 Crypto.Hash 库下,常用的有: MD5 SHA-1 SHA-128 SHA-256
随机数在 Crypto.Random 库下
实用小工具在 Crypto.Util 库下
数字签名在 Crypto.Signature 库下

AES加解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Cipher import AES
import base64

key = bytes('this_is_a_key'.ljust(16,' '),encoding='utf8')
aes = AES.new(key,AES.MODE_ECB)

# encrypt
plain_text = bytes('this_is_a_plain'.ljust(16,' '),encoding='utf8')
text_enc = aes.encrypt(plain_text)
text_enc_b64 = base64.b64encode(text_enc)
print(text_enc_b64.decode(encoding='utf8'))

# decrypt
msg_enc = base64.b64decode(text_enc_b64)
msg = aes.decrypt(msg_enc)
print(msg.decode(encoding='utf8'))

注意:key和明文是需要填充到指定位数的,可以使用ljust或者zfill之类的填充,也可以用Util中的pad()函数填充!

对称密码DES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Cipher import DES
import base64

key = bytes('test_key'.ljust(8,' '),encoding='utf8')
des = DES.new(key,DES.MODE_ECB)

# encrypt
plain_text = bytes('this_is_a_plain'.ljust(16,' '),encoding='utf8')
text_enc = des.encrypt(plain_text)
text_enc_b64 = base64.b64encode(text_enc)
print(text_enc_b64.decode(encoding='utf8'))

# decrypt
msg_enc = base64.b64decode(text_enc_b64)
msg = des.decrypt(msg_enc)
print(msg.decode(encoding='utf8'))

非对称密码RSA

这个库的 RSA 主要是用来生成公钥文件/私钥文件或者读取公钥文件/私钥文件
生成公/私钥文件

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
from Crypto.PublicKey import RSA

rsa = RSA.generate(2048) # 返回的是密钥对象

public_pem = rsa.publickey().exportKey('PEM') # 生成公钥字节流
private_pem = rsa.exportKey('PEM') # 生成私钥字节流

f = open('public.pem','wb')
f.write(public_pem) # 将字节流写入文件
f.close()
f = open('private.pem','wb')
f.write(private_pem) # 将字节流写入文件
f.close()
#
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArreg3IX19DbszqSdBKhR
9cm495XAk9PBQJwHiwjKv6S1Tk5h7xL9/fPZIITy1M1k8LwuoSJPac/zcK6rYgMb
DT9tmVLbi6CdWNl5agvUE2WgsB/eifEcfnZ9KiT9xTrpmj5BJql9H+znseA1AzlP
iTukrH1frD3SzZIVnq/pBly3QbsT13UdUhbmIgeqTo8wL9V0Sj+sMFOIZY+xHscK
IeDOv4/JIxw0q2TMTsE3HRgAX9CXvk6u9zJCH3EEzl0w9EQr8TT7ql3GJg2hJ9SD
biebjImLuUii7Nv20qLOpIJ8qR6O531kmQ1gykiSfqj6AHqxkufxTHklCsHj9B8F
8QIDAQAB
-----END PUBLIC KEY-----

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArreg3IX19DbszqSdBKhR9cm495XAk9PBQJwHiwjKv6S1Tk5h
7xL9/fPZIITy1M1k8LwuoSJPac/zcK6rYgMbDT9tmVLbi6CdWNl5agvUE2WgsB/e
ifEcfnZ9KiT9xTrpmj5BJql9H+znseA1AzlPiTukrH1frD3SzZIVnq/pBly3QbsT
13UdUhbmIgeqTo8wL9V0Sj+sMFOIZY+xHscKIeDOv4/JIxw0q2TMTsE3HRgAX9CX
vk6u9zJCH3EEzl0w9EQr8TT7ql3GJg2hJ9SDbiebjImLuUii7Nv20qLOpIJ8qR6O
531kmQ1gykiSfqj6AHqxkufxTHklCsHj9B8F8QIDAQABAoI...
-----END RSA PRIVATE KEY-----
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
读取公/私钥文件加解密:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

def rsa_encrypt(plain):
with open('public.pem','rb') as f:
data = f.read()
key = RSA.importKey(data)
rsa = PKCS1_v1_5.new(key)
cipher = rsa.encrypt(plain)
return base64.b64encode(cipher)

def rsa_decrypt(cipher):
with open('private.pem','rb') as f:
data = f.read()
key = RSA.importKey(data)
rsa = PKCS1_v1_5.new(key)
plain = rsa.decrypt(base64.b64decode(cipher),'ERROR') # 'ERROR'必需
return plain

if __name__ == '__main__':
plain_text = b'This_is_a_test_string!'
cipher = rsa_encrypt(plain_text)
print(cipher)
plain = rsa_decrypt(cipher)
print(plain)

注意:RSA 有两种填充方式,一种是 PKCS1_v1_5,另一种是 PKCS1_OAEP

Hash算法

hashlib 库的用法类似,先实例化某个 Hash 算法,再用 update() 调用就可以了!

1
2
3
4
5
6
7
8
9
from Crypto.Hash import SHA1,MD5

sha1 = SHA1.new()
sha1.update(b'sha1_test')
print(sha1.digest()) # 返回字节串
print(sha1.hexdigest()) # 返回16进制字符串
md5 = MD5.new()
md5.update(b'md5_test')
print(md5.hexdigest())

数字签名

发送发用私钥签名,验证方用公钥验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

# 签名
message = 'To be signed'
key = RSA.import_key(open('private_key.der').read())
h = SHA256.new(message)
signature = pkcs1_15.new(key).sign(h)

# 验证
key = RSA.import_key(open('public_key.der').read())
h = SHA.new(message)
try:
pkcs1_15.new(key).verify(h, signature):
print "The signature is valid."
except (ValueError, TypeError):
print "The signature is not valid."

随机数

random 库类似。第一个函数很常用

1
2
3
4
5
6
7
import Crypto.Random
import Crypto.Random.random

print(Crypto.Random.get_random_bytes(4)) # 得到n字节的随机字节串
print(Crypto.Random.random.randrange(1,10,1)) # x到y之间的整数,可以给定step
print(Crypto.Random.random.randint(1,10)) # x到y之间的整数
print(Crypto.Random.random.getrandbits(16)) # 返回一个最大为N bit的随机整数

其它功能

常用到 Util 中的pad()函数来填充密钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import *
from Crypto.Util.Padding import *

# 按照规定的几种类型 pad,自定义 pad可以用 ljust()或者 zfill()
str1 = b'helloworld'
pad_str1 = pad(str1,16,'pkcs7') # 填充类型默认为'pkcs7',还有'iso7816'和'x923'
print(unpad(pad_str1,16))
# number
print(GCD(11,143)) # 最大公约数
print(bytes_to_long(b'hello')) # 字节转整数
print(long_to_bytes(0x41424344)) # 整数转字节
print(getPrime(16)) # 返回一个最大为 N bit 的随机素数
print(getStrongPrime(512)) # 返回强素数
print(inverse(10,5)) # 求逆元
print(isPrime(1227)) # 判断是不是素数

gmpy2库

gmpy2常见函数使用

1.初始化大整数

1
2
3
4
import gmpy2
gmpy2.mpz(909090)

result:mpz(909090)

2.求大整数a,b的最大公因数

1
2
3
4
import gmpy2
gmpy2.gcd(6,18)

result:mpz(6)

3.求大整数x模m的逆元y

1
2
3
4
5
import gmpy2
#4*6 ≡ 1 mod 23
gmpy2.invert(4,23)

result:mpz(6)

4.检验大整数是否为偶数

1
2
3
4
5
6
7
8
9
10
import gmpy2
gmpy2.is_even(6)

result:True

-----------
import gmpy2
gmpy2.is_even(7)

result:False

5.检验大整数是否为奇数

1
2
3
4
5
6
7
8
9
10
import gmpy2
gmpy2.is_odd(6)

result:False

-----------
import gmpy2
gmpy2.is_odd(7)

result:True

6.检验大整数是否为素数

1
2
3
4
import gmpy2
gmpy2.is_prime(5)

result:True

7.求大整数x开n次根

1
2
3
4
import gmpy2
gmpy2.iroot(81,2)

result:(mpz(9),True)

8.求大整数x的y次幂模m取余

1
2
3
4
5
import gmpy2
#2^4 mod 5 = 1
gmpy2.powmod(2,4,15)

result:mpz(1)

9.扩展欧几里得

1
2
3
4
5
import gmpy2
#3*6+17*(-1) = (3,17)
gmpy2.gcdext(3,17)

result:(mpz(1), mpz(6), mpz(-1))

RSA库

仅对字节运行

生成密钥

1
2
3
4
(pubkey, privkey) = rsa.newkeys(512, poolsize=8)
#均为元组形式
#poolsize表示线程数
#accurate=True默认
name value
pubkey (n,e)
privkey (n,e,d,p,q)

实际测试生成4096位密钥需要50秒左右

加密解密

rsa.encrypt(message,pubkey)

rsa.decrypt(crypto,prikey)

过程演示

1
2
3
4
5
(bob_pub, bob_priv) = rsa.newkeys(512)
message = 'hello Bob!'.encode('utf8')
crypto = rsa.encrypt(message, bob_pub)
message = rsa.decrypt(crypto, bob_priv)
print(message.decode('utf8'))

如果加密消息被篡改会报错

签名和验证

rsa.sign()

为邮件创建分离签名

rsa.verify()

验证签名,成功返回True

如果签名被篡改会报错

函数格式

  1. rsa.encrypt(message: bytes, pub_key: rsa.key.PublicKey) → bytes
  2. rsa.decrypt(crypto: bytes, priv_key: rsa.key.PrivateKey) → bytes
  3. rsa.sign(message: bytes, priv_key: rsa.key.PrivateKey, hash_method: str) → bytes
  4. rsa.verify(message: bytes, signature: bytes, pub_key: rsa.key.PublicKey) → str
  5. rsa.find_signature_hash(signature: bytes, pub_key: rsa.key.PublicKey) → str 返回从签名中检测到的哈希名称

其他

https://stuvel.eu/python-rsa-doc/genindex.html

Secrets库

secrets是python3.6加入到标准库的,使用secrets模块,可以生成适用于处理机密信息(如密码,帐户身份验证,安全令牌)的加密强随机数。

导入

1
import secrets

SystemRandom

它是使用OS提供的最高质量源生成随机数的类。 有关更多详细信息,请参阅random.SystemRandom。

1
2
3
secrets.SystemRandom()

<random.SystemRandom at 0x7fd537094418>

choice(sequence)

从非空序列中选择一个元素。

1
secrets.choice([23,3,5,6])

randbelow

[0,n) 取0-n之间的整数

1
2
3
secrets.randbelow(10)

6

randbits(k)

随机不超过k位的整数

1
2
3
secrets.randbits(10)

426

token生成

secret模块还提供用于生成适合于重置密码和难以想象的URL的安全令牌的功能。

token_bytes

1
2
3
secrets.token_bytes(nbytes=10)

b'\xca\xfeQ\x03&\x8b7\xd7&\xaf'

返回包含nbytes个字节的字节字符串。 如果nbytes为None或未给出,则使用有效的默认值。

token_hex

返回十六进制随机文本字符串。 该字符串有n个字节的随机字节,每个字节转换为两个十六进制数字。 如果nbytes为None或未给出,则使用有效的默认值。

1
2
3
secrets.token_hex()

'494ee83c2a42ce3dc488b059eb64a50628667e9f2c939a87672d3d6a1748252a'

token_urlsafe

返回随机字节为nbytes的URL安全文本字符串。 文本以Base64编码,每个字节平均约为1.3个字符。 如果nbytes为None或未给出,则使用有效的默认值。

1
2
3
secrets.token_urlsafe()

'giZFsPv9ch4OGWvuqihEIkj0LwOsHDOjgGHoqDeTvb8'

compare_digest

字符串比较的安全方式,减少攻击风险。

1
2
3
secrets.compare_digest("e","E")

False