提示: Python 2.7中的str是字節串,而Python 3.x中的str是字符串。本文中的代碼都是經過Python 2.7實現的,若是你使用的是Python 3.x,因爲下面大部分加密與解密函數的參數都要求是字節對象,所以在調用下面介紹的加解密函數時,可能須要先將字符串參數轉換爲字節對象。javascript
網絡安全涉及不少方面,而網絡數據的安全傳輸一般會面臨如下幾方面的威脅:html
針對以上幾個問題,能夠用如下幾種數據加密方式來解決(每種數據加密方式又有多種不一樣的算法實現):java
數據加密方式 | 描述 | 主要解決的問題 | 經常使用算法 |
---|---|---|---|
對稱加密 | 指數據加密和解密使用相同的密鑰 | 數據的機密性 | DES, AES |
非對稱加密 | 也叫公鑰加密,指數據加密和解密使用不一樣的密鑰--密鑰對兒 | 身份驗證 | DSA,RSA |
單向加密 | 指只能加密數據,而不能解密數據 | 數據的完整性 | MD5,SHA系列算法 |
須要說明的是,上面SHA系列算法是根據生成的密文的長度而命名的各類算法名稱,如SHA1(160bits)、SHA22四、SHA25六、SHA384等。咱們常據說的MD5算法生成的密文長度爲128bits。python
關於上面提到的這些內容,你們能夠參考《網絡數據傳輸安全及SSH與HTTPS工做原理》這篇博文來了解更多的信息。本文主要討論的問題是,咱們如何使用Python來實現這些數據加密方式。nginx
Python中的大部分功能都是經過模塊提供的,所以咱們主要是經過Python中提供的一些內置的模塊或外部模塊來實現上面提到的各類加密算法。使用過程也很簡單,只須要調用這些模塊提供的相應的函數接口便可。git
上面咱們已經提到過,單向加密算法有:MD五、SHA系列算法 和 HMAC,而到目前爲止Python內置的用於實現數據加密的模塊也主要是提供單向加密功能,而且這些模塊隨着Python版本的迭代也經歷了一些調整和整合:算法
md5模塊和sha模塊爲何會被整合到一個hashlib模塊中呢? 由於md5模塊提供的是MD5算法的實現,sha模塊提供的是SHA1算法的實現,而MD5和SHA1都是hash算法,具體解釋看下面的名詞解釋。數據庫
HASH: 通常翻譯爲「散列」(也有直接音譯爲「哈希」),就是把任意長度的輸入(又叫作預映射,pre-image),經過散列算法,變成固定長度的輸出,該輸出值就是散列值。這種轉換是一種壓縮映射,也就是散列值的空間一般遠小於輸入的空間,不一樣的輸入可能會散列成相同的輸出,而不可能從散列值來惟一確認輸入值。簡單來講,hash算法就是一種將任意長度的消息壓縮爲某一固定長度的消息摘要的函數。swift
MD5: 全稱爲 Message Digest algorithm 5,即信息摘要算法。該算法能夠生成定長的數據指紋,被普遍應用於加密和解密技術,經常使用於文件和數據的完整性校驗。api
SHA: 全稱爲 Secure Hash Algorithm,即安全散列算法/安全哈希算法。該算法是數字簽名等密碼學應用中的重要工具,被普遍應用於電子商務等信息安全領域。根據生成的密文的長度而命名的各類具體的算法有:SHA1(160bits)、SHA224(224bits)、SHA256(256bits)、SHA384(384bits)等。
HMAC: 全稱爲 Hash Message Authentication Code,即散列消息鑑別碼。HMAC是基於密鑰的哈希算法認證協議,主要是利用哈希算法(如MD5, SHA1),以一個密鑰和一個消息做爲輸入,生成一個消息摘要做爲輸出,所以其具體的算法名稱爲HMAC-MD五、HMAC-SHA1等。可見HMAC算法是基於各類哈希算法的,只是它在運算過程當中還可使用一個密鑰來加強安全性。
Python早期的相關模塊這裏再也不介紹了,咱們今天主要說明的是如下幾個模塊:
模塊名 | 內置模塊 | 描述 |
---|---|---|
hashlib | Y | 主要提供了一些常見的單向加密算法(如MD5,SHA等),每種算法都提供了與其同名的函數實現。 |
hmac | Y | 提供了hmac算法的實現,hamc也是單向加密算法,可是它支持設置一個額外的密鑰(一般被稱爲'salt')來提升安全性 |
random | Y | 該模塊主要用於一些隨機操做,如獲取一個隨機數,從一個可迭代對象中隨機獲取指定個數的元素。 |
secrets | Y | 這是Python 3.6中新增的模塊,用於獲取安全隨機數。 |
base64 | Y | 該模塊主要用於二進制數據與可打印ASCII字符之間的轉換操做,它提供了基於Base16, Base32, 和Base64算法以及實際標準Ascii85和Base85的編碼和解碼函數。 |
pycrypto | N | 支持單向加密、對稱加密和公鑰加密以及隨機數操做,這是個第三方模塊,須要額外安裝。 |
說明: random模塊嚴格上來說並不能被稱爲數據加密模塊,且官方文檔中也強調過該模塊不該該用於數據加密。可是咱們在進行數據加密時,仍是會經常用到隨機數的操做,因此這裏就順便對這個模塊進行下說明。
hashlib和hmac都是python內置的加密模塊,它們都提供實現了單向加密算法的api。
hashlib模塊爲不一樣的安全哈希/安全散列(Secure Hash Algorithm)和 信息摘要算法(Message Digest Algorithm)實現了一個公共的、通用的接口,也能夠說是一個統一的入口。由於hashlib模塊不只僅是整合了md5和sha模塊的功能,還提供了對更多中算法的函數實現,如:MD5,SHA1,SHA224,SHA256,SHA384和SHA512。
提示: 「安全哈希/安全散列」 與 「信息摘要」 這兩個術語是能夠等價互換的。比較老的算法被稱爲消息摘要,而現代屬於都是安全哈希/安全散列。
hashlib模塊包含的函數與屬性:
函數名/屬性名 | 描述 |
---|---|
hashlib.new(name[, data]) | 這是一個通用的哈希對象構造函數,用於構造指定的哈希算法所對應的哈希對象。其中name參數用於指定哈希算法名稱,如'md5', 'sha1',不區分大小寫;data是一個可選參數,表示初始數據。 |
hashlib.哈希算法名稱() | 這是一個hashlib.new()的替換方式,能夠直接經過具體的哈希算法名稱對應的函數來獲取哈希對象,如 hashlib.md5(),hashlib.sha1()等。 |
hashlib.algorithms_guaranteed | Python 3.2新增的屬性,它的值是一個該模塊在全部平臺都會支持的哈希算法的名稱集合:set(['sha1', 'sha224', 'sha384', 'sha256', 'sha512', 'md5']) |
hashlib.algorithms_available | Python 3.2新增的屬性,它的值是是一個當前運行的Python解釋器中可用的哈希算法的名稱集合,algorithms_guaranteed將永遠是它的子集。 |
hash對象包含的方法與屬性:
函數名/屬性名 | 描述 |
---|---|
hash.update(data) | 更新哈希對象所要計算的數據,屢次調用爲累加效果,如m.update(a); m.update(b)等價於m.update(a+b) |
hash.digest() | 返回傳遞給update()函數的全部數據的摘要信息--二進制格式的字符串 |
hash.hexdigest() | 返回傳遞給update()函數的全部數據的摘要信息--十六進制格式的字符串 |
hash.copy() | 返回該哈希對象的一個copy("clone"),這個函數能夠用來有效的計算共享一個公共初始子串的數據的摘要信息。 |
hash.digest_size | hash結果的字節大小,即hash.digest()方法返回結果的字符串長度。這個屬性的值對於一個哈希對象來講是固定的,md5:16,sha1(20), sha224(28) |
hash.block_size | hash算法內部塊的字節大小 |
hash.name | 當前hash對象對應的哈希算法的標準名稱--小寫形式,能夠直接傳遞給hashlib.new()函數來建立另一個同類型的哈希對象。 |
1)獲取一個哈希算法對應的哈希對象(好比名稱爲hash): 能夠經過 hashlib.new(哈希算法名稱, 初始出入信息)
函數,來獲取這個哈希對象,如hashlib.new('MD5', 'Hello'),hashlib.new('SHA1', 'Hello')等;也能夠經過hashlib.哈希算法名稱()
來獲取這個哈希對象,如hashlib.md5(), hashlib.sha1()等。
2)設置/追加輸入信息: 調用已獲得哈希對象的update(輸入信息)
方法能夠設置或追加輸入信息,屢次調用該方法,等價於把每次傳遞的參數憑藉後進行做爲一個參數墊底給update()方法。也就是說,屢次調用是累加,而不是覆蓋。
3)獲取輸入信息對應的摘要: 調用已獲得的哈希對象的digest()
方法或hexdigest()
方法便可獲得傳遞給update()方法的字符串參數的摘要信息。digest()方法返回的摘要信息是一個二進制格式的字符串,其中可能包含非ASCII字符,包括NUL字節,該字符串長度能夠經過哈希對象的digest_size
屬性獲取;而hexdigest()方法返回的摘要信息是一個16進制格式的字符串,該字符串中只包含16進制的數字,且長度是digest()返回結果長度的2倍,這可用郵件的安全交互或其它非二進制的環境中。
咱們以MD5算法爲例獲取字符串"Hello, World"的摘要信息(也叫數據指紋)
import hashlib
hash = hashlib.md5()
hash.update('Hello, ')
hash.update('World!')
ret1 = hash.digest()
print(type(ret1), len(ret1), ret1)
ret2 = hash.hexdigest()
print(type(ret2), len(ret2), ret2)
輸出結果:
(<type 'str'>, 16, 'e\xa8\xe2}\x88y(81\xb6d\xbd\x8b\x7f\n\xd4')
(<type 'str'>, 32, '65a8e27d8879283831b664bd8b7f0ad4')
分析:
在實際工做中,咱們一般都是獲取數據指紋的16進制格式,好比咱們在數據庫中存放用戶密碼時,不是明文存放的,而是存放密碼的16進制格式的摘要信息。當用戶發起登陸請求時,咱們按照相同的哈希算法獲取用戶發送的密碼的摘要信息,與數據中存放的與該帳號對應的密碼摘要信息作比對,二者一致則驗證成功。
另外須要說明的是,下面這幾段代碼是等價的:
hash = hashlib.md5()
hash.update('Hello, ')
hash.update('World!')
hash = hashlib.md5()
hash.update('Hello, World!')
hash = hashlib.new('md5')
hash.update('Hello, World!')
hash = hashlib.new('md5', 'Hello, ')
hash.update('World!')
說明: 若是咱們想使用其餘哈希算法來獲取數據指紋,只須要把上面代碼中的"md5"換成其餘算法名便可,如hashlib.sha1(),hashlib.new('sha1')等。
前面說過,HMAC算法也是一種一種單項加密算法,而且它是基於上面各類哈希算法/散列算法的,只是它能夠在運算過程當中使用一個密鑰來增加強安全性。hmac模塊實現了HAMC算法,提供了相應的函數和方法,且與hashlib提供的api基本一致。
hmac模塊提供的函數:
函數名 | 描述 |
---|---|
hmac.new(key, msg=None, digestmod=None) | 用於建立一個hmac對象,key爲密鑰,msg爲初始數據,digestmod爲所使用的哈希算法,默認爲hashlib.md5 |
hmac.compare_digest(a, b) | 比較兩個hmac對象,返回的是a==b的值 |
hmac對象中的方法和屬性:
方法名/屬性名 | 描述 |
---|---|
HMAC.update(msg) | 同hashlib.update(msg) |
HMAC.digest() | 同hashlib.digest() |
HMAC.hexdigest() | 同hashlib.hexdigest() |
HMAC.copy() | 同hashlib.copy() |
HMAC.digest_size | 同hashlib.digest_size |
HMAC.block_size | 同hashlib.block_size |
HMAC.name | 同hashlib.name |
hmac模塊模塊的使用步驟與hashlib模塊的使用步驟基本一致,只是在第1步獲取hmac對象時,只能使用hmac.new()函數,由於hmac模塊沒有提供與具體哈希算法對應的函數來獲取hmac對象。
import hmac
import hashlib
h1 = hmac.new('key', 'Hello, ')
h1.update('World!')
ret1 = h1.hexdigest()
print(type(ret1), len(ret1), ret1)
h2 = hmac.new('key', digestmod=hashlib.md5)
h2.update('Hello, World!')
ret2 = h2.hexdigest()
print(type(ret2), len(ret2), ret2)
輸出結果:
(<type 'str'>, 32, 'cfad9d610c1e548a03562f8eac399033')
(<type 'str'>, 32, 'cfad9d610c1e548a03562f8eac399033')
random和secrets模塊都是Pytthon內置的隨機數操做模塊,其中secrets模塊是Python 3.6才新增的模塊。
random模塊實現了一個僞隨機數生成器,可用來生成隨機數以及完成與隨機數相關的功能。下面咱們來介紹下該模塊下經常使用的幾個函數:
函數名 | 描述 |
---|---|
random.random() | 用於生成半開區間[0, 1.0)內的一個隨機浮點數 |
random.uniform(a, b) | 用於生成一個指定範文內[a, b]的隨機浮點數 |
random.randint(a, b) | 用於生成一個指定範圍內[a, b]的整數 |
random.randrange(start, stop[, step]) | 用於從指定範圍內[start, stop),按指定基數step遞增的集合中獲取一個隨機數,step默認值爲1。 |
random.randrange(stop) | 等價於random.randrange(0, stop) |
random.choice(seq) | 從指定序列seq中隨機獲取一個元素 |
random.sample(population, k) | 從指定序列中隨機獲取k個不重複的元素,並以列表形式返回,用於不進行內容替換的隨機抽樣。 |
random.shuffle(x[, random]) | 用於隨機打亂一個列表中元素,須要注意的是該函數操做的是列表對象,且沒有返回值。 |
說明:
- 1)random.sample(population, k)只有在population中沒有重複元素的狀況下獲取到的隨機抽樣結果纔會有相同的元素;
- 2)其實上面這些函數都是基於random.random()這個基礎函數實現的;
- 3)官方文章中有聲明,該模塊徹底不適合用做數據加密。
import random
print("random.random(): ", random.random())
print("random.uniform(10, 20): ", random.uniform(10, 20))
print("random.randint(10, 20): ", random.randint(10, 20))
print("random.randrange(10, 20, 2): ", random.randrange(10, 20, 2))
print("random.choice('abcd1234'): ", random.choice('abcd1234'))
print("random.sample('abcd1234', 3): ", random.sample('abcd1234', 3))
print("random.sample('abcd1234', 3): ", random.sample('abcd1234', 3))
print("random.shuffle([1, 2, 3, 4, 5, 6]): ", random.shuffle([1, 2, 3, 4, 5, 6]))
list = [1, 2, 3, 4, 5, 6]
random.shuffle(list)
print("random.shuffle([1, 2, 3, 4, 5, 6]): ", list)
輸出結果:
('random.random(): ', 0.2967959697940342)
('random.uniform(10, 20): ', 10.774070602657055)
('random.randint(10, 20): ', 18)
('random.randrange(10, 20, 2): ', 18)
("random.choice('abcd1234'): ", 'd')
("random.sample('abcd1234', 3): ", ['d', '1', '4'])
("random.sample('abcd1234', 3): ", ['d', 'd', 'd'])
('random.shuffle([1, 2, 3, 4, 5, 6]): ', None)
('random.shuffle([1, 2, 3, 4, 5, 6]): ', [5, 1, 2, 4, 3, 6])
再次說明,random.shuffle()的函數沒有返回結果,且其操做的參數必須是一個列表對象。
secrets模塊是Python 3.6新增的內置模塊,它能夠生成用於管理密碼、帳戶驗證信息、安全令牌和相關祕密信息等數據的密碼強隨機數。須要特別聲明的是,與random模塊中的默認僞隨機數生成器相比,咱們應該優先使用secrets模塊,由於random模塊中的默認僞隨機數生成器是爲建模和模擬而設計的,不是爲安全或密碼學而設計的。整體來說,咱們能夠經過secrets模塊完成兩種操做:
下面來看下secrets模塊提供的函數:
函數名 | 描述 |
---|---|
secrets.choice(sequence) | 功能與random.choice(seq)相同,從指定的非空序列中隨機選擇一個元素並返回 |
secrets.randbelow(n) | 功能與random.randrange(n)相同,從半開區間[0, n)內隨機返回一個整數 |
secrets.randbits(k) | 返回一個帶有k個隨機位的整數 |
secrets.token_bytes([nbytes=None]) | 返回一個包含nbytes個字節的隨機字節串 |
secrets.token_hex([nbytes=None]) | 返回一個包含nbytes字節的16進制格式的隨機文本字符串,每一個字節被轉成成2個16進制數字,這能夠用來生成一個隨機密碼。 |
secrets.token_urlsafe([nbytes]) | 返回一個包含nbytes個字節的隨機安全URL文本字符串,這能夠在提供重置密碼的應用中用來生成一個臨時的隨機令牌 |
secrets.compare_digest(a, b) | 比較字符串a和字符串b是否相等,相等則返回True,不然返回False |
若是以上函數中的nbytes參數未提供或設置爲None則會取一個合理的默認值。那麼生成一個令牌(token)時應該使用多個字節呢? 爲了抵抗暴力破解攻擊,令牌須要有足夠的隨機性,當生成令牌使用的字節數越多時,暴力破解須要嘗試的次數就越多。所以,當計算機的計算能力變得更強時,也就意味着計算能夠再更短的時間內完成更多的猜想次數。所以,這裏所使用的字節個數不該該是一個固定的值,而是應該隨着計算機計算能力的加強而增長。到2015年爲止,咱們相信32個字節(256bits)的隨機性對於secrets模塊的典型應用場景來講是足夠的了。
實例1: 生成一個由8位數字和字母組成的隨機密碼
import secrets
import string
alphanum = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphanum) for i in range(8))
實例2: 生成一個由10位數字和字母組成的隨機密碼,要求至少有一個小寫字符,至少一個大寫字符 和 至少3個數字
import secrets
import string
alphanum = string.ascii_letters + string.digits
while True:
password = ''.join(secrets.choice(alphanum) for i in range(10))
if (any(c.islower() for c in password)
and any(c.isupper() for c in password)
and len(c.isdigit() for c in password) >= 3):
break
實例3: 生成一個用於找回密碼應用場景的、包含一個安全令牌的、很難猜到的臨時URL
import secrets
url = 'https://mydomain.com/reset=' + secrets.token_urlsafe()
說明: secrets模塊是Python 3.6新增的內置模塊,儘管官方文檔中強調過random模塊並不適合用來作安全和加密相關的工做,可是在Python 3.6以前咱們仍是能夠用random模塊來模擬secrets模塊提供的這些功能的實現。
常常聽到有人說「base64加密」,其實base64並不能用於數據加密,它也不是爲了純粹的數據加密而生的,它的出現是爲了解決不可見字符串的網絡傳輸和數據保存問題。由於,用base64對數據進行轉換的過程不能成爲「加密」與「解密」,只能成爲「編碼」與「解碼」。下面咱們也會用到它,因此這裏順便作下簡單的介紹。
Base64是一種用64個字符來表示任意二進制數據的方法,它是一種經過查表對二進制數據進行編碼的方法,不能用於數據加密。base64最初的出現時爲了可以正確的傳輸郵件數據,由於郵件中的附件(好比圖片)的二進制數中可能存在不可見字符(ascii碼中128-255之間的值是不可見字符),好比咱們嘗試用記事本或其餘文本編輯器打開一個圖片時,一般都會看到一大堆亂碼,這些亂碼就是不可見字符。因爲早期的一些網絡設備和網絡協議是沒法正確識別這些字符的,這就可能在數據傳輸時出現各類沒法預知的問題。base64的做用就是把含有不可見字符的信息用可見字符來表示(Ascii碼中0-127之間的值是可見字符),從而解決這個問題。
關於base64的介紹及實現原理能夠看看這幾篇文章:
base64適用於小段內容的編碼,好比數字證書的簽名、URL、Cookie信息等須要經過網絡進行傳輸的小段數據。關於base64在數字簽名中的應用會在本文後面講解pycrypto模塊使用實例時有具體的應用示例。
Python中有一個內置的base64模塊可直接用來進行base64的編碼和解碼工做--即提供 「二進制數據」 與 「可打印(可見)的ASCII字符」之間的轉換功能。經常使用的函數有如下幾個:
函數名 | 描述 |
---|---|
base64.b64encode(s, altchars=None) | 對二進制數據(字節串)s經過base64進行編碼,返回編碼後的字節串 |
base64.b64decode(s, altchars=None, validate=False) | 對經過base64編碼的字節對象或ASCII字符串s進行解碼,返回解碼後的字節串 |
base64.urlsafe_b64encode(s) | 與b64encode()函數不一樣的是,它會把標準Base64編碼結果中的字符'+'和字符'/'分別替換成字符'-'和字符'_'。 |
base64.urlsafe_b64decode(s) | 解碼經過base64.urlsafe_b64encode()函數編碼的字節對象或ASCII字符串s。 |
提示: URL中有一些有特殊意義的字符,也就是保留字符:';', '/', '?', ':', '@', '&', '=', '+', '$' 和 ',' ,在URL的參數值中應該避免這些字符的出現。
下面來看個簡單的示例:
import base64 data = 'hello, 世界!' based_data1 = base64.b64encode(data) plain_data1 = base64.b64decode(based_data1) based_data2 = base64.urlsafe_b64encode(data) plain_data2 = base64.urlsafe_b64decode(based_data2) print(based_data1) print(based_data2) print(plain_data1) print(plain_data2)
輸出結果:
aGVsbG8sIOS4lueVjO+8gQ== aGVsbG8sIOS4lueVjO-8gQ== hello, 世界! hello, 世界!
經過上面的這個簡單示例的輸出結果會發現,隨翻urlsafe_b64encode()函數會把編碼結果中的字符'+'和字符'/'替換成其餘URL的非保留字符,可是它的編碼結果中仍是可能出現字符'='。那麼這些字符'='表明什麼呢?可否去掉呢?
其實base64編碼的過程當中會先把原來數據中的每3個字節的二進制數據編碼爲4個字節的文本數據,當原始數據最後不滿3個字節時就須要用'\00'字節進行補位湊夠3個字節,並且會在編碼結果的最後加上相應個數的'='號來表示補了多少個字節,這樣解碼的時候就能夠去掉那些補位的字節。
由此咱們能夠得出兩個結論:
基於以上第2個結論,爲了不編碼結果中可能出現的的字符'='對網絡數據傳輸形成影響,能夠在傳出前去掉後面的字符'=',接收方能夠經過對數據的長度對4求模獲得應該補上的字符'='個數,從而獲得正確的數據。好比,咱們能夠經過下面這個解碼函數來完成這個過程:
import base64
def safe_b64decode(s):
length = len(s) % 4
for i in range(length):
s = s + '='
return base64.b64decode(s)
if __name__ == '__main__':
print(safe_b64decode('aGVsbG8sIOS4lueVjO+8gQ=='))
print(safe_b64decode('aGVsbG8sIOS4lueVjO+8gQ='))
print(safe_b64decode('aGVsbG8sIOS4lueVjO+8gQ'))
輸出結果:
hello, 世界! hello, 世界! hello, 世界!
可見,雖然咱們把上面那個示例中經過base64編碼後的結果後面的字符'='去掉了,經過咱們本身定義的safe_b64decode()函數最終獲得了正確的解碼結果。
pycryto模塊不是Python的內置模塊,它的官方網站地址是這裏。pycrypto模塊是一個實現了各類算法和協議的加密模塊的結合,提供了各類加密方式對應的多種加密算法的實現,包括 單向加密、對稱加密以及公鑰加密和隨機數操做。而上面介紹的hashlib和hmac雖然是Python的內置模塊,可是它們只提供了單向加密相關算法的實現,若是要使用對稱加密算法(如, DES,AES等)或者公鑰加密算法咱們一般都是使用pycryto這個第三方模塊來實現。
須要注意的是,pycrypto模塊最外層的包(package)不是pycrypto,而是Crypto。它根據加密方式類別的不一樣把各類加密方法的實現分別放到了不一樣的子包(sub packages)中,且每一個加密算法都是以單獨的Python模塊(一個.py文件)存在的。咱們來看下這些子包:
包名 | 描述 |
---|---|
Crypto.Hash | 該包中主要存放的是單向加密對應的各類哈希算法/散列算法的實現模塊,如MD5.py, SHA.py,SHA256.py等。 |
Crypto.Cipher | 該包中主要存放的是對稱加密對應的各類加密算法的實現模塊,如DES.py, AES.py, ARC4.py等;以及公鑰加密對應的各類加密算法的實現模塊,如PKCS1_v1_5.py等。 |
Crypto.PublicKey | 該包中主要存放的是公鑰加密與簽名算法的實現模塊,如RSA.py, DSA.py等。 |
Crypto.Signatue | 該包中主要存放的是公鑰簽名相關算法的實現模塊,如PKCS1_PSS.py, PKCS1_v1_5.py。 |
Crypto.Random | 該包中只有一個隨機數操做的實現模塊 random.py |
Crypto.Protocol | 該包中存放的是一些加密協議的實現模塊,如Chaffing.py, KDF.py等。 |
Crypto.Util | 該包存放的是一些有用的模塊和函數 |
這裏須要說明的是,Crypto.PublicKey子包下的RSA.py和DSA.py模塊只是用來生成祕鑰對的,而基於公鑰的加密與解密功能是由Crypto.Cipher子包下的PKCS1_v1_5.py或PKCS1_OAEP.py以這個密鑰對兒爲密鑰來實現的;一樣,簽名與驗證相關算法的功能是由Crypto.Signature子包下的PKCS1_v1_5.py和PKCS1_PASS.py以這個密鑰對而爲密鑰來實現的。
因爲pycryto不是Python的內置模塊,因此在使用它以前須要經過Python模塊管理工具(如pip)來安裝。不幸的是,若是你使用的是Windows平臺會遇到一些問題,好比執行pip install pycryto
命令來安裝pycrpto時可能會獲得如下錯誤提示信息:
error: Microsoft Visual C++ 9.0 is required (Unable to find vcvarsall.bat).
或
error: Unable to find vcvarsall.bat
這是因爲pycrypto模塊是用C語言實現的,Python模塊管理工具在安裝它時須要使用C/C++編譯工具對它的代碼進行編譯,可是找不到對應版本的編譯工具,具體解釋及解決方案請查看《這篇博文》。
因爲pycrypto把不一樣的類別加密算法的實現模塊都放到了Crypto下不一樣的子包下了,因此咱們只須要肯定咱們所須要使用的加密算法的實現模塊在哪一個子包下,而後導入相應的實現模塊就可使用了。好比咱們打算使用MD5算法,就能夠經過from Crypto.Hash import MD5
來導入MD5這個模塊,而後就可使用該模塊相應的api了。
實例1: 使用SHA256算法獲取一段數據的摘要信息
from Crypto.Hash import SHA256
hash = SHA256.new()
hash.update('Hello, World!')
digest = hash.hexdigest()
print(digest)
輸出結果:
dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
實例2: 使用AES算法加密,解密一段數據
from Crypto.Cipher import AES
# 加密與解密所使用的密鑰,長度必須是16的倍數
secret_key = "ThisIs SecretKey"
# 要加密的明文數據,長度必須是16的倍數
plain_data = "Hello, World123!"
# IV參數,長度必須是16的倍數
iv_param = 'This is an IV456'
# 數據加密
aes1 = AES.new(secret_key, AES.MODE_CBC, iv_param)
cipher_data = aes1.encrypt(plain_data)
print('cipher data:', cipher_data)
# 數據解密
aes2 = AES.new(secret_key, AES.MODE_CBC, 'This is an IV456')
plain_data2 = aes2.decrypt(cipher_data) # 解密後的明文數據
print('plain text:', plain_data2)
輸出結果:
('cipher data\xef\xbc\x9a', '\xcb\x7fd\x03\x12T,\xbe\x91\xac\x1a\xd5\xaa\xe6P\x9a')
('plain text\xef\xbc\x9a', 'Hello, World123!')
實例3: 隨機數操做
from Crypto.Random import random
print('random.randint: ', random.randint(10, 20))
print('random.randrange: ', random.randrange(10, 20, 2))
print('random.randint: ', random.getrandbits(3))
print('random.choice: ', random.choice([1, 2, 3, 4, 5]))
print('random.sample: ', random.sample([1, 2, 3, 4, 5], 3))
list = [1, 2, 3, 4, 5]
random.shuffle(list)
print('random.shuffle: ', list)
輸出結果:
('random.randint: ', 10L)
('random.randrange: ', 10L)
('random.randint: ', 5L)
('random.choice: ', 5)
('random.sample: ', [5, 4, 2])
('random.shuffle: ', [5, 2, 1, 3, 4])
實例4: 使用RSA算法生成密鑰對兒
生成祕鑰對:
from Crypto import Random
from Crypto.PublicKey import RSA
# 獲取一個僞隨機數生成器
random_generator = Random.new().read
# 獲取一個rsa算法對應的密鑰對生成器實例
rsa = RSA.generate(1024, random_generator)
# 生成私鑰並保存
private_pem = rsa.exportKey()
with open('rsa.key', 'w') as f:
f.write(private_pem)
# 生成公鑰並保存
public_pem = rsa.publickey().exportKey()
with open('rsa.pub', 'w') as f:
f.write(public_pem)
私鑰文件rsa.key的內容爲:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCo7vV5xSzEdQeFq9n5MIWgIuLTBHuutZlFv+Ed8fIk3yC4So/d
y1f64iuYFcDeNU7eVGqTSkHmAl4AihDXoaH6hxohrcX0bCg0j+VoQMe2zID7MzcE
d50FhJbuG6JsWtYzLUYs7/cQ3urZYwB4PEVa0WxQj2aXUMsxp6vl1CgB4QIDAQAB
AoGAS/I5y4e4S43tVsvej6efu1FTtdhDHlUn1fKgawz1dlwVYqSqruSW5gQ94v6M
mZlPnqZGz3bHz3bq+cUYM0jH/5Tygz4a+dosziRCUbjMsFePbJ4nvGC/1hwQweCm
+7sxog4sw91FrOfAg/iCcoeho0DghDolH9+zzwRYPIWUyUECQQDFGe+qccGwL9cU
v+GmZxtF8GkRL7YrXI7cvnZhnZZ7TANjxlYukLGEpiFGIDd0Aky1QhkK18L8DTO4
+iGXTpgJAkEA22o03/1IqeRBofbkkDmndArHNUnmv5pyVFaLKPoVgA4A1YsvqxUL
DK6RwFGONUMknBWY59EDKCUdIf3CsVIhGQJAJKDMRB19xBMv4iBCe9z/WYDy1YnL
TcWWmvkeIMfbVjBrFNif3WlwQ9lnp5OHGpzuymRtKPGtv49ohECfi3HEmQJAPI+n
AoAdk07+Up8b3TccoinrbCj2uMH/dongpTHJx2uWDVr6kEUhpKF2d1fLYaYjr7VC
XBHTxjvgO6aYG2to2QJBAIzDugOSTeQFpidCoewfa0XX4guF+WRf8wzyBC/XE6TY
3cIY05sjbpfiVwW/Cb8Z2ia8EgBTGN8HSIFOUQ2jRl4=
-----END RSA PRIVATE KEY-----
公鑰文件rsa.pub的內容爲:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo7vV5xSzEdQeFq9n5MIWgIuLT
BHuutZlFv+Ed8fIk3yC4So/dy1f64iuYFcDeNU7eVGqTSkHmAl4AihDXoaH6hxoh
rcX0bCg0j+VoQMe2zID7MzcEd50FhJbuG6JsWtYzLUYs7/cQ3urZYwB4PEVa0WxQ
j2aXUMsxp6vl1CgB4QIDAQAB
-----END PUBLIC KEY-----
實例5: 公鑰加密算法的實現
前面說過,公鑰加密算法是由Crypto.Cipher子包下的PKCS1_v1_5.py或PKCS1_OAEP.py模塊以已經存在的密鑰對兒爲密鑰來實現的,如今經常使用的是PKCS1_v1_5。另外,咱們前面提到過,使用對方的公鑰加密,使用對方的私鑰解密才能保證數據的機密性,所以這裏以上面生成的公鑰進行加密數據,以上面生成的私鑰解密數據:
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5
import base64
# 數據加密
message = "This is a plain text."
with open('rsa.pub', 'r') as f:
public_key = f.read()
rsa_key_obj = RSA.importKey(public_key)
cipher_obj = Cipher_PKCS1_v1_5.new(rsa_key_obj)
cipher_text = base64.b64encode(cipher_obj.encrypt(message))
print('cipher test: ', cipher_text)
# 數據解密
with open('rsa.key', 'r') as f:
private_key = f.read()
rsa_key_obj = RSA.importKey(private_key)
cipher_obj = Cipher_PKCS1_v1_5.new(rsa_key_obj)
random_generator = Random.new().read
plain_text = cipher_obj.decrypt(base64.b64decode(cipher_text), random_generator)
print('plain text: ', plain_text)
輸出結果:
('cipher test: ', 'oq1sOSz4lS9PgrKmiwuAHs7iUhmWMvWdEbXLTOdhGtyIAr6xwmjtnBNpuvMVIM2Mz/O/xVzPu5L8nzUVW2THKpQinNwC7JWF0wnxrTHwKrmfXIIxxibQJS02obxkoEeqrjRo0b8V7yktYIV3ig2SlU3yjcr+lOFmRX+h6dE2TAI=')
('plain text: ', 'This is a plain text.')
實例6: 數據簽名與簽名驗證的實現
一樣,簽名與驗證相關算法的功能是由Crypto.Signature子包下的PKCS1_v1_5.py和PKCS1_PASS.py以這個密鑰對而爲密鑰來實現的。數據簽名的目的是爲了防止別人篡改發送人的原始數據,其原理是:
from Crypto.Hash import SHA
from Crypto.Signature import PKCS1_v1_5 as Signature_PKCS1_v1_5
message = "This is the message to send."
# 數據簽名
with open('rsa.key', 'r') as f:
private_key = f.read()
rsa_key_obj = RSA.importKey(private_key)
signer = Signature_PKCS1_v1_5.new(rsa_key_obj)
digest = SHA.new()
digest.update(message)
signature = base64.b64encode(signer.sign(digest))
print('signature text: ', signature)
# 驗證簽名
with open('rsa.pub', 'r') as f:
public_key = f.read()
rsa_key_obj = RSA.importKey(public_key)
signer = Signature_PKCS1_v1_5.new(rsa_key_obj)
digest = SHA.new(message)
is_ok = signer.verify(digest, base64.b64decode(signature))
print('is ok: ', is_ok)
輸出結果:
('signature text: ', 'Bb4gvPU9Ji63kk3SSTiAVLctDbdb91DQuQKecbTcO2Jvpwbr7fr9sKZO+vZ8LIuSOdJkhbGX6swsSNwDI/CoT0xCdjiasfySPgsLyTcSWLyy9P7SrDuveH1ABUR/oYisvT1wFsScu0NMOBR8sLpboPk2DiW6n400jZq7t09xUyc=')
('is ok: ', True)
上面這幾個關於pycrpto的使用實例來自這裏。
上面講了不少內容,如今咱們簡單總結下:
轉載來源:https://www.cnblogs.com/yyds/p/7072492.html