安裝與使用
Crypto 演算法庫在 python 中最初叫 pycrypto,這個作者有點懶,好幾年沒有更新,後來就有大佬寫了個替代庫 pycryptodome。這個庫目前只支援 python3,安裝也很簡單pip install pycryptodome
就行了!詳細的用法可以看看 官方文件
常見對稱密碼在 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
注意:python3 和 python2 在字串方面有個明顯的區別 – python3 中有位元組串 b`byte`,python2 中沒有位元組
。由於這個庫是在 python3 下的,所以加解密用的都是位元組!
使用這個庫來加解密特別簡單,記住這四步:
- 匯入所需庫
from Crypto.Cipher import AES
- 初始化 key
key = b`this_is_a_key`
- 例項化加解密物件
aes = AES.new(key,AES.MODE_ECB)
- 使用例項加解密
text_enc = aes.encrypt(b`helloworld`)
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
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 主要是用來生成
公鑰檔案/私鑰檔案或者讀取
公鑰檔案/私鑰檔案
生成公/私鑰檔案:
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-----
讀取公/私鑰檔案加解密:
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() 呼叫就可以了!
具體栗子:
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())
數字簽名
傳送發用私鑰簽名,驗證方用公鑰驗證
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
庫類似。第一個函式很常用
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()
函式來填充金鑰
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)) # 判斷是不是素數