视频加载失败

常用模块之Crypto模块

3677 字
18 分钟
常用模块之Crypto模块

【一】对称加密算法(AES/DES)#

【1】对称加密算法介绍#

(1)什么是对称加密#

  • 对称加密又称但密钥加密,整个加密过程中只使用一个密钥。
  • 所谓对称其实就是使用一把密钥加密,使用同一把密钥解密。
  • 对称加密由于加解和解密使用的是同一个密钥算法,故而在加解密的过程中速度比较快,适合于数据量比较大的加解密。

(2)对称加密的优缺点#

  • 对称加密的主要有优点就是算法公开、计算量小、加密速度快、加密效率高;
  • 但是它也存在强大的缺点,缺点就是密钥协商过程中,一旦密钥泄露,别人可以获取到密钥,这样也能对密文进行解密。
  • 另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的独一密钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。

(3)常用的对称加密算法#

  • DES、3DES、AES、TDEA、Blowfish、RC2、RC4 和 RC5 等。

【2】DES加密算法#

(1)DES加密算法介绍#

  • DES 加密解密算法最初由美国 IBM 公司研究人员所设计发明,且为第一个公开的商用密码算法标准,自诞生以来便得到了 ISO 的一致认可。
  • DES 是分组密码算法的典型代表,它的明文分组长度为 64bits,密钥长度为 64bits,其中包括有 8bits 的奇偶校验,因此有效密钥长度为 56bits。
  • DES 加密解密算法使用的过程相同,且可以随时均都可以进行变动。
  • 它们其中有极少数被认为是易破解的弱密钥,但是很容易抛开它们不使用,因此其自身安全性主要依赖于有效密钥。

(2)DES加密算法原理#

  • DES 算法加密过程首先先对明文分组进行操作,需要加密的明文分为每块 64bits 的固定大小。
  • 下图所示左右两部分分别为64bits 的明文分组加密过程和其 16 个子密钥生成的过程。

【3】AES加密算法#

(1)AES加密算法介绍#

  • AES 加密算法为分组密码,分组长度为 128 位即 16 个字节,密匙长度有128、192 或 256 位,根据密匙 长 度的不同,加密的轮数也不同,本文采用长度为 128 位的密匙,加密轮数为 10 轮。
  • AES 加密算法不仅编码紧凑、设计简单而且可抵抗多种类型的攻击,其基本结构包括 4个部分。
  • 这四个部分分别为字节替换、行位移、列混合和轮密匙加。

[1] 字节替换(SubBytes)#

  • 字节替换也就是通 过 S-BOX 对字节元素进行非线性的变换,S-BOX 由有限域 GF(2 的 8 次方) 上的乘法求逆运算和仿射变换运算而来,通过查表的方式即可直接得到变换前后的字节元素,替换后字节元素至少有两位发生变换,能 充分打乱原来的字节元素,本文所介绍的 AES 加 密 算 法 就是对 S-BOX 进行改 进 而 来。
  • 具体替换规则为假设一字 节为 xy,则 S-BOX 中第x行第y列所对应的元素就是替换后的元素。

[2] 行位移(ShiftRows)#

  • 行位移是 AES 加密算法中的一个简单线性运算,即在 4 x 4 的状态矩阵中,把第i行循环左移i个字节(i=0, 1, 2, 3)。

[3] 列混合(MixColumns)#

  • 列混合是将状态矩阵中的每一列看成一个多项式,让其与一个固定的多项式 a(x) 相乘,再做模多项式 m(x) = x4(x的四次方) + 1 的运算,其中 a(x)=’03‘x3(x的3次方)+ ’01‘x2(x的平方)+ ‘01’x + ‘02’。

[4] 轮密匙加(AddRoundKey)#

  • 轮密匙加变换就是让状态矩阵与经过密匙扩展得到的子密匙做异或运算,因此轮密匙加变换的逆变换就是其本身,其中子密匙是原始密匙通过密匙扩展算法得到的。

(2)AES 算法流程#

  • AES 加密算法先将 128 位的明文进行分组,得到一个 4x4 的明文状态矩阵作为算法的输入,然后选取密匙矩阵先对明文状态矩阵做一次轮密匙加变换,再经过 10 轮的轮函数加密,轮函数操作依次为字节替换、行位移、列混合和轮密匙加,其中由于最后一轮的列混合不仅不会提高安全性,反而会拉低 算 法 运 算 速 度,故该轮丢弃列混合变换。
  • 解密算法仍为 10 轮,由于算法的4个轮操作均为可逆变换,因此解密过程就是用与加密过程同样的密匙对每一轮的加密操作进行逆运算。

【4】对称加密算法使用#

