Python數(shù)據(jù)加密:抄起來,壞人都瞎了眼
數(shù)據(jù)加密是一種保護(hù)信息安全的技術(shù),它可以把明文(原始的數(shù)據(jù))轉(zhuǎn)換成密文(不可讀的數(shù)據(jù)),從而防止未經(jīng)授權(quán)的人員訪問或修改數(shù)據(jù)。
加密簡(jiǎn)介
數(shù)據(jù)加密有多種方法,勇哥能力有限,就介紹三種常見的數(shù)據(jù)加密方法:對(duì)稱加密、非對(duì)稱加密和單向加密。
編碼與解碼
常所說的加密方式,都是對(duì)二進(jìn)制編碼的格式進(jìn)行加密的。Python 中進(jìn)行加密時(shí),要確保操作的是 Bytes,否則就會(huì)報(bào)錯(cuò)。將字符串和 Bytes 互相轉(zhuǎn)換可以使用 encode()和 decode()方法。
- encode():str 對(duì)象的方法,用于將字符串轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)(即 bytes),也稱為“編碼”。
- decode():bytes 對(duì)象的方法,用于將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為字符串,也稱為“解碼”。
- 在設(shè)置解碼采用的字符編碼時(shí),需要與編碼時(shí)采用的字符編碼一致。使用 decode()方法時(shí),同樣不會(huì)修改原字符串。
1.decode() | encode()案例:
def data_encode(st):
"""編碼 """
return st.encode("utf-8")
def bs64_data_decode(st):
"""解碼"""
return st.decode("utf8")
Base64 加/解密:base64 加密解密比較常見的就是圖片處理。
2.base64 案例:
"""base64信息轉(zhuǎn)圖片與圖片轉(zhuǎn)base64"""
def base64_to_img(byte_str, file_path):
imgdata = base64.b64decode(byte_str)
with open(file_path, 'wb') as f:
f.write(imgdata)
def base64_to_str(byte_str):
return base64.b64encode(byte_str.encode("utf-8")).decode('utf-8')
單向加密
單向加密是一種只能對(duì)數(shù)據(jù)進(jìn)行加密,而不能進(jìn)行解密的方法,也叫做散列函數(shù)或哈希函數(shù)。單向加密的目的是保證數(shù)據(jù)的完整性,即數(shù)據(jù)在傳輸或存儲(chǔ)過程中沒有被篡改或損壞。單向加密可以用來生成數(shù)據(jù)的摘要或簽名,用于驗(yàn)證數(shù)據(jù)的來源和內(nèi)容。單向加密有一個(gè)重要的特性,就是相同的輸入會(huì)產(chǎn)生相同的輸出,而不同的輸入會(huì)產(chǎn)生不同的輸出。常見的單向加密算法有:MD5、SHA 系列等。
1.MD5 加密示例:
def md5(st: str) -> str:
"""
Args:
st:待加密字符串
Returns: 返回MD5 加密后的字符串
"""
md = hashlib.md5() # 創(chuàng)建MD5對(duì)象
md.update(st.encode(encoding="utf-8"))
return md.hexdigest()
2.sha1 加密示例:
SHA1 基于 MD5,加密后的數(shù)據(jù)長(zhǎng)度更長(zhǎng),它對(duì)長(zhǎng)度小于 264 的輸入,產(chǎn)生長(zhǎng)度為 160bit 的散列值。比 MD5 多 32 位,因此,比 MD5 更加安全,但 SHA1 的運(yùn)算速度就比 MD5 要慢。
def sha1_secret_str(st):
"""
使用sha1加密算法,返回str加密后的字符串
Args:
st:
Returns:
"""
sha = hashlib.sha1(st.encode("utf-8"))
return sha.hexdigest()
3.sha256 加密示例:
據(jù)說是比特幣挖礦就使用到。
SHA256是SHA-2下細(xì)分出的一種算法。
SHA-2,名稱來自于安全散列算法2(英語:Secure Hash Algorithm 2)的縮寫,一種密碼散列函數(shù)算法標(biāo)準(zhǔn)(哈希算法),由美國(guó)國(guó)家安全局研發(fā),屬于SHA算法之一,是SHA-1的后繼者。
SHA-2下又可再分為六個(gè)不同的算法標(biāo)準(zhǔn),包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。這些變體除了生成摘要的長(zhǎng)度 、循環(huán)運(yùn)行的次數(shù)等一些微小差異外,
算法的基本結(jié)構(gòu)是一致的。對(duì)于任意長(zhǎng)度的消息,SHA256都會(huì)產(chǎn)生一個(gè)256bit長(zhǎng)的哈希值,稱作消息摘要。這個(gè)摘要相當(dāng)于是個(gè)長(zhǎng)度為32個(gè)字節(jié)的數(shù)組,通常用一個(gè)長(zhǎng)度為64的十六進(jìn)制字符串來表示。
def sha256_single(st):
"""
sha256加密
Args:
st: 加密字符串
Returns:加密結(jié)果轉(zhuǎn)換為16進(jìn)制字符串,并大寫
"""
sha_obj = hashlib.sha256()
sha_obj.update(st.encode("utf-8"))
return sha_obj.hexdigest().upper()
對(duì)稱加密
對(duì)稱加密是一種使用相同的密鑰來進(jìn)行數(shù)據(jù)加密和解密的方法,也叫做私鑰加密或共享密鑰加密。對(duì)稱加密的優(yōu)點(diǎn)是速度快,適合加密大量的數(shù)據(jù)。對(duì)稱加密的缺點(diǎn)是需要雙方事先約定好并保管好密鑰,否則會(huì)導(dǎo)致數(shù)據(jù)泄露或丟失。對(duì)稱加密有兩種模式:分組密碼和流密碼。分組密碼是把明文分成固定長(zhǎng)度的塊,然后對(duì)每個(gè)塊進(jìn)行加密或解密,生成相同長(zhǎng)度的密文或明文。流密碼是把明文分成單個(gè)字節(jié),然后對(duì)每個(gè)字節(jié)進(jìn)行加密或解密,生成單個(gè)字節(jié)的密文或明文。常見的對(duì)稱加密算法有:DES、AES、Blowfish 等。
1.AES(高級(jí)加密標(biāo)準(zhǔn)):
AES 只有一個(gè)密鑰,這個(gè)密鑰既用來加密,也用于解密,AES 加密方式有五種:ECB, CBC, CTR, CFB, OFB。
從安全性角度推薦 CBC 加密方法,本文介紹了 CBC,ECB 兩種加密方法的 python 實(shí)現(xiàn)。
CBC 和 ECB 模式的區(qū)別就是:
- CBC 加密需要一個(gè)十六位的 key(密鑰)和一個(gè)十六位 iv(偏移量)
- ECB 加密不需要 iv,只需要 key(密鑰)
class DoAES:
"""
AES 加密
"""
def __init__(self, key):
self.key = key # 將密鑰轉(zhuǎn)換為字符型數(shù)據(jù)
self.mode = AES.MODE_ECB # 操作模式選擇ECB
def encrypt(self, text):
"""加密函數(shù)"""
file_aes = AES.new(self.key, self.mode) # 創(chuàng)建AES加密對(duì)象
text = text.encode('utf-8') # 明文必須編碼成字節(jié)流數(shù)據(jù),即數(shù)據(jù)類型為bytes
while len(text) % 16 != 0: # 對(duì)字節(jié)型數(shù)據(jù)進(jìn)行長(zhǎng)度判斷
text += b'\x00' # 如果字節(jié)型數(shù)據(jù)長(zhǎng)度不是16倍整數(shù)就進(jìn)行補(bǔ)充
en_text = file_aes.encrypt(text) # 明文進(jìn)行加密,返回加密后的字節(jié)流數(shù)據(jù)
return str(base64.b64encode(en_text), encoding='utf-8') # 將加密后得到的字節(jié)流數(shù)據(jù)進(jìn)行base64編碼并再轉(zhuǎn)換為unicode類型
def decrypt(self, text):
"""解密函數(shù)"""
file_aes = AES.new(self.key, self.mode)
text = bytes(text, encoding='utf-8') # 將密文轉(zhuǎn)換為bytes,此時(shí)的密文還是由basen64編碼過的
text = base64.b64decode(text) # 對(duì)密文再進(jìn)行base64解碼
de_text = file_aes.decrypt(text) # 密文進(jìn)行解密,返回明文的bytes
return str(de_text, encoding='utf-8').strip() # 將解密后得到的bytes型數(shù)據(jù)轉(zhuǎn)換為str型,并去除末尾的填充
if __name__ == '__main__':
# key = os.urandom(16) #隨即產(chǎn)生n個(gè)字節(jié)的字符串,可以作為隨機(jī)加密key使用
key = '2l4LoWczlWxlMZJAAp5N0g6EygZZd9C6' # 隨即產(chǎn)生n個(gè)字節(jié)的字符串,可以作為隨機(jī)加密key使用
text = '4534' # 需要加密的內(nèi)容
aes_test = DoAES(key.encode('utf-8'))
cipher_text = aes_test.encrypt(text)
init_text = aes_test.decrypt(cipher_text)
print('加密后:' + cipher_text)
print('解密后:' + init_text)
輸出結(jié)果:
加密后:blhiuFNLSqBUMMDbWxUBBw==
解密后:4534
進(jìn)程已結(jié)束,退出代碼0
2.DES 示例:
class Des:
def __init__(self, text):
self.text = text # 原始字符串
self.KEY = '2l4LoWcz' # 這個(gè)key是固定問開發(fā),
# self.KEY = os.urandom(8) # 隨即產(chǎn)生n個(gè)字節(jié)的字符串,可以作為隨機(jī)加密key使用
def des_encrypt(self):
"""DES 加密
Returns:加密后字符串,16進(jìn)制
"""
secret_key = self.KEY # 密碼
iv = secret_key # 偏移
# secret_key:加密密鑰,CBC:加密模式,iv:偏移, padmode:填充
des_obj = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5)
# 返回為字節(jié)
secret_bytes = des_obj.encrypt(self.text.encode("utf-8"), padmode=PAD_PKCS5)
# 返回為16進(jìn)制
return binascii.b2a_hex(secret_bytes)
def des_decrypt(self):
"""
DES 解密
Returns:解密后的字符串
"""
secret_key = self.KEY
iv = secret_key
des_obj = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5)
decrypt_str = des_obj.decrypt(binascii.a2b_hex(self.text), padmode=PAD_PKCS5)
return bytes.decode(decrypt_str) # bytes.decode() 將bit轉(zhuǎn)為str
測(cè)試數(shù)據(jù):
if __name__ == '__main__':
text = 'TestingPlayer:測(cè)試玩家勇哥' # 需要加密的內(nèi)容
d_encrypt = Des(text)
cipher_text = d_encrypt.des_encrypt()
d_decrypt = Des(cipher_text)
init_text = d_decrypt.des_decrypt()
print('加密后:', cipher_text)
print('解密后:', init_text)
執(zhí)行結(jié)果:
加密后:b'6a7e6bf7f34fcf0c4024e49b110cb6fe7924026e4c0477e5563599123358a3026ef1e81fd7445cb2'
解密后:TestingPlayer:測(cè)試玩家勇哥
非對(duì)稱加密
非對(duì)稱加密也叫做公鑰加密,是一種使用不同的兩個(gè)密鑰來進(jìn)行數(shù)據(jù)加密和解密的方法。這兩個(gè)密鑰分別叫做公鑰和私鑰,它們之間有數(shù)學(xué)上的關(guān)聯(lián),但不能相互推導(dǎo)。公鑰是公開的,可以用來給數(shù)據(jù)進(jìn)行加密或驗(yàn)證簽名;私鑰是保密的,可以用來給數(shù)據(jù)進(jìn)行解密或生成簽名。非對(duì)稱加密的優(yōu)點(diǎn)是安全性高,不需要雙方事先約定好并傳輸秘鑰。非對(duì)稱加密的缺點(diǎn)是速度慢,適合加密少量的數(shù)據(jù)。常見的非對(duì)稱加密算法有:RSA、DSA、ECC 等。
1.RSA(羅納德·李維斯特·沙米爾·阿迪曼):
# 導(dǎo)入cryptography庫(kù)中的rsa模塊
from cryptography.hazmat.primitives.asymmetric import rsa
# 生成一對(duì)公鑰和私鑰,長(zhǎng)度為2048位
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
print("私鑰:", private_key)
print("公鑰:", public_key)
# 定義一個(gè)明文字符串
plaintext = "Hello, world!"
# 使用公鑰對(duì)明文進(jìn)行加密,得到一個(gè)字節(jié)串類型的密文
ciphertext = public_key.encrypt(plaintext.encode(), rsa.OAEP(mgf=rsa.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
print("密文:", ciphertext)
# 使用私鑰對(duì)密文進(jìn)行解密,得到一個(gè)字節(jié)串類型的明文
decrypted = private_key.decrypt(ciphertext, rsa.OAEP(mgf=rsa.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
print("解密后的明文:", decrypted.decode())
輸出結(jié)果:
私鑰: <cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey object at 0x7f9a6a0f4d90>
公鑰: <cryptography.hazmat.backends.openssl.rsa._RSAPublicKey object at 0x7f9a6a0f4d60>
密文: b'\x8b\x9e\x1e\x1b\x8f\x9d\x1e\x8b\x9c\x9f\x1f\x8b\x9d\x1e\x8b\x9c\x9f\x1f\x8b\x9d\x1e\x8b\x9c\x9f\x1f\x8b\x9d\x1e\x8b\x9c\x9f\x1f\xb3\xe2\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xe3\xc5\xd2\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xf7\xf6\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0'
解密后的明文: Hello, world!
2.RAS 封裝:
class Rsa:
def __init__(self, st: str):
self.st = st
# rsa加密
def rsa_encrypt(self):
# 生成公鑰、私鑰
(pubkey, privkey) = rsa.newkeys(1024)
print("公鑰: ", pubkey)
print("私鑰: ", privkey)
# 明文編碼格式
content = self.st.encode('utf-8')
# 公鑰加密
crypto = rsa.encrypt(content, pubkey)
# # 一般加密的密文會(huì)以base64編碼的方式輸出
b_res = base64.b64encode(crypto).decode()
return b_res, privkey
# rsa解密
def rsa_decrypt(self, pk):
# 私鑰解密
st = base64.b64decode(self.st.encode())
content = rsa.decrypt(st, pk)
con = content.decode('utf-8')
return con