加密技術(shù)定義、使用
在開發(fā)爬蟲的過程中,我們經(jīng)常遇到的一種反爬措施是數(shù)據(jù)加密。常見的加密算法可以分為三類:對稱加密算法,非對稱加密算法和Hash算法(事實上不是加密算法而是摘要算法)
一、對稱加密
1.定義
采用單鑰密碼系統(tǒng)的加密方法,同一個密鑰可以同時用作信息的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密。由于其速度快,對稱性加密通常在消息發(fā)送方需要加密大量數(shù)據(jù)時使用。但是,加解密雙方使用同樣的密鑰進行加密和解密。密鑰是控制加密及解密的指令,算法是一種規(guī)則,規(guī)定如何進行加密和解密。因此加密的安全性不僅取決于加密算法本身,密鑰管理的安全性更是重要。因為加密解密都使用同一個密鑰,如何把密鑰安全地傳遞到解密者手上就成了必須要解決的問題。
2.工作過程
下面舉個例子來簡要說明一下對稱加密的工作過程。甲和乙是一對生意搭檔,他們住在不同的城市。由于生意上的需要,他們經(jīng)常會相互之間郵寄重要的貨物。為了保證貨物的安全,他們商定制作一個保險盒,將物品放入其中。他們打造了兩把相同的鑰匙分別保管,以便在收到包裹時用這個鑰匙打開保險盒,以及在郵寄貨物前用這把鑰匙鎖上保險盒。
上面是一個將重要資源安全傳遞到目的地的傳統(tǒng)方式,只要甲乙小心保管好鑰匙,那么就算有人得到保險盒,也無法打開。這個思想被用到了現(xiàn)代計算機通信的信息加密中。
3.常用算法
對稱加密的常用算法有:DES,3DES,AES
DES
簡介
DES全稱為Data Encryption Standard,即數(shù)據(jù)加密標準,是一種使用密鑰加密的塊算法,1977年被美國聯(lián)邦政府的國家標準局確定為聯(lián)邦資料處理標準(FIPS),并授權(quán)在非密級政府通信中使用,隨后該算法在國際上廣泛流傳開來。
算法原理
太過非人類,有興趣自行查閱相關資料。
算法特點
分組比較短、密鑰太短、密碼生命周期短、運算速度較慢。因為算法中有大量的位運算,一般在硬件中實現(xiàn)。
3DES
簡介
3DES(或稱為Triple DES)是三重數(shù)據(jù)加密算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。它相當于是對每個數(shù)據(jù)塊應用三次DES加密算法。由于計算機運算能力的增強,原版DES密碼的密鑰長度變得容易被暴力破解;3DES即是設計用來提供一種相對簡單的方法,即通過增加DES的密鑰長度來避免類似的攻擊,而不是設計一種全新的塊密碼算法。
AES
https://blog.csdn.net/lrwwll/article/details/78069013 有意思的博客
簡介
高級加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯(lián)邦政府采用的一種區(qū)塊加密標準。這個標準用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。經(jīng)過五年的甄選流程,高級加密標準由美國國家標準與技術(shù)研究院(NIST)于2001年11月26日發(fā)布于FIPS PUB 197,并在2002年5月26日成為有效的標準。2006年,高級加密標準已然成為對稱密鑰加密中最流行的算法之一。
不同于它的前任標準DES,Rijndael使用的是代換-置換網(wǎng)絡,而非Feistel架構(gòu)。AES在軟件及硬件上都能快速地加解密,相對來說較易于實作,且只需要很少的存儲器。作為一個新的加密標準,目前正被部署應用到更廣大的范圍。
算法原理
不討論。
設計思想
- 抵抗所有已知的攻擊。
- 在多個平臺上速度快,編碼緊湊。
- 設計簡單。
實際開發(fā)中使用AES加密需要注意的地方
- 服務端和我們客戶端必須使用一樣的密鑰和初始向量IV。
- 服務端和我們客戶端必須使用一樣的加密模式。
- 服務端和我們客戶端必須使用一樣的Padding模式。
以上三條有一個不滿足,雙方就無法完成互相加解密。
同時針對對稱加密密鑰傳輸問題這個不足:我們一般采用RSA+AES加密相結(jié)合的方式,用AES加密數(shù)據(jù),而用RSA加密AES的密鑰。同時密鑰和IV可以隨機生成,這要是128位16個字節(jié)就行,但是必須由服務端來生成,因為如果由我們客戶端生成的話,就好比我們客戶端存放了非對稱加密的私鑰一樣,這樣容易被反編譯,不安全,一定要從服務端請求密鑰和初始向量IV。
在python中使用
PyCrypto是 Python 中密碼學方面最有名的第三方軟件包??上У氖?,它的開發(fā)工作于2012年就已停止。
幸運的是,有一個該項目的分支PyCrytodome 取代了 PyCrypto 。
PyCrypto文檔: https://pycryptodome.readthedocs.io/en/latest/src/introduction.html
安裝與導入
-
完全替代老的
PyCrypto庫,你可以這樣安裝:pip install pycryptodome這種情況下,所有的模塊安裝在
Crypto包下。避免同時安裝
PyCrypto和PyCryptodome, 它們相互起沖突。因此,只有當您確定整個應用程序部署在virtualenv中時,才建議使用此選項。
-
一個獨立于老的
PyCrypto庫的方案,你可以這樣安裝:pip install pycryptodomex這種情況下,所有的模塊安裝在
Cryptodome包下。PyCrypto和PyCryptodome可以共存。
不同操作系統(tǒng)的安裝詳見官方文檔。
案例
DES案例
# DES 使用
import binascii
from Cryptodome.Cipher import DES
key = b'-8B key-' # key 必須是8個字節(jié)64位
# 創(chuàng)建一個密碼對象
# iv 參數(shù)也需要是一個8字節(jié)64位的二進制數(shù)的初始化向量
# DES.MOD_OFB加密模式
cipher = DES.new(key, DES.MODE_OFB, iv=b'12345179')
# 待加密數(shù)據(jù)
data = '我是心藍最帥無敵'.encode()
# 加密
msg = cipher.encrypt(data)
# 輸出二進制數(shù)據(jù)
print(msg)
# 輸出16進制字符串
print(binascii.b2a_hex(msg))
# 解密過程
# 創(chuàng)建一個新的密碼對象
# 模式,key,iv 和加密過程對應
cipher2 = DES.new(key, iv=cipher.iv, mode=DES.MODE_OFB)
# 解密
res = cipher2.decrypt(msg)
# 解碼成明文字符串
print(res.decode('utf-8'))
文檔參考:https://pycryptodome.readthedocs.io/en/latest/src/cipher/des.html
DES加密了解就好,幾乎沒人使用了。
3DES案例
# 3DES案例
from Cryptodome.Cipher import DES3
# 需要24個字節(jié)長度的key,一般會隨機生成
# 本質(zhì)上是三次des的key的串聯(lián),k1,k2,k3
# 當k1=k2=k3時,DES3降級為DES
key = b'12345678qwertyui12345678'
# 創(chuàng)建一個密碼對象
cipher = DES3.new(key, DES3.MODE_CFB)
# 原數(shù)據(jù)
data = '我是心藍'.encode()
# 加密
msg = cipher.encrypt(data)
print(msg)
# 解密過程
cipher2 = DES3.new(key, DES3.MODE_CFB, iv=cipher.iv)
# 解密
res = cipher2.decrypt(msg)
print(res.decode('utf-8'))
文檔參考:https://pycryptodome.readthedocs.io/en/latest/src/cipher/des3.html
AES案例
# AES案例
from Cryptodome.Cipher import AES
# 16個字節(jié)的密碼
key = b'1234567890123456'
# 創(chuàng)建加密對象
cipher = AES.new(key, AES.MODE_EAX)
data = '我是心藍'.encode()
# 加密
msg = cipher.encrypt(data)
print(msg)
# 解密 過程
# 創(chuàng)建一個新的密碼對象
cipher2 = AES.new(key, AES.MODE_EAX, nonce=cipher.nonce)
res = cipher2.decrypt(msg)
print(res.decode('utf-8'))
文檔參考:https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html
二、非對稱加密
1.定義
非對稱加密算法是一種密鑰的保密方法。
非對稱加密算法需要兩個密鑰:公開密鑰(publickey:簡稱公鑰)和私有密鑰(privatekey:簡稱私鑰)。公鑰與私鑰是一對,如果用公鑰對數(shù)據(jù)進行加密,只有用對應的私鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。
2.特點
非對稱密碼體制的特點:算法強度復雜、安全性依賴于算法與密鑰但是由于其算法復雜,而使得加密解密速度沒有對稱加密解密的速度快。對稱密碼體制中只有一種密鑰,并且是非公開的,如果要解密就得讓對方知道密鑰。所以保證其安全性就是保證密鑰的安全,而非對稱密鑰體制有兩種密鑰,其中一個是公開的,這樣就可以不需要像對稱密碼那樣傳輸對方的密鑰了。這樣安全性就大了很多。
事實上,公鑰加密算法很少用于數(shù)據(jù)加密,它通常只是用來做身份認證,因為它的密鑰太長,加密速度太慢--公鑰加密算法的速度甚至比對稱加密算法的速度慢上3個數(shù)量級(1000倍)。
3.工作原理
- A要向B發(fā)送信息,A和B都要產(chǎn)生一對用于加密和解密的公鑰和私鑰
- A的私鑰保密,A的公鑰告訴B;B的私鑰保密,B的公鑰告訴A。
- A要給B發(fā)送信息時,A用B的公鑰加密信息,因為A知道B的公鑰。
- A將這個消息發(fā)給B(已經(jīng)用B的公鑰加密消息)。
- B收到這個消息后,B用自己的私鑰解密A的消息。其他所有收到這個報文的人都無法解密,因為只有B才有B的私鑰。
4.常用算法
非對稱加密最常用,最廣泛的算法是:RSA
RSA
簡介
RSA加密算法是一種非對稱加密算法。在公開密鑰加密和電子商業(yè)中RSA被廣泛使用。RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們?nèi)硕荚?a target="_blank">麻省理工學院工作。RSA就是他們?nèi)诵帐祥_頭字母拼在一起組成的。今天只有短的RSA鑰匙才可能被強力方式解破。到目前為止,世界上還沒有任何可靠的攻擊RSA算法的方式。只要其鑰匙的長度足夠長,用RSA加密的信息實際上是不能被解破的。
算法原理
RSA算法基于一個十分簡單的數(shù)論事實:將兩個大質(zhì)數(shù)相乘十分容易,但是想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰。
RSA的加解密過程非常簡單:
| 公鑰 | (n, e) |
| 私鑰 | (n, d) |
| 加密 | 密文 = (明文^e)%n |
| 解密 | 明文 = (密文^d)%n |
RSA加密算法實現(xiàn)過程:https://www.cnblogs.com/coolYuan/p/9168284.html
RSA加密算法原理:http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
RSA加密算法python實現(xiàn):https://www.jb51.net/article/138018.htm
在python中使用
生成RSA密鑰
# 生成RSA密鑰
from Cryptodome.PublicKey import RSA
key = RSA.generate(2048)
private_key = key.export_key()
print(private_key)
file_out = open("private.pem", "wb")
file_out.write(private_key)
public_key = key.publickey().export_key()
print(public_key)
file_out = open("receiver.pem", "wb")
file_out.write(public_key)
使用RSA加密數(shù)據(jù)
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP
data = '我是心藍哈哈哈哈'.encode()
# 導入公鑰
recipient_key = RSA.import_key(open("receiver.pem").read())
# 創(chuàng)建一個密碼對象
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# 加密
msg = cipher_rsa.encrypt(data)
print(msg)
使用RSA解密數(shù)據(jù)
# 接上面代碼
# 導入私鑰
private_key = RSA.import_key(open("private.pem").read())
cipher_rsa = PKCS1_OAEP.new(private_key)
# 解密
res = cipher_rsa.decrypt(msg)
print(res.decode('utf-8'))
爬蟲中的用法
有些網(wǎng)站會在請求過程中發(fā)送公鑰,然后加密參數(shù)后傳回后臺來實現(xiàn)數(shù)據(jù)的加密。
import binascii
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP
data = '我是心藍哈哈哈哈'.encode()
# 網(wǎng)頁上收到的n值
pubkey_n = '8d7e6949d411ce14d7d233d7160f5b2cc753930caba4d5ad24f923a505253b9c39b09a059732250e56c594d735077cfcb0c3508e9f544f101bdf7e97fe1b0d97f273468264b8b24caaa2a90cd9708a417c51cf8ba35444d37c514a0490441a773ccb121034f29748763c6c4f76eb0303559c57071fd89234d140c8bb965f9725'
# e常常為65537
pbukey_e = 65537
# 生成公鑰
recipient_key = RSA.RsaKey(n=int(pubkey_n, 16), e=65537)
# 創(chuàng)建rsa對象
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# 加密數(shù)據(jù)
msg = cipher_rsa.encrypt(data)
print(msg)
# 輸出16進制字符串
print(binascii.b2a_hex(msg))
三、Hash算法
1.定義
一種算法的名稱,一般翻譯為哈希,或者散列。
它是把任意長度的輸入通過散列算法轉(zhuǎn)變成固定長度的輸出,該輸出就是散列值。
2.特點
- 不可逆: 無法通過散列值還原原來的數(shù)據(jù)
- 定長輸出:無論輸入的原始數(shù)據(jù)有多長,結(jié)果長度相同
- 抗修改:輸入微小的改變,會引起結(jié)果的巨大改變
- 強碰撞性:很難找到兩段不同的數(shù)據(jù),使他們產(chǎn)生相同的hash值
3.應用領域
- 一致性驗證
- 數(shù)字簽名
- 安全訪問認證
4.常用算法
md5
MD5消息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數(shù),可以產(chǎn)生出一個128位(16字節(jié))的散列值(hash value),用于確保信息傳輸完整一致。
SHA家族
安全散列算法(英語:Secure Hash Algorithm,縮寫為SHA)是一個密碼散列函數(shù)家族,是FIPS所認證的安全散列算法。能計算出一個數(shù)字消息所對應到的,長度固定的字符串(又稱消息摘要)的算法。
SHA家族的五個算法,分別是SHA-1、SHA-224、SHA-256、SHA-384,和SHA-512,由美國國家安全局(NSA)所設計,并由美國國家標準與技術(shù)研究院(NIST)發(fā)布;是美國的政府標準。后四者有時并稱為SHA-2。
5.在python中使用
import hashlib
# sha1 sha256 sha512 一樣的用法
info = '心藍老師最帥!'
m = hashlib.md5(info.encode()) # 注意傳入數(shù)據(jù)一定是二進制數(shù)據(jù)
res1 = m.hexdigest() # 輸出散列字符串
print(res1)
res2 = m.digest() # 輸出二進制數(shù)據(jù)
# 大數(shù)據(jù)的hash
big_m = hashlib.md5()
with open('movie.avi', 'rb') as f:
for line in f:
big_m.update(line) # 循環(huán)追加
print(big_m.hexdigest())
作業(yè)
將RSA的加密過程通過面向?qū)ο蟮姆绞綄懗梢粋€類,封裝起來。
只需要傳入一些參數(shù)就可以進行加密。
from Cryptodome.Cipher import AES, PKCS1_OAEP
from Cryptodome.PublicKey import RSA
class Rsa:
def __init__(self, pubkey, e=65537):
"""
create a Rsa object
:param pubkey:
public key
It can be an integer or a string.
When it is a string, its format is either
a hexadecimal string or a PEM format
:param e:
The general value is 65537.
"""
if isinstance(pubkey, int):
self.key = RSA.RsaKey(n=pubkey, e=e)
else:
if not isinstance(pubkey, str):
raise ValueError('pubkey must be str or int.')
if '----' in pubkey:
try:
self.key = RSA.import_key(pubkey)
except Exception as e:
print(e)
else:
if pubkey == pubkey.lower():
pubkey = int(pubkey, 16)
self.key = RSA.RsaKey(n=pubkey, e=e)
else:
pubkey = '-----BEGIN PUBLIC KEY-----\n' + pubkey + '\n-----END PUBLIC KEY-----'
try:
self.key = RSA.import_key(pubkey)
except Exception as e:
print(e)
def encrypt(self, data):
"""
encrypted data by rsa
:param data: Plaintext
:return: Binary ciphertext
"""
cipher_rsa = PKCS1_OAEP.new(self.key)
return cipher_rsa.encrypt(data)
class Aes:
def __init__(self, key):
"""
create a Aes object
:param key: key used for encryption
"""
if len(key) not in (16, 24, 32):
raise ValueError('Incorrect AES key length.')
if not isinstance(key, bytes):
raise ValueError('The key must be bytes.')
self.key = key
def encrypt(self, data, mode, *args, **kwargs):
"""
encrypted data by aes
:param data: Plaintext
:param mode: Encryption mode
:param args:
:param kwargs:
:return: Binary ciphertext
"""
cipher = AES.new(self.key, mode=mode, *args, **kwargs)
if not isinstance(data, bytes):
raise ValueError('The data must be bytes.')
return cipher.encrypt(data)
if __name__ == '__main__':
rsa = Rsa(pubkey=open("receiver.pem").read())
res = rsa.encrypt('我是心藍'.encode())
print(res)
print(len(res))