(1)安装#

Terminal window
pip install pycryptodome

(2)模式介绍#

  • AES 加密最常用的模式就是 ECB模式 和 CBC 模式,当然还有很多其它模式,他们都属于AES加密。
  • ECB模式和CBC 模式俩者区别就是
    • ECB 不需要 iv偏移量
    • 而CBC需要。
参数 作用及数据类型
1. 秘钥 加密的时候用秘钥,解密的时候需要同样的秘钥才能解出来; 数据类型为bytes
2. 明文 需要加密的参数; 数据类型为bytes
3. 模式 aes 加密常用的有 ECB 和 CBC 模式(我只用了这两个模式,还有其他模式);数据类型为aes类内部的枚举量
iv 偏移量 这个参数在 ECB 模式下不需要,在 CBC 模式下需要;数据类型为bytes
"""
长度
16: *AES-128*
24: *AES-192*
32: *AES-256*
MODE 加密模式.
常见的ECB, CBC
ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或或操作后再加密,这样做的目的是增强破解难度。
"""

(3)AES之ECB模式#

  • 参数限制。
    • 秘钥必须为16字节或者16字节的倍数的字节型数据。
    • 明文必须为16字节或者16字节的倍数的字节型数据
    • 如果不够16字节需要进行补全,关于补全规则,后面会在补全模式中具体介绍。
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
from typing import Any, Union
class AESBaseCipher(object):
def __init__(self, key: Union[None, str], iv: Union[None, str]) -> None:
'''
初始化参数
:param key: 声明key:key的长度必须是16的倍数
:param iv: 声明iv:iv是CBC模式特有的参数,为数据偏移量,iv的长度必须是16的倍数
'''
self.key = self.change_to_bytes(original_data=key)
self.iv = self.change_to_bytes(original_data=iv)
def pad_length(self, original_data: bytes) -> bytes:
'''
:param original_data: 原始数据(二进制)
:return: 补全长度后的二进制数据
'''
while len(original_data) % 16 != 0:
original_data += b'\0'
return original_data
def change_to_bytes(self, original_data: str) -> bytes:
'''
:param original_data: 原始数据(字符串)
:return: 转码后的二进制数据
'''
if self.__class__.__name__ == "ECBCipher":
pass
else:
assert len(original_data) == 16, f"参数必须是16位,当前长度为{len(original_data)}!"
return str(original_data).encode('utf-8')
class ECBCipher(AESBaseCipher):
def __init__(self, key: str, iv: Union[None, str]) -> None:
super().__init__(key=key, iv=iv)
def encrypt_data(self, original_data: str) -> Any:
'''
使用AES加密算法对数据进行加密
:param original_data: 原始数据
:return:
'''
# 【1】创建AES加密器
# 创建aes对象 aes对象 = AES.new(必须参数key, 密文加密模式, 偏移量iv)
cipher = AES.new(self.key, AES.MODE_ECB)
# 【2】处理原始数据
# 需要加密的密文(数据) , 且密文需要转码转换为二进制数据
bytes_data = original_data.encode('utf-8')
# 对数据进行补全, 补全规则为: 补全长度为16的倍数
# 方式一:自己计算长度并补齐
# bytes_data = self.pad_length(data=data)
# 方式二:自动补全数据长度,使用Padding模块中的pad函数进行补全
bytes_data_pad = pad(data_to_pad=bytes_data, block_size=AES.block_size)
# 【3】加密数据
encrypted_data = cipher.encrypt(bytes_data_pad)
# 【4】对加密后的数据进行base64编码
final_data = base64.b64encode(encrypted_data).decode()
# 【5】返回加密后的数据
return final_data
def decrypt_data(self, encrypted_data: str) -> Any:
'''
使用AES解密算法对数据进行解密
:param encrypted_data: 加密后的数据
:return:
'''
# 【1】创建AES解密器
# 创建aes对象 aes对象 = AES.new(必须参数key, 密文加密模式, 偏移量iv)
cipher = AES.new(self.key, AES.MODE_ECB)
# 【2】处理加密后的数据 - base64解码
aes_encrypted_data = base64.b64decode(encrypted_data)
# 【3】处理加密后的数据 - aes解码
decrypted_data = cipher.decrypt(aes_encrypted_data)
# 【4】去除填充的字节
decrypted_data = unpad(padded_data=decrypted_data, block_size=AES.block_size).decode('utf-8')
return decrypted_data
if __name__ == '__main__':
key = '1314521131452121'
iv = '8384582838458221'
data = "this is cipherData"
ecb = ECBCipher(key=key, iv=iv)
encrypted_data = ecb.encrypt_data(original_data=data)
print(encrypted_data) # f4EpeDDBTn3aJIezKEv9xL4n7rJlAvBh8SMXtrvxclc=
decrypt_data = ecb.decrypt_data(encrypted_data=encrypted_data)
print(decrypt_data) # this is cipherData

