Python 加密與解密小結
這篇文章主要介紹了Python 加密與解密,使用base64或pycrypto模塊
前言
據記載,公元前400年,古希臘人發明了置換密碼。1881年世界上的第一個電話保密專利出現。在第二次世界大戰期間,德國軍方啟用“恩尼格瑪”密碼機,密碼學在戰爭中起著非常重要的作用。隨著信息化和數字化社會的發展,人們對信息安全和保密的重要性認識不斷提高,于是在1997年,美國國家標準局公布實施了“美國數據加密標準(DES)”,民間力量開始全面介入密碼學的研究和應用中,采用的加密算法有DES、RSA、SHA等。隨著對加密強度需求的不斷提高,近期又出現了AES、ECC等。
使用密碼學可以達到以下目的:保密性:防止用戶的標識或數據被讀取。數據完整性:防止數據被更改。身份驗證:確保數據發自特定的一方。
我們所說的加密方式,都是對二進制編碼的格式進行加密的,對應到Python中,則是我們的Bytes
。所以當我們在Python中進行加密操作的時候,要確保我們操作的是Bytes
,否則就會報錯。注:兩位十六進制常常用來顯示一個二進制字節。
將字符串和Bytes
互相轉換可以使用encode()
和decode()
方法。
# 方法中不傳參數則是以默認的utf-8編碼進行轉換
In [1]: '南北'.encode()
Out[1]: b'\xe5\x8d\x97\xe5\x8c\x97'In [2]: b'\xe5\x8d\x97\xe5\x8c\x97'.decode()
Out[2]: '南北'
利用binascii
模塊可以將十六進制顯示的字節轉換成我們在加解密中更常用的顯示方式:
In [1]: import binasciiIn [2]: '南北'.encode()
Out[2]: b'\xe5\x8d\x97\xe5\x8c\x97'In [3]: binascii.b2a_hex('南北'.encode())
Out[3]: b'e58d97e58c97'In [4]: binascii.a2b_hex(b'e58d97e58c97')
Out[4]: b'\xe5\x8d\x97\xe5\x8c\x97'In [5]: binascii.a2b_hex(b'e58d97e58c97').decode()
Out[5]: '南北'
加密算法分類
對稱加密算法:
對稱加密采用了對稱密碼編碼技術,它的特點是文件加密和解密使用相同的密鑰發送方和接收方需要持有同一把密鑰,發送消息和接收消息均使用該密鑰。相對于非對稱加密,對稱加密具有更高的加解密速度,但雙方都需要事先知道密鑰,密鑰在傳輸過程中可能會被竊取,因此安全性沒有非對稱加密高。常見的對稱加密算法:DES,AES,3DES等等
非對稱加密算法:
文件加密需要公開密鑰(publickey)和私有密鑰(privatekey)。接收方在發送消息前需要事先生成公鑰和私鑰,然后將公鑰發送給發送方。發送放收到公鑰后,將待發送數據用公鑰加密,發送給接收方。接收到收到數據后,用私鑰解密。在這個過程中,公鑰負責加密,私鑰負責解密,數據在傳輸過程中即使被截獲,攻擊者由于沒有私鑰,因此也無法破解。非對稱加密算法的加解密速度低于對稱加密算法,但是安全性更高。非對稱加密算法:RSA、DSA、ECC等算法
消息摘要算法:
消息摘要算法可以驗證信息是否被篡改。在數據發送前,首先使用消息摘要算法生成該數據的簽名,然后簽名和數據一同發送給接收者。接收者收到數據后,對收到的數據采用消息摘要算法獲得簽名,最后比較簽名是否一致,以此來判斷數據在傳輸過程中是否發生修改。
Python加密庫
PyCrypto是 Python 中密碼學方面最有名的第三方軟件包。可惜的是,它的開發工作于2012年就已停止。其他人還在繼續發布最新版本的 PyCrypto,如果你不介意使用第三方的二進制包,仍可以取得Python 3.5 的相應版本。比如,可以在 Github 上找到了對應Python3.5的PyCrypto 二進制包。幸運的是,有一個該項目的分支?PyCrytodome 取代了 PyCrypto 。
# Linux 上安裝它,你可以使用以下pip 命令:
pip3 install -i https://pypi.douban.com/simple pycryptodome
# 導入
import Crypto# 在Windows 系統上安裝則稍有不同:
pip3 install -i https://pypi.douban.com/simple pycryptodomex
# 導入
import Cryptodome
DES加密
全稱為Data EncryptionStandard,即數據加密標準,是一種使用密鑰加密的塊算法,又被稱為美國數據加密標準
DES算法的入口參數有三個:Key、Data、Mode。其中Key為7個字節共56位,是DES算法的工作密鑰;Data為8個字節64位,是要被加密或被解密的數據;Mode為DES的工作方式,有兩種:加密或解密。
密鑰長64位,密鑰事實上是56位參與DES運算(第8、16、24、32、40、48、56、64位是校驗位,使得每個密鑰都有奇數個1),分組后的明文組和56位的密鑰按位替代或交換的方法形成密文組。
from Crypto.Cipher import DES
key = b'abcdefgh' # 密鑰 8位或16位,必須為bytesdef pad(text):# 加密函數,如果text不是8的倍數【加密文本text必須為8的倍數!】,那就補足為8的倍數while len(text) % 8 != 0:text += ' 'return textdes = DES.new(key, DES.MODE_ECB) # 創建一個DES實例
text = 'Python rocks!'
padded_text = pad(text) # 需要加密的數據
encrypted_text = des.encrypt(padded_text.encode('utf-8')) # 加密
print(encrypted_text)
# rstrip(' ')返回從字符串末尾刪除所有字符串的字符串(默認空白字符)的副本
plain_text = des.decrypt(encrypted_text).decode().rstrip(' ') # 解密
print(plain_text)
3DES
3DES(或稱為Triple DES)是三重數據加密算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。它相當于是對每個數據塊應用三次DES加密算法。
由于計算機運算能力的增強,原版DES密碼的密鑰長度變得容易被暴力破解。3DES即是設計用來提供一種相對簡單的方法,即通過增加DES的密鑰長度來避免類似的攻擊,而不是設計一種全新的塊密碼算法。
3DES(即Triple DES)是DES向AES過渡的加密算法(1999年,NIST將3-DES指定為過渡的加密標準),加密算法,其具體實現如下:設Ek()和Dk()代表DES算法的加密和解密過程,K代表DES算法使用的密鑰,M代表明文,C代表密文,這樣:
3DES加密過程為:C=Ek3(Dk2(Ek1(M)))
3DES解密過程為:M=Dk1(EK2(Dk3(C)))
AES加密
高級加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府采用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣為全世界所使用。經過五年的甄選流程,高級加密標準由美國國家標準與技術研究院(NIST)于2001年11月26日發布于FIPS PUB 197,并在2002年5月26日成為有效的標準。2006年,高級加密標準已然成為對稱密鑰加密中最流行的算法之一。
AES在軟件及硬件上都能快速地加解密,相對來說較易于實作,且只需要很少的存儲器。作為一個新的加密標準,目前正被部署應用到更廣大的范圍。
AES只是個基本算法,實現AES有若干模式。其中的CBC模式因為其安全性而被TLS(就是https的加密標準)和IPSec(win采用的)作為技術標準。簡單地說,CBC使用密碼和salt(起擾亂作用)按固定算法(md5)產生key和iv。然后用key和iv(初始向量,加密第一塊明文)加密(明文)和解密(密文)。
特點
- 抵抗所有已知的攻擊。
- 在多個平臺上速度快,編碼緊湊。
- 設計簡單。
AES為分組密碼,分組密碼也就是把明文分成一組一組的,每組長度相等,每次加密一組數據,直到加密完整個明文。在AES標準規范中,分組長度只能是128位,也就是說,每個分組為16個字節(每個字節8位)。密鑰的長度可以使用128位、192位或256位(16、24和32字節)。密鑰的長度不同,推薦加密輪數也不同。
##################################### AES.MODE_ECB ####################################
from Cryptodome.Cipher import AES
from binascii import b2a_hex, a2b_hex
# 密鑰key 長度必須為16(AES-128)、24(AES-192)、或32(AES-256)Bytes 長度.
# 目前AES-128足夠用
key = 'abcdefghigklmnop'
# 加密內容需要長達16位字符,所以進行空格拼接
def pad(text):while len(text) % 16 != 0:text += ' 'return text# 進行加密算法,模式ECB模式,把疊加完16位的秘鑰傳進來
aes = AES.new(key.encode(), AES.MODE_ECB)
# 加密內容,此處需要將字符串轉為字節
text = 'hello'
# 進行內容拼接16位字符后傳入加密類中,結果為字節類型
encrypted_text = aes.encrypt(pad(text).encode())
encrypted_text_hex = b2a_hex(encrypted_text)
print(encrypted_text_hex)# 此處是為了驗證是否能將字節轉為字符串后,進行解密成功
# 實際上a 就是 encrypted_text ,也就是加密后的內容
# 用aes對象進行解密,將字節類型轉為str類型,錯誤編碼忽略不計
de = str(aes.decrypt(a2b_hex(encrypted_text_hex)), encoding='utf-8',errors="ignore")
# 獲取str從0開始到文本內容的字符串長度。
print(de[:len(text)])##################################### AES.MODE_CFB ####################################
from Cryptodome.Cipher import AES
from Cryptodome import Random
from binascii import b2a_hex # 要加密的明文
data = '南來北往'
# 密鑰key 長度必須為16(AES-128)、24(AES-192)、或32(AES-256)Bytes 長度.
# 目前AES-128足夠用
key = b'this is a 16 key'
# 生成長度等于AES塊大小的不可重復的密鑰向量
iv = Random.new().read(AES.block_size)# 使用key和iv初始化AES對象, 使用MODE_CFB模式
mycipher = AES.new(key, AES.MODE_CFB, iv)
# 加密的明文長度必須為16的倍數,如果長度不為16的倍數,則需要補足為16的倍數
# 將iv(密鑰向量)加到加密的密文開頭,一起傳輸
ciphertext = iv + mycipher.encrypt(data.encode())# 解密的話要用key和iv生成新的AES對象
mydecrypt = AES.new(key, AES.MODE_CFB, ciphertext[:16])
# 使用新生成的AES對象,將加密的密文解密
decrypttext = mydecrypt.decrypt(ciphertext[16:])print('密鑰k為:', key)
print('iv為:', b2a_hex(ciphertext)[:16])
print('加密后數據為:', b2a_hex(ciphertext)[16:])
print('解密后數據為:', decrypttext.decode())
面向對象方式
#coding: utf8
import sys
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hexclass prpcrypt():def __init__(self, key):self.key = keyself.mode = AES.MODE_CBC#加密函數,如果text不是16的倍數【加密文本text必須為16的倍數!】,那就補足為16的倍數def encrypt(self, text):cryptor = AES.new(self.key, self.mode, self.key)#這里密鑰key 長度必須為16(AES-128)、24(AES-192)、或32(AES-256)Bytes 長度.目前AES-128足夠用length = 16count = len(text)add = length - (count % length)text = text + ('\0' * add)self.ciphertext = cryptor.encrypt(text)#因為AES加密時候得到的字符串不一定是ascii字符集的,輸出到終端或者保存時候可能存在問題#所以這里統一把加密后的字符串轉化為16進制字符串return b2a_hex(self.ciphertext)#解密后,去掉補足的空格用strip() 去掉def decrypt(self, text):cryptor = AES.new(self.key, self.mode, self.key)plain_text = cryptor.decrypt(a2b_hex(text))return plain_text.rstrip('\0')if __name__ == '__main__':pc = prpcrypt('keyskeyskeyskeys') #初始化密鑰e = pc.encrypt("00000")d = pc.decrypt(e) print e, de = pc.encrypt("00000000000000000000000000")d = pc.decrypt(e) print e, d
運行結果
a9755fd70d8d6db65a6fac12d4797dde 00000
2c1969f213c703ebedc36f9e7e5a2b88922ac938c983201c200da51073d00b2c 00000000000000000000000000
RSA加密
RSA加密算法是一種非對稱加密算法
。在公開密鑰加密和電子商業中RSA被廣泛使用。
該算法基于一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可發布的供任何人使用,私鑰則為自己所有,供解密之用。
3個參數:rsa_n, rsa_e,message。rsa_n, rsa_e 用于生成公鑰,message: 需要加密的消息
因為RSA加密算法的特性,RSA的公鑰私鑰都是10進制的,但公鑰的值常常保存為16進制的格式,所以需要將其用int()
方法轉換為10進制格式。
安裝 pip install rsa
使用
import rsa
from binascii import b2a_hex, a2b_hexclass rsacrypt():def __init__(self, pubkey, prikey):self.pubkey = pubkeyself.prikey = prikeydef encrypt(self, text):self.ciphertext = rsa.encrypt(text.encode(), self.pubkey)# 因為rsa加密時候得到的字符串不一定是ascii字符集的,輸出到終端或者保存時候可能存在問題# 所以這里統一把加密后的字符串轉化為16進制字符串return b2a_hex(self.ciphertext)def decrypt(self, text):decrypt_text = rsa.decrypt(a2b_hex(text), prikey)return decrypt_textif __name__ == '__main__':pubkey, prikey = rsa.newkeys(256)rs_obj = rsacrypt(pubkey,prikey)text='hello'ency_text = rs_obj.encrypt(text)print(ency_text)print(rs_obj.decrypt(ency_text))"""
b'7cb319c67853067abcd16aad25b3a8658e521f83b1e6a6cf0c4c2e9303ad3e14'
b'hello'
"""
base64
?
Base64編碼,64指A-Z、a-z、0-9、+和/這64個字符,還有“=”號不屬于編碼字符,而是填充字符。為什么發明這么個編碼呢,這個編碼的原理很簡單,“破解”也很容易,原因是電子郵件剛出來的時候,只傳遞英文字符,這沒有問題,但是后來,中國人,日本人都要發email,這樣問題就來了,因為這些字符有可能會被郵件服務器或者網關當成命令處理,故必須得有一種編碼來對郵件進行加密,但是加密的目的是為了能夠使得一些原始的服務器不出問題(現在服務器早已經能處理這些亂七八糟得情況了,不過因為已經形成了一套規范,所以郵件還是得經過Base64編碼才能傳遞)。
優點:方法簡單。缺點:不保險,別人拿到密文可以自己解密出明文
編碼原理:將3個字節轉換成4個字節((3 X 8)=24=(4X6)),先讀入3個字節,每讀一個字節,左移8位,再右移四次,每次6位,這樣就有4個字節了。解碼原理:將4個字節轉換成3個字節,先讀入4個6位(用或運算),每次左移6位,再右移3次,每次8位,這樣就還原了。
import base64
s1 = base64.encodestring('hello world')
s2 = base64.decodestring(s1)
print s1, s2
結果
aGVsbG8gd29ybGQ=
hello world
?
?