目录
- 一、编码与加密原理
-
- 1.1 ASCII 编码
- 1.2 详解 Base64
-
- 1.2.1 Base64 的编码过程和计算方法
- 1.2.2 基于编码的反爬虫设计
- 1.2.3 Python自带base64模块实现base64编码解码类封装
- 1.3 MD5消息摘要算法
-
- 1.3.1 MD5 介绍
- 1.3.2 Python实现md5以及其他常用消息摘要算法封装
- 1.4 对称加密与 AES
-
- 1.4.1 介绍
- 1.4.2 Python实现对称加密算法封装
- 1.5 非对称加密与 RSA
-
- 1.5.1 介绍
- 1.5.2 Python实现非对称加密算法(rsa)封装
- 二、Crypto-JS
-
- 2.1 nodejs之md5、sha1、sha256、base64
- 2.2 Crypto-Js之AES
- 2.3 Crypto-Js之DES
- 2.4 Crypto-Js之3DES
本文是对 2024最新版JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现 一文进行查漏补缺,会进行一些算法细节以及 python 实现算法的补充。
一、编码与加密原理
1.1 ASCII 编码
字符集是指各国家的文字、标点符号、图形符号和数字等字符的集合。计算机要准确地处理不同字符集,就需对字符进行编码。ASCII ((American Standard Code for Information Interchange):美国信息交换标准代码)是基于拉丁字母 的一套电脑 编码 系统,主要用于显示现代 英语 和其他 西欧 语言。它是最通用的 信息交换 标准,并等同于 国际 标准ISO/IEC 646。ASCII 第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符 [1] 。ASCII 编码实际上约定了字符和二进制的映射关系,如小写字母 'A'
对应的 8 位二进制数为 01000001。因此,我们也可以将它看作二进制与拉丁字符的映射表。
ASCII 的 RFC 文档编号为 20(详见 https://datatracker.ietf.org/doc/html/rfc20),其中约定了 ASCII 的使用范围、标准码、字符表示和代码识别等内容。ASCII 标准码如下图所示:
ASCII 码默认使用 7 位二进制数来表示所有的大写字母、小写字母、数字(0 ~ 9)、标点符号和特殊的控制符。 字符表示和代码识别部分约定了字符与二进制的映射关系,在 ASCII 标准码中,b7 为高位,b1 为低位。 例如字符 )
在标准码表中的第 9 行第 2 列。按照 b7 到 b1 的高低位排序,那么字符 )
的 7 位二进制表示如下图所示:
假设我们用 →
符号表示映射关系,那么有 00101001→)
。RFC 20 中约定,在 8 位字节中最高位始终为 0。也就是说,我们也可以用 8 位二进制来表示字符 )
,如下图所示:
即 00101001→)
。除了二进制表示之外,我们通常还会用到十进制表示和十六进制表示。有网友整理了多种进制与字符映射关系的对照表,如下图所示:
1.2 详解 Base64
Base64 基于 64 个可打印字符来表示 8 位二进制数据,它是网络中常见的编码方式。Base64 的出现是为了解决不可打印的字符(如非英文的字符)在网络传输过程中造成的乱码现象。字符 amoxiang
使用 Base64 编码后,得到 YW1veGlhbmc=
,我们很难 读
懂编码后的字符表示的内容。很多反爬虫设计会将 Base64 编码也纳入其中,这正是考虑到编码结果的不可读性。虽然从 Base64 编码结果可以推导出编码前的字符,但它的迷惑作用还是非常大的。
1.2.1 Base64 的编码过程和计算方法
Base64 的 RFC 文档编号为 4648,文档地址为 https://tools.ietf.org/html/rfc4648。RFC4648 约定了 Base16、Base32 和 Base64 的编码规范和计算方法。将字符进行 Base64 编码时,首先要将字符转换成对应的 ASCII 码,然后得出 8 位二进制数,接着连接 3 个 8 位输入,形成字节数为 24 的输入组,再将 24 位输入组拆分成 4 组 6 位的二进制数,然后将 6 位二进制数转换为十进制数,最后找到十进制数在 Base64 编码表中对应的字符,并将这些字符组合成新的字符串,这个字符串就是编码结果。编码过程中用到的 Base64 编码表下图所示:
要注意的是,在编码过程中,如果字符位数少于 24 位,那么就需要进行特殊处理,也就是在编码结果的末尾用 =
符号填充。我们可以通过一个例子来加深对 Base64 编码过程的理解。首先,我们将字符 async 转换成 ASCII 码,并找到对应的 8 位二进制数。字符、ASCII 码和 8 位二进制数的对应值如下所示:
接着将 3 组 8 位二进制数连接成 24 位的输入组,再将 24 位输入组拆分成 4 组 6 位的二进制数。要注意的是,如果输入组的元素不足 24 位,那么就用 0 进行填充。24 位输入组转换成 6 位二进制数的过程如下图所示:
得到 6 位二进制数之后,我们还需要计算出对应的十进制。二进制转十进制其实是按权相加,将二进制数写成加权系数展开式,并按十进制加法规则求和。字符 a
对应的6位二进制数为 011000,将其转换成十进制时,计算过程如下图所示:
进制间的转换,如果不太明白的,可以参考文章:计算机组成原理之数据的表示和运算(一)。按照这个计算方法,计算其他的 6 位二进制数,最后得到字符 async
对应的十进制值:
24 23 13 57 27 38 12 65
补位字符 =
没有对应的值,约定其值为 65。在得到所有的十进制值之后,就可以将其与 RFC4648 中的 Base64 编码表进行映射,从而得出编码后的字符串。映射过程如下图所示:
最终得出字符 async
的 Base64 编码结果为 YXN5bmM=
,完整的编码过程如下图所示:
Base64 编码时所用的对照表是固定的,也就是说它的编码过程是可逆的。这意味着我们只需要将编码的流程倒置,就能够得解码的方法。Base64 编码表中的 +
和 /
会影响文件编码和 URI 编码,我们在实际使用时,需要考虑到应用场景中是否包含文件编码或 URI 。如果在 URI 场景下使用 Base64,就会引起错误,RFC4648 文档中给出了一个解决办法:使用 -
和 _
替代 +
和 /
。
1.2.2 基于编码的反爬虫设计
在 1.2.1 Base64 的编码过程和计算方法
中,学习了 Base64 编码的相关知识,了解到编码过程以及使用到的对照表。Base64 被广泛应用在互联网中,有经验的爬虫工程师看到带有 ==
符号或者 =
符号的字符串时,自然就会认为这是 Base64 编码字符串后得到的结果。如:d3d3Lmh1YXdlaS5jb20=、d3d3Lmp1ZWppbi5pbQ==。这时候,爬虫工程师只需要按照 Base64 解码规则进行倒推,就能得到原字符。很多编程语言有 Base64 解码模块,解码不费吹灰之力。我们可以使用 Python 解码上方的字符串:
from base64 import b64decodecode = ['d3d3Lmh1YXdlaS5jb20=', 'd3d3Lmp1ZWppbi5pbQ==']
for c in code:string = b64decode(c).decode('utf8')print(string)
代码运行后,输出结果如下:
www.huawei.com
www.juejin.im
爬虫工程师很轻松就拿到了原字符,这显然不是开发者想要见到的结果。其实,开发者还可以通过自定义编码规则的方式保护数据。只需要稍微改动一下 Base64 编码过程中用到的对照表,或者改动输入组的划分规则,就可以创造一个新的编码规则。Base64 编码和解码时都是将原本 8 位的二进制数转换成 6 位的二进制数。如果我们改动位数,将其设置为 5 位或者 4 位,那么就可以实现新的编码规则,对应的 Python 代码如下:
class Custom64:comparison = {'0': 'A', '1': 'B', '2': 'C', '3': 'D', '4': 'E','5': 'F', '6': 'G', '7': 'H', '8': 'I', '9': 'J','10': 'K', '11': 'L', '12': 'M', '13': 'N', '14': 'O','15': 'P', '16': 'Q', '17': 'R', '18': 'S', '19': 'T','20': 'U', '21': 'V', '22': 'W', '23': 'X', '24': 'Y','25': 'Z', '26': 'a', '27': 'b', '28': 'c', '29': 'd','30': 'e', '31': 'f', '32': 'g', '33': 'h', '34': 'i','35': 'j', '36': 'k', '37': 'l', '38': 'm', '39': 'n','40': 'o', '41': 'p', '42': 'q', '43': 'r', '44': 's','45': 't', '46': 'u', '47': 'v', '48': 'w', '49': 'x','50': 'y', '51': 'z', '52': '0', '53': '1', '54': '2','55': '3', '56': '4', '57': '5', '58': '6', '59': '7','60': '8', '61': '9', '62': '+', '63': '/', '65': '=',}def encode(self, value: str, threshold: int = 4) -> str:# 对传入的字符进行编码,并返回编码结果value = ''.join(['0' + bin(ord(t))[2:] for t in value])inputs = self.shift(value, threshold)result = ''for i in inputs:if i == '0' * threshold:# 全为0则视为补位encoding = 65else:encoding = 0for key, v in enumerate(i):# 二进制数按权相加得到十进制数val = int(v) * pow(2, len(i) - 1 - key)encoding += val# 从对照表中取值after = self.comparison.get(str(encoding))result += afterreturn resultdef decode(self, value: str, threshold: int, group: int = 8) -> str:"""对传入的字符串解码,得到原字符"""result = []coder = self.str2binary(value, threshold=threshold)bins = self.shift(''.join(coder), group)for i in range(len(bins)):binary = ''.join(bins)[i * group: (i + 1) * group]if binary != '0' * group:# 如果全为0则视为补位,无需处理result.append(''.join([chr(i) for i in [int(b, 2) for b in binary.split(' ')]]))return ''.join(result)def str2binary(self, value: str, threshold: int = 6) -> list:"""字符串转十进制再转二进制"""result = []values = self.str2decimal(value)for i in values:# 判断是否为补位if i == '65':val = '0' * thresholdelse:val = '{:0{threshold}b}'.format(int(i), threshold=threshold)result.append(val)return result@staticmethoddef shift(value: str, threshold: int, group: int = 24) -> list:"""位数转换"""remainder = len(value) % groupif remainder:# 如果有余数,则说明需要用0补位padding = '0' * (group - remainder)value += padding# 按照threshold值切割字符result = [value[i:i + threshold] for i in range(0, len(value), threshold)]return resultdef str2decimal(self, value: str) -> list:"""使用Base64编码表做对照,取出字符串对应的十进制数"""keys = []for t in value:for k, v in self.comparison.items():if v == t:keys.append(k)return keysif __name__ == '__main__':# threshold 的值建议为 4/5/6cus = Custom64()encode_res = cus.encode('async', threshold=5)decode_res = cus.decode(encode_res, threshold=5)print(encode_res)print(decode_res)
类 Custom64
完成了自定义位数的编码和解码功能。首先,用字典实现 RFC4648 中的 Base64 编码表。然后定义了用于编码字符串的方法 encode() 和用于解码的方法 decode(),还有其他用于转换的方法。使用参数传递的方式就可以在 Base64 编码和自定义编码之间进行切换。代码运行后,输出结果如下:
MFZXSbTD=A
async
如果我们将 threshold 的值改为 6,那么 Custom64 就等同于原始的 Base64 编码,对应的输出结果如下:
YXN5bmM=
async
当我们使用 Custom64 对字符 async
进行编码时,只要设置 threshold 的值不为 6,得到的编码结果就是不相同的。如果爬虫工程师使用 Base64 对该编码结果进行解码,那么他将无法得到正确的原字符。这不仅达到了保护数据的目的,还能够迷惑爬虫工程师,使其将时间花费在 Base64
解码不成功的问题上。
1.2.3 Python自带base64模块实现base64编码解码类封装
# -*- coding: utf-8 -*-
# @Time : 2024-04-30 01:57
# @Author : AmoXiang
# @File : md5、base64、sha
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import base64class Base64Util(object):@staticmethoddef base64_encrypt_text(decrypt_text: str) -> str:"""Bse64加密:param decrypt_text: 明文:return: 密文"""return base64.b64encode(decrypt_text.encode('utf-8')).decode()@staticmethoddef base64_decrypt_text(encrypt_text: str) -> str:"""Bse64解密:param encrypt_text: 密文:return: 明文"""return base64.b64decode(encrypt_text).decode('utf-8')if __name__ == '__main__':# 测试print(Base64Util.base64_encrypt_text("async"))print(Base64Util.base64_decrypt_text("YXN5bmM="))# node.js中: console.log(global.btoa('async'));# node.js中: console.log(global.atob('YXN5bmM='));
1.3 MD5消息摘要算法
1.3.1 MD5 介绍
MD5 消息摘要算法(MD5 Message-Digest Algorithm,简称 MD5) 是一种被广泛使用的散列函数,它能够将任意长度的消息转换成 128 位的消息摘要。与 Base64 编码不同的是,MD5 是不可逆的,这意味着我们可以将字符串转换为 MD5 值,但无法将 MD5 值转换成原字符串。MD5 的 RFC 文档编号为 1321,文档地址为 https://tools.ietf.org/html/rfc1321。RFC1321 约定了一些术语和符号,并描述了 MD5 算法的计算步骤和方法。消息摘要的计算共有 5 个步骤。
1.Append Padding Bits。
2.Append Length。
3.Initialize MD Buffer。
4.Process Message in 16-Word Blocks。
5.Output。
第5步将输出 128 位的消息摘要。如果我们要生成 async
的消息摘要,那么就需要先将其转换为二进制数,如下:
01100001 01110011 01111001 01101110 01100011
RFC1321 将消息位数称为 b
,此处为 40。接着需要按照 RFC1321 中 Append Padding Bits 的描述附加填充位,在第 b+1
位填充 1,并在第 b+2
位到第 448 位填充 0,总共得到 448 位。接着按照 Append Length 的描述,计算原始信息长度与 264 的模,也就是 b mod 264,此处为 40 mod 264,计算结果为 40。将此值转换为 64 位二进制数,即:
00101000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
将该 64 位二进制数追加到 448 位之后得到共 512 位的二进制数,然后将这 512 位的二进制数进行分组,分组可以用 M[0,…,N-1] 表示,其中 N 为 16 的倍数,最后得到 16 组 32 位的分组结果。Append Padding Bits 和 Append Length 的过程如下图所示:
至此,我们就完成了消息摘要算法的前两个步骤。Initialize MD Buffer 中给出了用于消息摘要计算的 4 个常数:
word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10
Process Message in 16-Word Blocks 中给出了具体的计算步骤和所需的其他条件。首先,定义 4 个用于计算的函数:
F(X,Y,Z) = XY v not(X) Z
G(X,Y,Z) = XZ v Y not(Z)
H(X,Y,Z) = X xor Y xor Z
I(X,Y,Z) = Y xor (X v not(Z))
每个函数都将 3 个 32 位的字符作为输入,计算后输出 1 个 32 位的字符作为计算结果。这一步使用了一个具有 64 个元素的表 , 为 [1, …, 64]。 表示表格的第个元素,计算公式为:T[i] = 232 × abs(sin(i)),具体的代码实现,去看 https://datatracker.ietf.org/doc/html/rfc1321 将最后得到的 A、B、C、D 按照从 A 的低位到 D 的高位进行排序,就能得到 MD5 算法的输出。相比 Base64 编码,MD5 的运算过程要复杂很多。由于 MD5 在运算过程中使用了补位、追加和移位等操作,所以他人无法从输出结果倒推出输入字符,这个特性被称为 不可逆
。MD5 可以对任意长度的消息进行运算,输出固定位数(128 位)的结果,这个特性被称为 压缩
。MD5 的典型应用场景就是一致性验证,如文件一致性和信息一致性。文件一致性验证常常出现在软件下载站点或操作系统镜像下载站点,文件下载场景如下图所示:
服务器端到客户端的文件传输通常是分片进行的,传输过程有可能出现文件分片缺失或不完整的情况。MD5 在此场景下的作用是验证下载后的文件是否与服务器端文件一致,及时发现文件缺失现象。客户端在文件下载完成后计算该文件的 MD5 值,并与服务器端给出的 MD5 值进行对比,如果值相同,则代表文件完整,反之则代表文件有缺失。信息一致性的验证,签名验证就是信息一致性的一种应用,其场景如下图所示:
客户端原始数据为 56 asc NBA 1997 3389 asyncins
,在计算消息摘要之前,按照规则将原始数据中的所有数字都加上 1,得到 57 asc NBA 1998 3390 asyncins
。然后使用消息摘要算法 MD5 对处理后的字符串进行计算,得到 bfefc089916d08c2e1d756ed54f31e88
,并将该 MD5 值与原始数据上传到服务器端。接着服务器端将原始消息按照相同的规则进行处理,将服务器端计算得到的MD5 值与客户端上传的 MD5 值进行对比,如果值相同,则代表数据未被篡改。
MD5 算法具有 不可逆和压缩 这两种特性,而 MD5 算法输出的值则具有 不可读 的特性,这也使其成为密码保存的不二之选。Web网站将密码保存到数据库的时候,通常是存储密码字符串的 MD5 值,如下图所示:
程序会计算用户在注册时输入的字符串 hELLo3306
的 MD5 值,然后将该值作为用户的 密码
保存在数据库中。当用户登录时,程序会计算用户输入的 hELLo3306
的 MD5 值,并将该值与数据库中存储的相同用户的 密码
进行对比,如果值相同,则代表密码正确。由于 MD5 算法的 不可逆 特性、压缩特性 和 MD5 值的 不可读特性,所以即使密码(即 729db332525afdfdb75bb3aec0302ca4) 泄露,也不会影响账户的安全性。这正是 MD5 算法被广泛应用于密码存储、校验场景的根本原因。MD5 是消息摘要算法中的一种,同样计算消息摘要的算法还有 SHA1 (详见 RFC3174) 和 SHA256(详见 RFC4634) 等。
1.3.2 Python实现md5以及其他常用消息摘要算法封装
代码运行前,请安装 pycrypto 以及 pycryptodome 库,安装命令如下:
pip install pycrypto
pip install pycryptodome
# 安装pycrypto失败,解决方案如下(应该是只安装pycryptodome模块也行,读者嫌麻烦,可以直接安装 pycryptodome):
1.安装Visual Studio2.将Visual Studio中的stdint.h文件复制
从 C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include
目录下找到 stdint.h 文件,然后复制到
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt
(该目录报错的时候会提示,读者的目录名称不一定与我一致,一定要看安装过程中报错的信息提示) 目录下3.进入目录:C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt修改文件 inttypes.h 将文件中的内容 #include <stdint.h> 改为 #include "stdint.h"(我是在第14行)4.更改成功后,保存退出。再次使用命令 pip install pycrypto 成功
pycryptodome API documentation: https://pycryptodome.readthedocs.io/en/latest/src/api.html
示例代码:
# -*- coding: utf-8 -*-
# @Time : 2024-04-30 01:57
# @Author : AmoXiang
# @File : md5、sha1、hmac
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680from hashlib import md5
from hashlib import sha1
import hmacfrom Crypto.Hash import MD5
from Crypto.Hash import SHA1 # 使用 pip 安装 pycrypto、pycryptodomeclass Md5Sha1HmacUtil(object):@staticmethoddef md5_encrypt_text(decrypt_text: str) -> str:"""python自带原生包md5加密:param decrypt_text: 明文:return: 密文"""return md5(decrypt_text.encode('utf8')).hexdigest()@staticmethoddef md5_encrypt_text2(decrypt_text: str) -> str:"""使用第三方模块pycryptodome进行md5加密:param decrypt_text: 明文:return: 密文"""return MD5.new(decrypt_text.encode('utf8')).hexdigest()@staticmethoddef sha1_encrypt_text(decrypt_text: str) -> str:"""python自带原生包SHA1加密:param decrypt_text: 明文:return: 密文"""return sha1(decrypt_text.encode('utf8')).hexdigest()@staticmethoddef sha1_encrypt_text2(decrypt_text: str) -> str:"""使用第三方模块pycryptodome进行sha1加密:param decrypt_text: 明文:return: 密文"""_sha1 = SHA1.new()_sha1.update(decrypt_text.encode('utf8'))return _sha1.hexdigest()@staticmethoddef hmac_encrypt_text(decrypt_text: str, key: str) -> str:"""hmac更加安全,通过密钥:param decrypt_text: 明文:param key: 密钥:return: 密文"""mac = hmac.new(key.encode('utf-8'), decrypt_text.encode('utf-8'), md5)# mac = hmac.new(key.encode('utf-8'), decrypt_text.encode('utf-8'), sha1)return mac.hexdigest()if __name__ == '__main__':# 测试print(Md5Sha1HmacUtil.md5_encrypt_text("hELLo3306"))print(Md5Sha1HmacUtil.md5_encrypt_text2("hELLo3306"))print(Md5Sha1HmacUtil.sha1_encrypt_text2("hELLo3306"))print(Md5Sha1HmacUtil.sha1_encrypt_text("hELLo3306"))
1.4 对称加密与 AES
1.4.1 介绍
加密和解密时使用同一个密钥的加密方式叫作对称加密,使用不同密钥的是非对称加密,它们的区别如下图所示:
相对于非对称加密来说,对称加密的速度更快,速度的优势使得它更适合大量数据加密的场景。常见的对称加密算法有 DES、3DES、BLOWFISH、RC5 和 AES 等。目前应用最为广泛、强度最高的是 AES,其全称为 advanced encryption standard(高级加密标准),在密码学中被称为 Rijndael 加密算法。接下来,我们将通过学习 AES 的运算过程和原理来了解对称加密算法。RFC 并未收录 AES,AES 文档可以在美国国家标准技术研究院网站上找到(详见 http://csrs.nist.gov/publications/fips/fips197/fips-192.pdf)。AES 的加密和解密过程如下图所示:
密钥是整个过程中最关键的部分,只有拿到加密时使用的密钥,才能够将密文转换为加密前的明文。明文需要经过多轮运算后才能得到密文,密文也需要经过多轮运算后才能够得到明文,运算轮次与密钥的长度有关。密钥的长度可以是 128 位、192 位或者 256 位,长度越大,需要运算的轮次就越多,密钥长度和运算轮次的关系如下图所示:
以密钥长度 128 位为例,密钥将会分为 4 组(Nk),即 4 个数据块(Nb),每组 32 位,对应的轮次(Nr)为 10 轮(192 位和 256 位对应的分别是 12 轮和 14 轮)。运算在二维字节数组上进行,该数组被称为 state。state 由 4 行字节组成,每行包含 Nb(Nb 为密钥长度除以 32)字节。加密操作围绕着 state 进行,经过对应轮次运算后输出的结果即为密文。输入、数组和密文的关系如下图所示:
矩阵中字节的排列顺序为从上到下,从左到右。运算包含以下几个步骤:
- SubBytes:替换操作,通过非线性函数将 state 中的字节替换成 S-box 表中对应的字节。
- ShiftRows:行移位操作,将 state 中每行的字节按照规律进行移位。
- MixColumns:列混合操作,通过线性函数对 state 中的列进行混合。
- MixColumns:列混合操作,通过线性函数对 state 中的列进行混合。
SubBytes 步骤中用到的 S-box 表如下图所示:
替换时,只需要根据原字节的值在 S-box 表中定位新的值即可。SubBytes 替换操作如下图所示:
行移位操作按照左循环进行,如下图所示。state 的第 0 行左移 0 字节,第 1 行左移 1 字节,第 2 行左移 2 字节,第 3 行左移 3 字节。
列混合操作是矩阵相乘。经过替换和移位的 state 与固定值的矩阵相乘,得到新的 state,如下图所示:
按位异或操作其实是将密钥与 state 进行异或操作,如下图所示:
解密过程是加密过程的逆操作。加密时,在 SubBytes 步骤中用到 S-box,解密时则需要用到 AES 文档提供的 Inverse S-box 表,如下图所示:
AddRoundKey 步骤中的操作在加密和解密时是相同的,其他解密步骤可按照 AES 文档介绍到的 InvShiftRows、InvMixCloumns 进行。从 AES 文档的介绍中我们知道,AES 加密过程中的操作都是可逆的,关键就在于密钥。DES 和 3DES 等对称加密算法的关键也是密钥。
1.4.2 Python实现对称加密算法封装
大部分编程语言有成熟的 AES 库,如 JavaScript 语言的 CryptoJS 库,Python 语言的 pycryptodome 库,我们可以使用这些库快速地实现加密需求。pycryptodome 库实现 AES 加密和解密的代码如下:
# -*- coding: utf-8 -*-
# @Time : 2024-04-30 03:49
# @Author : AmoXiang
# @File : AES、DES、Des3
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680import base64
from binascii import b2a_hex, a2b_hexfrom Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Cipher import DES3
from Crypto.Cipher import DESclass AesUtil(object):@staticmethoddef aes_encrypt_text(decrypt_text: str, key: str, iv="", model="CBC", method="base64") -> str:"""AES加密:param decrypt_text: 明文:param key: 密钥:param model: 加密模式: CBC, ECB:param iv: 密钥偏移量,只有CBC模式有:param method: 用base64加密还是16进制字符串:return: 密文"""if model == 'CBC':aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))else:aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)encrypt_text = aes.encrypt(pad(decrypt_text.encode('utf-8'), AES.block_size, style='pkcs7'))if method == "base64":return base64.b64encode(encrypt_text).decode()else:return b2a_hex(encrypt_text).decode()@staticmethoddef aes_decrypt_text(encrypt_text: str, key: str, iv="", model="CBC", method="base64") -> str:"""AES解密:param encrypt_text: 密文:param key: 密钥:param model: 解密模式: CBC, ECB:param iv: 密钥偏移量,只有CBC模式有:param method: 用base64解密还是16进制字符串:return:解密后的数据"""if model == 'CBC':aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))else:aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)if method == "base64":decrypt_text = aes.decrypt(base64.b64decode(encrypt_text)).decode('utf8')else:decrypt_text = aes.decrypt(a2b_hex(encrypt_text)).decode('utf8')return decrypt_textclass DesUtil(object):@staticmethoddef des_encrypt_text(decrypt_text: str, key: str, iv="", model="CBC", method="base64") -> str:"""DES加密:param decrypt_text: 明文:param key: 密钥:param model: 加密模式: CBC, ECB:param iv: 密钥偏移量:param method: 用base64加密还是16进制字符串:return: 加密后的数据"""if model == 'CBC':des_obj = DES.new(key[:8].encode('utf-8'), DES.MODE_CBC, iv.encode('utf-8'))else:des_obj = DES.new(key[:8].encode('utf-8'), DES.MODE_ECB)encrypt_text = des_obj.encrypt(pad(decrypt_text.encode('utf-8'), DES3.block_size, style='pkcs7'))if method == "base64":return base64.b64encode(encrypt_text).decode()else:return b2a_hex(encrypt_text).decode()@staticmethoddef des_decrypt_text(encrypt_text: str, key: str, iv="", model="CBC", method="base64") -> str:"""DES解密:param encrypt_text: 密文:param key: 秘钥:param model: 解密模式: CBC, ECB:param iv:秘钥偏移量:param method: 用base64解密还是16进制字符串:return:解密后的数据"""if model == 'CBC':des_obj = DES.new(key[:8].encode('utf-8'), DES.MODE_CBC, iv.encode('utf-8'))else:des_obj = DES.new(key[:8].encode('utf-8'), DES.MODE_ECB)if method == "base64":decrypt_text = des_obj.decrypt(base64.b64decode(encrypt_text)).decode('utf8')else:decrypt_text = des_obj.decrypt(a2b_hex(encrypt_text)).decode('utf8')return decrypt_textclass Des3Util(object):@staticmethoddef des3_encrypt_text(decrypt_text: str, key: str, iv="", model="CBC", method="base64") -> str:"""DES3加密:param decrypt_text: 明文:param key: 密钥:param model: 加密模式: CBC, ECB:param iv: 密钥偏移量:param method: 用base64加密还是16进制字符串:return: 加密后的数据"""if model == 'CBC':des3 = DES3.new(key.encode('utf-8'), DES3.MODE_CBC, iv[:8].encode('utf-8'))else:des3 = DES3.new(key.encode('utf-8'), DES3.MODE_ECB)encrypt_text = des3.encrypt(pad(decrypt_text.encode('utf-8'), DES3.block_size, style='pkcs7'))if method == "base64":return base64.b64encode(encrypt_text).decode()else:return b2a_hex(encrypt_text).decode()@staticmethoddef des3_decrypt_text(encrypt_text: str, key: str, iv="", model="CBC", method="base64") -> str:"""DES3解密:param encrypt_text: 密文:param key: 秘钥:param model: 解密模式: CBC, ECB:param iv:秘钥偏移量:param method: 用base64解密还是16进制字符串:return:解密后的数据"""if model == 'CBC':des3 = DES3.new(key.encode('utf-8'), DES3.MODE_CBC, iv[:8].encode('utf-8'))else:des3 = DES3.new(key.encode('utf-8'), DES3.MODE_ECB)if method == "base64":decrypt_text = des3.decrypt(base64.b64decode(encrypt_text)).decode('utf8')else:decrypt_text = des3.decrypt(a2b_hex(encrypt_text)).decode('utf8')return decrypt_textif __name__ == '__main__':# 测试print(AesUtil.aes_encrypt_text('{"Type":0,"page":3,"expire":1596461032894}', key="8c18266c4e8fa5de",iv="8c18266c4e8fa5de", method="not base64"))
1.5 非对称加密与 RSA
1.5.1 介绍
1976 年,计算机科学家 Whitfield Diffie 和 Martin Hellman 二人提出了新的加密方式,这种加密方式可以在不传递密钥的情况下实现加密和解密操作,它利用的是两种规则之间的数学关系。与对称加密不同的是,这种方式需要用到两个密钥:公钥(public key) 和私钥(private key)。公钥和私钥是一对,如果用该公钥对数据进行加密,那么只有用对应的私钥才能够解密数据。反之,如果用私钥对数据进行加密,那么只有用对应的公钥才能够解密数据。由于加密和解密时使用的密钥是不相同的,所以这种加密方式被称为非对称加密。在非对称加密算法中,应用最广泛、强度最高的是 RSA 算法,该算法由 Rivest、Shamire 和 Adleman 三人提出。RSA 算法的版本很多,RSA 2.0 版本的 RFC 编号为 2437,RSA 2.1 版本的 RFC 编号为 3447,RSA 2.2 版本的 RFC 编号为 8017。在开始学习 RSA 算法的原理之前,我们需要了解一些数学概念,如质数、互质关系、欧拉函数、欧拉定理和模反元素。
质数: 质数又称素数,定义为:在大于1的自然数中,除了 1 和它本身之外不能被其他数整除的数(如 17)。
互质关系: 公因数只有1的两个数(如15和16)构成互质关系。人们总结了一些方法来判断两个数是否构成互质关系。
- 相邻的两个自然数构成互质关系,如 17 和 18。
- 相邻的两个奇数构成互质关系,如 7 和 9。
- 两个数之间,数值较大的数为质数时,两个数构成互质关系,如 97 和 50。
欧拉函数:在给定的条件(正整数n)下,求小于等于n的正整数中,有多少个数与n构成互质关系。这个求值的方法就叫作欧拉函数,欧拉函数用 φ(n) 表示。假设n为 7,那么在1到7之间与7形成互质关系的数有 1、2、3、4、5、6 这6个数,即 φ(n)=6。
欧拉定理: 欧拉定理表明,如果两个正整数 n 和 m 构成互质关系,则 n 的 φ(m) 次方恒等于 1(mod n)。
模反元素: 正整数 n 和 m 构成互质关系,那么一定可以找到整数 b,使得 nb-1 被 m 整除。整数 b 就叫作正整数 n 的模反元素。如 3 和 5 互质,那么 3×2-1 能够被 5 整除,所以整数 2 就是正整数 3 的模反元素。同理,3×7-1 也能够被 5 整除,所以整数 7 也是正整数 3 的模反元素。我们也可以理解为 2 加或减 m 的整数倍都是正整数 3 的模反元素,如 -14、-3、2、7、12 等,即 n 的 φ(m)-1 次方就是 n 的模反元素。
公钥计算: 设定两个构成互质关系的质数 p 和 q,并计算它们的乘积 n。接着用公式 φ(n)=(p-1)(q-1) 计算乘积 n 的欧拉函数 φ(n)。然后选择一个整数 e(要求 1<e<φ(n),且 e 与 φ(n) 构成互质关系)。最后将 (n,e) 作为公钥。
私钥计算: 在公钥计算的基础上,计算整数 e 对于 φ(n) 的模反元素,将 (n,b) 作为私钥。将( n,e) 和 (n,b) 转成 ASN.1 格式,就得到了最终的公钥和私钥。计算出公钥和私钥后,我们就可以对明文进行加密了。设明文为 m,密文为 c,则加密公式:c=me mod n 解密公式:m=cb mod n
在公钥和私钥生成的过程中出现了 p、q、n、φ(n)、e、b 等 6 个数,这些数中最关键的就是与 n 组成私钥的 b,b 泄露等同于私钥泄露。当 p 和 q 足够大的时候,即使 n 泄露,他人也无法通过分解 n 得到 p 和 q,进而无法求出 b。RSA 算法的可靠性正是建立在大数分解的数论难题基础之上的。
# -*- coding: utf-8 -*-
# @Time : 2022-05-07 17:19
# @Author : AmoXiang
# @File : test.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64message = 'async' # 消息原文# 初始化RSA对象
rsa = RSA.generate(1024, Random.new().read)
# 生成私钥
private_key = rsa.exportKey()
# 生成公钥
public_key = rsa.publickey().exportKey()
# 打印私钥和公钥
print(private_key.decode('utf8'))
print(public_key.decode('utf8'))# 将私钥和公钥存入对应名称的文件
with open('private.pem', 'wb') as f:f.write(private_key)with open('public.pem', 'wb') as f:f.write(public_key)with open('public.pem', 'r') as f:# 从文件中加载公钥pub = f.read()pubkey = RSA.importKey(pub)# 用公钥加密消息原文cipher = PKCS1_v1_5.new(pubkey)c = base64.b64encode(cipher.encrypt(message.encode('utf8'))).decode('utf8')with open('private.pem', 'r') as f:# 从文件中加载私钥pri = f.read()prikey = RSA.importKey(pri)# 用私钥解密消息密文cipher = PKCS1_v1_5.new(prikey)m = cipher.decrypt(base64.b64decode(c), 'error').decode('utf8')print('消息原文:%s\n消息密文:%s\n解密结果:%s' % (message, c, m))
执行该段代码可能会报如下的错误:
解决方案:找到 python 安装目录下的 nt.py 文件,位置如下所示:
D:\DevelopSoftware\python37\Lib\site-packages\Crypto\Random\OSRNG\nt.py
你的路径:\python37\Lib\site-packages\Crypto\Random\OSRNG\nt.py
修改 nt.py 中的以下代码:
# import winrandom 原始代码注释掉 改为下面的导入方式
from Crypto.Random.OSRNG import winrandom
如下图所示:
再次执行代码就不报错了。
1.5.2 Python实现非对称加密算法(rsa)封装
# -*- coding: utf-8 -*-
# @Time : 2024-04-30 03:49
# @Author : AmoXiang
# @File : rsa
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680import base64
import rsa
from binascii import b2a_hex, a2b_hexclass RsaUtil(object):@staticmethoddef rsa_encrypt_text1(public_key, decrypt_text: str, method="base64") -> str:"""RSA加密:param public_key: 公钥:param decrypt_text: 明文:param method: 用base64加密还是16进制字符串:return: 加密后的数据"""encrypt_text = rsa.encrypt(decrypt_text.encode('utf-8'), rsa.PublicKey.load_pkcs1(public_key))if method == "base64":return base64.b64encode(encrypt_text).decode()else:return b2a_hex(encrypt_text).decode()@staticmethoddef rsa_encrypt_text2(public_key, _text: str, method="base64") -> str:"""RSA加密,针对setPublicKey:param public_key: 公钥:param _text: 明文:param method: 用base64加密还是16进制字符串:return: 加密后的数据"""encrypt_text = rsa.encrypt(_text.encode('utf-8'), rsa.PublicKey.load_pkcs1_openssl_pem(public_key))if method == "base64":return base64.b64encode(encrypt_text).decode()else:return b2a_hex(encrypt_text).decode()@staticmethoddef rsa_encrypt_text3(key, _text: str, method="base64"):"""RSA加密,针对new RSAKeyPairimport rsafrom binascii import b2a_hex:param key: 公钥的参数:param _text: 待加密的明文:param method: 用base64加密还是16进制字符串:return: 加密后的数据"""e = int('010001', 16)n = int(key, 16)pub_key = rsa.PublicKey(e=e, n=n)encrypt_text = rsa.encrypt(_text.encode(), pub_key)if method == "base64":return base64.b64encode(encrypt_text).decode()else:return b2a_hex(encrypt_text).decode()@staticmethoddef rsa_decrypt_text(private_key, encrypt_text: str, method="base64") -> str:"""RSA解密:param private_key: 私钥:param encrypt_text: 密文:param method: 方式:return: 明文"""if method == "base64":decrypt_text = rsa.decrypt(base64.b64decode(encrypt_text), rsa.PrivateKey.load_pkcs1(private_key)).decode('utf8')else:decrypt_text = rsa.decrypt(a2b_hex(encrypt_text), rsa.PrivateKey.load_pkcs1(private_key)).decode('utf8')return decrypt_text@staticmethoddef rsa_sign(private_key, decrypt_text, method="MD5"):"""rsa签名:param private_key: 私钥:param decrypt_text: 明文:param method: 'MD5', 'SHA-1','SHA-224', SHA-256', 'SHA-384' or 'SHA-512':return:"""sign_text = rsa.sign(decrypt_text.encode('utf-8'), rsa.PrivateKey.load_pkcs1(private_key), method)sign_text = base64.b64encode(sign_text).decode()return sign_text@staticmethoddef rsa_verify(signature, public_key, decrypt_text):"""rsa验签:param signature: rsa签名:param public_key: 公钥:param decrypt_text: 明文:return:"""return rsa.verify(decrypt_text.encode('utf-8'), base64.b64decode(signature),rsa.PublicKey.load_pkcs1(public_key))if __name__ == '__main__':private_key = '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAy5R1R2yM5jPPvkO2F47qVqMkYj7o92DF8y1yMkCSxY1WwqG0\ndCdUZTnaoBuAz99wGt55oGLcdalV71nPUiGWs/b6GzVN5v72baz/Q2OxHtkrFKqL\nVX16LW31cW9hAntN84RCbvTeB0MNV+SHmXjIf17OQLCtDKHBZWZ5NKyqFstO+KOd\nu32d2jsw+DT5lOBzDUBk/wUw2KyFJVx7eK6sSXEyWqBk2nxMRDNYixIEN1V1EBSq\nf+OwKK5Mxi04r38+Qog8z03/t/u6CfAOWVmi+MdrD1VHXv/P7bnFlgRcLzKwK1QL\nTSLBE1PrMmNNj0oRjByhMoI9tY5X6mRBqLyDhwIDAQABAoIBAGO++RmGO6D9CNAJ\n4Bm52eKaK5UBiubOIR8NiNLLZb5qinRxg3eX35d7Wb2xzBLNwOFBWSl21trFncfY\n4qY0s+C4ZYHYQ7Om/7nsFeQAYAOj1yJYj01TXf4NTsGGF2t+W8qxZlV0H6dCOLL0\nU2YkUmRp4Le8eQVj6dyTcVaYNPxWQBnb9ZOEIEvEjeoO/DD7CCmt7LDCey9KrTQl\nAvuc2nN6uRV1Wfm0P8conKPJtVdgzMvJujNdpz+bBDqwsqgeCICjs/hSCNO81VH3\nDD7J0mG2OHqowOVqagoDHpBprHOUKxAeTs9I0KEL+hEI4zXCDL69+Xs6azuts733\nzSOmwxkCgYEA25czfPVxxcK685LhaAvwbmzWHqNp07ytRNGf+Aww6OdgWkdgPy0n\n20Gkg0HAqsxGcgZJk6cAkOy5hBLNHpHlGbeWFi+62lVNYUv3hAxumtiPyBMu7avE\nZQCTXND1H1f/2enRDJRxQsR8y/SX1ivmC5U6fx7hbpKxnXyRHnvSlk8CgYEA7VWp\nhLNkn4AEaPPW0TknwKG40At/hjecX2zWAyZVt4ydDSeKgMEOUdmvGGlSCrefAl0n\nPTfM9SdIDcO5OTa2wUayKLIsrb6TDnG6KXXN6z3HR3Q4qKJbG83eaMYDqqziPPV+\nxzRVWShI3EGwkLczASmiYy+sEAT0OkxP59xTKUkCgYBgaGjFkukJfy4fJDxsNtmv\nUX9MYkhjGrIjxbjq6UdL6dGGsVGTSxr1i0NUETkqg5bmFtaUybxY5GWqk6qUok8o\nVE7DnN73Xn4jmnun8OFagHvXxnxTApeuFGueU2tbAIKmxJ3wXPfA7Y0w6kkDUbCl\nIzZUe1VT+3mZgAgijxBsxwKBgQDNytiJ62/V6hBo3P6pPtEcdF6nb0DtpazfBaVw\n572twaywqlermzsKeCIenbx49I1ZZGLQ72C2NpCA9vTWCn5fiyiSpyScp0ImZTDS\nIIckctYoPDug5d7wdgtjeEfXp78osopyuwtCmu7Kpd8vLNt6J5raPI0K+vC22FL1\nLpOhmQKBgQCFeU448fL87N1MjMyusi8wJ5MLcn+kHbLTtpskTpfQM2p3Cnp4oL+7\nBI4AlXlKItV37rJIjZxQgLWhGoTZPplZaW4ooJCFJbazce5ua5fnsFS0oXhDN7uw\njaq+v5t8G6gFS09hEa4kz9O53t/7UGuQqh0Bxb0cJ9iNeAlhagvBDQ==\n-----END RSA PRIVATE KEY-----'print(RsaUtil.rsa_decrypt_text(private_key,'KplKSYpG9K83foayDXTWIViGX7HS0AAnyJvMQyBjaLLEp1P7x7zWeZ09DXwzpRgkAMK8cd/8OGtfl1bCZ0aylItIrUxw80pNtJCTwh/9tkBJQapgSJZjjo/CffEkVcdYAxpM5MLED2C6dC+otSiICOlFP72WfkSd/S8aWeTy8BAqzxDFwE2ipF0GvxXTMbsH10an+TPQxubmkwuk0OoAW4xexfeoRIuNJLkUdIcOccvGV82NswRLGrojhTMH8JTbfmTel7A1xF5IzqVxoz2WGLbFsDojdf3J2puzYQo7gBTXMs7qCfO0HjxODhwbHfEeLlYn1+aPlJ8x5PAlx9VWEw=='))
二、Crypto-JS
crypto-js:JavaScript library of crypto standards. 为 JavaScript 提供了各种各样的加密算法。 在线演示:https://tool.oschina.net/encrypt https://www.npmjs.com/package/crypto-js
本地需安装 node 环境,然后再安装 crypto-js 库:
npm install crypto-js
2.1 nodejs之md5、sha1、sha256、base64
md5、sha1、sha256 加密:
var CryptoJS = require("crypto-js");// var md5 = function (a) {
// return CryptoJS.MD5(a).toString()
// }; //与下面这行代码等价
var md5 = require("md5"); // npm install md5var sha1 = function (a) {// return CryptoJS.SHA1(a).toString();return CryptoJS.SHA1(a);
};
var sha256 = function (a) {return CryptoJS.SHA256(a).toString();
};
var sha512 = function (a) {return CryptoJS.SHA512(a).toString();
};
var sha3 = function (a) {return CryptoJS.SHA3(a).toString();
};// b334960c3a76543c7b8c6280e39b22cc
console.log(md5("AmoXiang"))
// d8a28302ed740a56eb992326ac466517d89ae69a
/*
* {words: [ -660438270, -311162282, -342285530, -1404672745, -660937062 ],sigBytes: 20
}
* */
console.log(sha1("AmoXiang"))
console.log(sha256("AmoXiang"))
console.log(sha512("AmoXiang"))
console.log(sha3("AmoXiang"))
2.2 Crypto-Js之AES
此脚本默认 CryptoJS.enc.Utf8.parse,实际有 CryptoJS.enc.Latin1.parse 以及 CryptoJS.enc.Hex.parse 可自行替换 AES 之 CBC 模式加密:需要 key,iv 和文本 示例代码如下:
var CryptoJS = require("crypto-js");
var encrypt_req = function (key, iv, text) {var l = CryptoJS.enc.Utf8.parse(text);var e = CryptoJS.enc.Utf8.parse(key);var a = CryptoJS.AES.encrypt(l, e, {mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7,iv: CryptoJS.enc.Utf8.parse(iv)})// return a.toString() // 此方式返回base64return a.ciphertext.toString() // 返回hex格式的密文
}var decrypt_req = function (key, iv, text) {var e = CryptoJS.enc.Utf8.parse(key);var WordArray = CryptoJS.enc.Hex.parse(text); // 如果text是base64形式,该行注释掉var text = CryptoJS.enc.Base64.stringify(WordArray); // 如果text是base64形式,该行注释掉var a = CryptoJS.AES.decrypt(text, e, {mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7,iv: CryptoJS.enc.Utf8.parse(iv)});return CryptoJS.enc.Utf8.stringify(a).toString()
}
AES 之 ECB 模式加密:只需要 key 和文本:
var CryptoJS = require("crypto-js");
var encrypt_req = function (key, text) {var l = CryptoJS.enc.Utf8.parse(text);var e = CryptoJS.enc.Utf8.parse(key);var a = CryptoJS.AES.encrypt(l, e, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7})return a.toString() // 此方式返回base64// return a.ciphertext.toString() // 返回hex格式的密文
}var decrypt_req = function (key, text) {var e = CryptoJS.enc.Utf8.parse(key);// var WordArray = CryptoJS.enc.Hex.parse(text); // 如果text是base64形式,该行注释掉// var text = CryptoJS.enc.Base64.stringify(WordArray); // 如果text是base64形式,该行注释掉var a = CryptoJS.AES.decrypt(text, e, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return CryptoJS.enc.Utf8.stringify(a).toString()
}
2.3 Crypto-Js之DES
此脚本默认 CryptoJS.enc.Utf8.parse,实际有 CryptoJS.enc.Latin1.parse 以及 CryptoJS.enc.Hex.parse 可自行替换
DES之CBC模式加密:需要 key,iv 和文本
var CryptoJS = require("crypto-js");
var encrypt_req = function (key, iv, text) {var l = CryptoJS.enc.Utf8.parse(text);var e = CryptoJS.enc.Utf8.parse(key);var a = CryptoJS.DES.encrypt(l, e, {mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7,iv: CryptoJS.enc.Utf8.parse(iv)})return a.toString() // 此方式返回base64// return a.ciphertext.toString() // 返回hex格式的密文
}var decrypt_req = function (key, iv, text) {var e = CryptoJS.enc.Utf8.parse(key);// var WordArray = CryptoJS.enc.Hex.parse(text); // 如果text是base64形式,该行注释掉// var text = CryptoJS.enc.Base64.stringify(WordArray); // 如果text是base64形式,该行注释掉var a = CryptoJS.DES.decrypt(text, e, {mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7,iv: CryptoJS.enc.Utf8.parse(iv)});return CryptoJS.enc.Utf8.stringify(a).toString()
}
DES 之 ECB 模式加密:需要 key 和文本:
var CryptoJS = require("crypto-js");
var encrypt_req = function (key, text) {var l = CryptoJS.enc.Utf8.parse(text);var e = CryptoJS.enc.Utf8.parse(key);var a = CryptoJS.DES.encrypt(l, e, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7})return a.toString() // 此方式返回base64// return a.ciphertext.toString() // 返回hex格式的密文
}var decrypt_req = function (key, text) {var e = CryptoJS.enc.Utf8.parse(key);// var WordArray = CryptoJS.enc.Hex.parse(text); // 如果text是base64形式,该行注释掉// var text = CryptoJS.enc.Base64.stringify(WordArray); // 如果text是base64形式,该行注释掉var a = CryptoJS.DES.decrypt(text, e, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return CryptoJS.enc.Utf8.stringify(a).toString()
}
2.4 Crypto-Js之3DES
此脚本默认 CryptoJS.enc.Utf8.parse,实际有 CryptoJS.enc.Latin1.parse 以及 CryptoJS.enc.Hex.parse 可自行替换
3DES 之 CBC 模式加密:需要 key,iv 和文本
``js
var CryptoJS = require(“crypto-js”);
var encrypt_req = function (key, iv, text) {
var l = CryptoJS.enc.Utf8.parse(text);
var e = CryptoJS.enc.Utf8.parse(key);
var a = CryptoJS.TripleDES.encrypt(l, e, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Utf8.parse(iv)
})
return a.toString() // 此方式返回base64
// return a.ciphertext.toString() // 返回hex格式的密文
}
var decrypt_req = function (key, iv, text) {var e = CryptoJS.enc.Utf8.parse(key);// var WordArray = CryptoJS.enc.Hex.parse(text); // 如果text是base64形式,该行注释掉// var text = CryptoJS.enc.Base64.stringify(WordArray); // 如果text是base64形式,该行注释掉var a = CryptoJS.TripleDES.decrypt(text, e, {mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7,iv: CryptoJS.enc.Utf8.parse(iv)});return CryptoJS.enc.Utf8.stringify(a).toString()
}
**3DES 之 ECB 模式加密:需要 key 和文本:**
```javavar CryptoJS = require("crypto-js");var encrypt_req = function (key, text) {var l = CryptoJS.enc.Utf8.parse(text);var e = CryptoJS.enc.Utf8.parse(key);var a = CryptoJS.TripleDES.encrypt(l, e, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7})return a.toString() // 此方式返回base64// return a.ciphertext.toString() // 返回hex格式的密文}var decrypt_req = function (key, text) {var e = CryptoJS.enc.Utf8.parse(key);// var WordArray = CryptoJS.enc.Hex.parse(text); // 如果text是base64形式,该行注释掉// var text = CryptoJS.enc.Base64.stringify(WordArray); // 如果text是base64形式,该行注释掉var a = CryptoJS.TripleDES.decrypt(text, e, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return CryptoJS.enc.Utf8.stringify(a).toString()}
说明
文章参考@Amo Xiang , 2024最新版JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)