(4)AES之CBC模式#

  • 在Python中进行AES加密解密时
    • 所传入的密文、明文、秘钥、iv偏移量、都需要是bytes(字节型)数据。
    • python 在构建aes对象时也只能接受bytes类型数据。
  • 当秘钥,iv偏移量,待加密的明文,字节长度不够16字节或者16字节倍数的时候需要进行补全。
  • CBC模式需要重新生成AES对象,为了防止这类错误,无论是什么模式都重新生成AES对象就可以了。
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
from typing import Any, Union
class AESBaseCipher(object):
def __init__(self, key: Union[None, str], iv: Union[None, str]) -> None:
'''
初始化参数
:param key: 声明key:key的长度必须是16的倍数
:param iv: 声明iv:iv是CBC模式特有的参数,为数据偏移量,iv的长度必须是16的倍数
'''
self.key = self.change_to_bytes(original_data=key)
self.iv = self.change_to_bytes(original_data=iv)
def pad_length(self, original_data: bytes) -> bytes:
'''
:param original_data: 原始数据(二进制)
:return: 补全长度后的二进制数据
'''
while len(original_data) % 16 != 0:
original_data += b'\0'
return original_data
def change_to_bytes(self, original_data: str) -> bytes:
'''
:param original_data: 原始数据(字符串)
:return: 转码后的二进制数据
'''
assert len(original_data) == 16, f"参数必须是16位,当前长度为{len(original_data)}!"
return original_data.encode('utf-8')
class CBCCipher(AESBaseCipher):
def __init__(self, key: str, iv: str) -> None:
super().__init__(key=key, iv=iv)
def encrypt_data(self, original_data: str) -> Any:
'''
使用AES加密算法对数据进行加密
:param original_data: 原始数据
:return:
'''
# 【1】创建AES加密器
# 创建aes对象 aes对象 = AES.new(必须参数key, 密文加密模式, 偏移量iv)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
# 【2】处理原始数据
# 需要加密的密文(数据) , 且密文需要转码转换为二进制数据
bytes_data = original_data.encode('utf-8')
# 对数据进行补全, 补全规则为: 补全长度为16的倍数
# 方式一:自己计算长度并补齐
# bytes_data = self.pad_length(data=data)
# 方式二:自动补全数据长度,使用Padding模块中的pad函数进行补全
bytes_data_pad = pad(data_to_pad=bytes_data, block_size=AES.block_size)
# 【3】加密数据
encrypted_data = cipher.encrypt(bytes_data_pad)
# 【4】对加密后的数据进行base64编码
final_data = base64.b64encode(encrypted_data).decode()
# 【5】返回加密后的数据
return final_data
def decrypt_data(self, encrypted_data: str) -> Any:
'''
使用AES解密算法对数据进行解密
:param encrypted_data: 加密后的数据
:return:
'''
# 【1】创建AES解密器
# 创建aes对象 aes对象 = AES.new(必须参数key, 密文加密模式, 偏移量iv)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
# 【2】处理加密后的数据 - base64解码
aes_encrypted_data = base64.b64decode(encrypted_data)
# 【3】处理加密后的数据 - aes解码
decrypted_data = cipher.decrypt(aes_encrypted_data)
# 【4】去除填充的字节
decrypted_data = unpad(padded_data=decrypted_data, block_size=AES.block_size).decode('utf-8')
return decrypted_data
if __name__ == '__main__':
key = '1314521131452121'
iv = '8384582838458221'
data = "this is cipherData"
aes = CBCCipher(key=key, iv=iv)
encrypted_data = aes.encrypt_data(original_data=data)
print(encrypted_data) # f4EpeDDBTn3aJIezKEv9xL4n7rJlAvBh8SMXtrvxclc=
decrypt_data = aes.decrypt_data(encrypted_data=encrypted_data)
print(decrypt_data) # this is cipherData

【二】非对称加密算法#

【1】什么是非对称加密#

  • 加密和解密的秘钥不是同一个秘钥.
  • 这里需要两把钥匙. 一个公钥, 一个私钥.
    • 公钥发送给客户端
    • 发送端用公钥对数据进行加密
    • 再发送给接收端,
    • 接收端使用私钥来对数据解密.
  • 由于私钥只存放在接受端这边. 所以即使数据被截获了. 也是无法进行解密的.

【2】公钥和私密详解#

公钥和私钥

