加密技术定义、使用
在开发爬虫的过程中,我们经常遇到的一种反爬措施是数据加密。常见的加密算法可以分为三类:对称加密算法,非对称加密算法和Hash算法(事实上不是加密算法而是摘要算法)
一、对称加密
1.定义
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。但是,加解密双方使用同样的密钥进行加密和解密。密钥是控制加密及解密的指令,算法是一种规则,规定如何进行加密和解密。因此加密的安全性不仅取决于加密算法本身,密钥管理的安全性更是重要。因为加密解密都使用同一个密钥,如何把密钥安全地传递到解密者手上就成了必须要解决的问题。
2.工作过程
下面举个例子来简要说明一下对称加密的工作过程。甲和乙是一对生意搭档,他们住在不同的城市。由于生意上的需要,他们经常会相互之间邮寄重要的货物。为了保证货物的安全,他们商定制作一个保险盒,将物品放入其中。他们打造了两把相同的钥匙分别保管,以便在收到包裹时用这个钥匙打开保险盒,以及在邮寄货物前用这把钥匙锁上保险盒。
上面是一个将重要资源安全传递到目的地的传统方式,只要甲乙小心保管好钥匙,那么就算有人得到保险盒,也无法打开。这个思想被用到了现代计算机通信的信息加密中。
3.常用算法
对称加密的常用算法有:DES,3DES,AES
DES
简介
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
算法原理
太过非人类,有兴趣自行查阅相关资料。
算法特点
分组比较短、密钥太短、密码生命周期短、运算速度较慢。因为算法中有大量的位运算,一般在硬件中实现。
3DES
简介
3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次DES加密算法。由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解;3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
AES
https://blog.csdn.net/lrwwll/article/details/78069013 有意思的博客
简介
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
不同于它的前任标准DES,Rijndael使用的是代换-置换网络,而非Feistel架构。AES在软件及硬件上都能快速地加解密,相对来说较易于实作,且只需要很少的存储器。作为一个新的加密标准,目前正被部署应用到更广大的范围。
算法原理
不讨论。
设计思想
- 抵抗所有已知的攻击。
- 在多个平台上速度快,编码紧凑。
- 设计简单。
实际开发中使用AES加密需要注意的地方
- 服务端和我们客户端必须使用一样的密钥和初始向量IV。
- 服务端和我们客户端必须使用一样的加密模式。
- 服务端和我们客户端必须使用一样的Padding模式。
以上三条有一个不满足,双方就无法完成互相加解密。
同时针对对称加密密钥传输问题这个不足:我们一般采用RSA+AES加密相结合的方式,用AES加密数据,而用RSA加密AES的密钥。同时密钥和IV可以随机生成,这要是128位16个字节就行,但是必须由服务端来生成,因为如果由我们客户端生成的话,就好比我们客户端存放了非对称加密的私钥一样,这样容易被反编译,不安全,一定要从服务端请求密钥和初始向量IV。
在python中使用
PyCrypto是 Python 中密码学方面最有名的第三方软件包。可惜的是,它的开发工作于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
可以共存。
不同操作系统的安装详见官方文档。
案例
DES案例
# DES 使用
import binascii
from Cryptodome.Cipher import DES
key = b'-8B key-' # key 必须是8个字节64位
# 创建一个密码对象
# iv 参数也需要是一个8字节64位的二进制数的初始化向量
# DES.MOD_OFB加密模式
cipher = DES.new(key, DES.MODE_OFB, iv=b'12345179')
# 待加密数据
data = '我是心蓝最帅无敌'.encode()
# 加密
msg = cipher.encrypt(data)
# 输出二进制数据
print(msg)
# 输出16进制字符串
print(binascii.b2a_hex(msg))
# 解密过程
# 创建一个新的密码对象
# 模式,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个字节长度的key,一般会随机生成
# 本质上是三次des的key的串联,k1,k2,k3
# 当k1=k2=k3时,DES3降级为DES
key = b'12345678qwertyui12345678'
# 创建一个密码对象
cipher = DES3.new(key, DES3.MODE_CFB)
# 原数据
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个字节的密码
key = b'1234567890123456'
# 创建加密对象
cipher = AES.new(key, AES.MODE_EAX)
data = '我是心蓝'.encode()
# 加密
msg = cipher.encrypt(data)
print(msg)
# 解密 过程
# 创建一个新的密码对象
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:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
2.特点
非对称密码体制的特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多。
事实上,公钥加密算法很少用于数据加密,它通常只是用来做身份认证,因为它的密钥太长,加密速度太慢--公钥加密算法的速度甚至比对称加密算法的速度慢上3个数量级(1000倍)。
3.工作原理
- A要向B发送信息,A和B都要产生一对用于加密和解密的公钥和私钥
- A的私钥保密,A的公钥告诉B;B的私钥保密,B的公钥告诉A。
- A要给B发送信息时,A用B的公钥加密信息,因为A知道B的公钥。
- A将这个消息发给B(已经用B的公钥加密消息)。
- B收到这个消息后,B用自己的私钥解密A的消息。其他所有收到这个报文的人都无法解密,因为只有B才有B的私钥。
4.常用算法
非对称加密最常用,最广泛的算法是:RSA
RSA
简介
RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。今天只有短的RSA钥匙才可能被强力方式解破。到目前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。
算法原理
RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
RSA的加解密过程非常简单:
公钥 | (n, e) |
私钥 | (n, d) |
加密 | 密文 = (明文^e)%n |
解密 | 明文 = (密文^d)%n |
RSA加密算法实现过程:https://www.cnblogs.com/coolYuan/p/9168284.html
RSA加密算法原理:http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
RSA加密算法python实现: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加密数据
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP
data = '我是心蓝哈哈哈哈'.encode()
# 导入公钥
recipient_key = RSA.import_key(open("receiver.pem").read())
# 创建一个密码对象
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# 加密
msg = cipher_rsa.encrypt(data)
print(msg)
使用RSA解密数据
# 接上面代码
# 导入私钥
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'))
爬虫中的用法
有些网站会在请求过程中发送公钥,然后加密参数后传回后台来实现数据的加密。
import binascii
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP
data = '我是心蓝哈哈哈哈'.encode()
# 网页上收到的n值
pubkey_n = '8d7e6949d411ce14d7d233d7160f5b2cc753930caba4d5ad24f923a505253b9c39b09a059732250e56c594d735077cfcb0c3508e9f544f101bdf7e97fe1b0d97f273468264b8b24caaa2a90cd9708a417c51cf8ba35444d37c514a0490441a773ccb121034f29748763c6c4f76eb0303559c57071fd89234d140c8bb965f9725'
# e常常为65537
pbukey_e = 65537
# 生成公钥
recipient_key = RSA.RsaKey(n=int(pubkey_n, 16), e=65537)
# 创建rsa对象
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# 加密数据
msg = cipher_rsa.encrypt(data)
print(msg)
# 输出16进制字符串
print(binascii.b2a_hex(msg))
三、Hash算法
1.定义
一种算法的名称,一般翻译为哈希,或者散列。
它是把任意长度的输入通过散列算法转变成固定长度的输出,该输出就是散列值。
2.特点
- 不可逆: 无法通过散列值还原原来的数据
- 定长输出:无论输入的原始数据有多长,结果长度相同
- 抗修改:输入微小的改变,会引起结果的巨大改变
- 强碰撞性:很难找到两段不同的数据,使他们产生相同的hash值
3.应用领域
- 一致性验证
- 数字签名
- 安全访问认证
4.常用算法
md5
MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
SHA家族
安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。
SHA家族的五个算法,分别是SHA-1、SHA-224、SHA-256、SHA-384,和SHA-512,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布;是美国的政府标准。后四者有时并称为SHA-2。
5.在python中使用
import hashlib
# sha1 sha256 sha512 一样的用法
info = '心蓝老师最帅!'
m = hashlib.md5(info.encode()) # 注意传入数据一定是二进制数据
res1 = m.hexdigest() # 输出散列字符串
print(res1)
res2 = m.digest() # 输出二进制数据
# 大数据的hash
big_m = hashlib.md5()
with open('movie.avi', 'rb') as f:
for line in f:
big_m.update(line) # 循环追加
print(big_m.hexdigest())
作业
将RSA的加密过程通过面向对象的方式写成一个类,封装起来。
只需要传入一些参数就可以进行加密。
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))