2020-03-30

加密技术定义、使用

在开发爬虫的过程中,我们经常遇到的一种反爬措施是数据加密。常见的加密算法可以分为三类:对称加密算法,非对称加密算法和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

安装与导入
  1. 完全替代老的PyCrypto库,你可以这样安装:

    pip install pycryptodome
    

    这种情况下,所有的模块安装在Crypto包下。

    避免同时安装PyCryptoPyCryptodome, 它们相互起冲突。

    因此,只有当您确定整个应用程序部署在virtualenv中时,才建议使用此选项。

  2. 一个独立于老的PyCrypto库的方案,你可以这样安装:

    pip install pycryptodomex
    

    这种情况下,所有的模块安装在Cryptodome包下。

    PyCryptoPyCryptodome可以共存。

不同操作系统的安装详见官方文档。

案例

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.工作原理

  1. A要向B发送信息,A和B都要产生一对用于加密和解密的公钥和私钥
  2. A的私钥保密,A的公钥告诉B;B的私钥保密,B的公钥告诉A。
  3. A要给B发送信息时,A用B的公钥加密信息,因为A知道B的公钥。
  4. A将这个消息发给B(已经用B的公钥加密消息)。
  5. 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))

你可能感兴趣的:(2020-03-30)