【3】常见的非对称加密算法#

  • RSA, DSA等等, 我们就介绍一个. RSA加密, 也是最常见的一种加密方案

【4】非对称加密算法使用#

(1)安装#

Terminal window
pip install pycryptodome

(2)RSA模式#

import os
from typing import Any
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
class AESBaseCipher(object):
def __init__(self) -> None:
self.BASE_DIR = os.path.dirname(__file__)
self.KEY_DIR = os.path.join(self.BASE_DIR, 'keys')
os.makedirs(self.KEY_DIR, exist_ok=True)
self.private_key_path = os.path.join(self.KEY_DIR, 'private.key')
self.public_key_path = os.path.join(self.KEY_DIR, 'public.key')
self.init_key()
def init_key(self) -> None:
'''
初始化公钥和私钥并保存
:return: 无
'''
# (1) 生成秘钥
# RSA 1024 是指公钥及私钥分别是 1024bit,也就是 1024/8 = 128Bytes.
# RSA 算法密钥长度的选择是安全性和程序性能平衡的结果,密钥长度越长,安全性越好,加密解密所需时间越长。
rsa_key = RSA.generate(1024)
# (2)将公钥和私钥分别写入到本地文件中
# 打开某个文件'rsa.publickey',模式为连续写入(wb),写入公钥rsakey.public_key().export_key()
with open(self.public_key_path, 'wb') as fp:
fp.write(rsa_key.public_key().export_key())
# 打开某个文件'rsa.privatekey',模式为连续写入(wb),写入公钥rsakey.export_key()
with open(self.private_key_path, 'wb') as fp:
fp.write(rsa_key.export_key())
def read_key(self, path: str) -> Any:
'''
读取公钥或私钥数据
:param path: 公钥或私钥的路径
:return: 返回读取到的公钥或私钥数据
'''
with open(path, mode='r') as f:
# 读取到公钥并存储
key = f.read()
return key
def init_rsa(self, path: str) -> Any:
'''
初始化rsa对象
:param path: 公钥或私钥的路径
:return: 初始化后的rsa对象
'''
# 【1】读取公钥和私钥数据
key = self.read_key(path)
# 【2】将读取到的公钥声明为下面加密算法中的公钥
rsa_public_key = RSA.importKey(key)
# 【3】创建rsa对象
rsa_obj = PKCS1_v1_5.new(rsa_public_key)
return rsa_obj
class RSACipher(AESBaseCipher):
def __init__(self):
super().__init__()
def encrypt_data(self, original_data: str) -> Any:
'''
加密数据
:param original_data: 需要加密的密文(数据) , 且密文需要转码转换为二进制数据,这里不需要补字符串长度
:return: 加密后的数据
'''
# 【1】需要加密的数据部分
bytes_original_data = original_data.encode()
# 【2】创建rsa对象
rsa_obj = self.init_rsa(path=self.public_key_path)
# 【3】用已经存在公钥的rsa对象加密数据
result = rsa_obj.encrypt(bytes_original_data)
# 【4】将加密后的密文数据进行base64编码,避免数据传输过程中造成数据的丢失
final_data = base64.b64encode(result).decode('utf8')
return final_data
def decrypt_data(self, original_data: str) -> str:
'''
解密数据
:param original_data: 拿到数据 这是base64编码后的编码数据
:return: 解密后的数据
'''
# 【1】创建rsa对象
rsa_obj = self.init_rsa(path=self.private_key_path)
# 【2】将base64编码后的数据进行解码
data_base64 = base64.b64decode(original_data)
# 【3】用已经存在私钥的rsa对象解密数据 data = rsa.decrypt(二进制密文数据, 固定参数None)
final_data = rsa_obj.decrypt(data_base64, None)
# 【4】将二进制数据转为字符串数据
final_data = final_data.decode('utf-8')
return final_data
if __name__ == '__main__':
rsa = RSACipher()
encrypt_data = rsa.encrypt_data(original_data="123456")
print(encrypt_data)
# ANgS8jhSoOq7wgiSHBSxkGoZFIaZKoOWqC+HpHuH3IIKee95Ttka0sUnqRVhx5U0Q94/ssf+Ahc+0NoN+fDfw50gHfopUXefeAfyDJ6lMkquNAAkETgJe10+nFxDXHWhBSVCRyKWnqEtO0Z8ZZAbpJFf419BVnB7fl/Krdau09o=
decrypt_data = rsa.decrypt_data(encrypt_data)
print(decrypt_data)
# 123456

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Profile Image of the Author
Dream
Hello, I'm Dream.
公告
欢迎来到我的博客!爱学习爱进步。
分类
标签
最新动态
站点统计
文章
300
分类
5
标签
26
总字数
640,882
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录