2024最新版JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)

目录

  • 一、编码与加密原理
    • 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 b64decode

code = ['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 = 65
            else:
                encoding = 0
                for key, v in enumerate(i):
                    # 二进制数按权相加得到十进制数
                    val = int(v) * pow(2, len(i) - 1 - key)
                    encoding += val
            # 从对照表中取值
            after = self.comparison.get(str(encoding))
            result += after
        return result

    def 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' * threshold
            else:
                val = '{:0{threshold}b}'.format(int(i), threshold=threshold)
            result.append(val)
        return result

    @staticmethod
    def shift(value: str, threshold: int, group: int = 24) -> list:
        """位数转换"""
        remainder = len(value) % group
        if remainder:
            # 如果有余数,则说明需要用0补位
            padding = '0' * (group - remainder)
            value += padding
        # 按照threshold值切割字符
        result = [value[i:i + threshold] for i in range(0, len(value), threshold)]
        return result

    def 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 keys


if __name__ == '__main__':
    # threshold 的值建议为 4/5/6
    cus = 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 base64


class Base64Util(object):
    @staticmethod
    def base64_encrypt_text(decrypt_text: str) -> str:
        """
        Bse64加密
        :param decrypt_text: 明文
        :return: 密文
        """
        return base64.b64encode(decrypt_text.encode('utf-8')).decode()

    @staticmethod
    def 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] 表示,其中 N16 的倍数,最后得到 1632 位的分组结果。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 Studio

2.将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/xw1680


from hashlib import md5
from hashlib import sha1
import hmac

from Crypto.Hash import MD5
from Crypto.Hash import SHA1  # 使用 pip 安装 pycrypto、pycryptodome


class Md5Sha1HmacUtil(object):
    @staticmethod
    def md5_encrypt_text(decrypt_text: str) -> str:
        """
        python自带原生包md5加密
        :param decrypt_text: 明文
        :return: 密文
        """
        return md5(decrypt_text.encode('utf8')).hexdigest()

    @staticmethod
    def md5_encrypt_text2(decrypt_text: str) -> str:
        """
        使用第三方模块pycryptodome进行md5加密
        :param decrypt_text: 明文
        :return: 密文
        """
        return MD5.new(decrypt_text.encode('utf8')).hexdigest()

    @staticmethod
    def sha1_encrypt_text(decrypt_text: str) -> str:
        """
        python自带原生包SHA1加密
        :param decrypt_text: 明文
        :return: 密文
        """
        return sha1(decrypt_text.encode('utf8')).hexdigest()

    @staticmethod
    def 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()

    @staticmethod
    def 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 进行,经过对应轮次运算后输出的结果即为密文。输入、数组和密文的关系如下图所示:

矩阵中字节的排列顺序为从上到下,从左到右。运算包含以下几个步骤:

  1. SubBytes:替换操作,通过非线性函数将 state 中的字节替换成 S-box 表中对应的字节。
  2. ShiftRows:行移位操作,将 state 中每行的字节按照规律进行移位。
  3. MixColumns:列混合操作,通过线性函数对 state 中的列进行混合。
  4. 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/xw1680

import base64
from binascii import b2a_hex, a2b_hex

from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Cipher import DES3
from Crypto.Cipher import DES


class AesUtil(object):
    @staticmethod
    def 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()

    @staticmethod
    def 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_text


class DesUtil(object):

    @staticmethod
    def 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()

    @staticmethod
    def 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_text


class Des3Util(object):

    @staticmethod
    def 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()

    @staticmethod
    def 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_text


if __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)构成互质关系。人们总结了一些方法来判断两个数是否构成互质关系。

  1. 相邻的两个自然数构成互质关系,如 17 和 18。
  2. 相邻的两个奇数构成互质关系,如 7 和 9。
  3. 两个数之间,数值较大的数为质数时,两个数构成互质关系,如 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/xw1680

from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

message = '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/xw1680


import base64
import rsa
from binascii import b2a_hex, a2b_hex


class RsaUtil(object):

    @staticmethod
    def 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()

    @staticmethod
    def 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()

    @staticmethod
    def rsa_encrypt_text3(key, _text: str, method="base64"):
        """
        RSA加密,针对new RSAKeyPair
        import rsa
        from 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()

    @staticmethod
    def 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

    @staticmethod
    def 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

    @staticmethod
    def 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 md5

var 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()   // 此方式返回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.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 和文本

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 和文本:

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.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()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/586457.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【GitHub】github学生认证,在vscode中使用copilot的教程

github学生认证并使用copilot教程 写在最前面一.注册github账号1.1、注册1.2、完善你的profile 二、Github 学生认证注意事项&#xff1a;不完善的说明 三、Copilot四、在 Visual Studio Code 中安装 GitHub Copilot 扩展4.1 安装 Copilot 插件4.2 配置 Copilot 插件&#xff0…

Java设计模式 _结构型模式_组合模式

一、组合模式 1、组合模式 组合模式&#xff08;Composite Pattern&#xff09;是这一种结构型设计模式。又叫部分整体模式。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次关系。即&#xff1a;创建了一个包含自己对象组的类&#xff0c;该类提供了修改…

Educational Codeforces Round 165 (Rated for Div. 2)[A~D]

这场签到很快那会rank1400吧&#xff0c;但到c就写不动了&#xff0c;最后排名也是3000 左右&#xff0c;可见很多人其实都不会写dp。快速签到也很重要啊&#xff01;&#xff01; A. Two Friends Problem - A - Codeforces 题目大意&#xff1a; M有n个朋友&#xff0c;编号…

【Java】java实现文件上传和下载(上传到指定路径/数据库/minio)

目录 上传到指定路径 一、代码层级结构 二、文件上传接口 三、使用postman进行测试&#xff1b; MultipartFile接收前端传递的文件&#xff1a;127.0.0.1:8082/path/uploadFile part接收前端传递的文件&#xff1a;127.0.0.1:8082/path/uploadFileByRequest 接收前端传递…

基于大模型的智能案件询问系统

一、数据库层面 1、document表 这个表是用来存储文件信息的。具体字段含义如下&#xff1a; 1. id&#xff1a;文件的唯一标识&#xff0c;整型&#xff0c;自增。 2. name&#xff1a;文件名称&#xff0c;字符串类型&#xff0c;最大长度为255个字符。 3. type&#xff1a…

宠物领养|基于SprinBoot+vue的宠物领养管理系统(源码+数据库+文档)

宠物领养目录 基于Spring Boot的宠物领养系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1前台 1.1 宠物领养 1.2 宠物认领 1.3 教学视频 2后台 2.1宠物领养管理 2.2 宠物领养审核管理 2.3 宠物认领管理 2.4 宠物认领审核管理 2.5 教学视频管理 四、…

Linux专栏03:使用Xshell远程连接云服务器

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 使用Xshell远程连接云服务器 编号&#xff1a;03 文章目录 使用Xsh…

Windows中Redis安装配置

一&#xff0c;下载 Redis官网 Redis中文网 Redis的Github资源 安装 更改资源路径及添加环境变量 添加防火墙异常 设置最大缓存 三、验证redis安装是否成功 redis-cli

TiDB系列之:部署TiDB集群常见报错解决方法

TiDB系列之&#xff1a;部署TiDB集群常见报错解决方法 一、部署TiDB集群二、unsupported filesystem ext3三、soft limit of nofile四、THP is enabled五、numactl not usable六、net.ipv4.tcp_syncookies 1七、service irqbalance not found,八、登陆TiDB数据库 一、部署TiDB…

RTSP,RTP,RTCP

机器学习 Machine Learning&#xff08;ML&#xff09; 深度学习&#xff08;DL&#xff0c;Deep Learning&#xff09; CV计算机视觉&#xff08;computer vision&#xff09; FFMPEG&#xff0c;MPEG2-TS,H.264,H.265,AAC rstp,rtp,rtmp,webrtc onvif,gb28181 最详细的音…

Rust中的并发性:Sync 和 Send Traits

在并发的世界中&#xff0c;最常见的并发安全问题就是数据竞争&#xff0c;也就是两个线程同时对一个变量进行读写操作。但当你在 Safe Rust 中写出有数据竞争的代码时&#xff0c;编译器会直接拒绝编译。那么它是靠什么魔法做到的呢&#xff1f; 这就不得不谈 Send 和 Sync 这…

排序算法大总结

引言 排序算法&#xff08;sorting algorithm&#xff09;是用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 如图 1-1 所示&#xff0c;排序算法中的数据类型可以是整数、浮点数、字符或字符串等…

SpringBoot中实现发送邮件

概要 在Spring Boot中发送电子邮件相对简单。你可以使用Spring的邮件支持来实现这一点。 步骤&#xff1a; 1.添加依赖&#xff1a;首先&#xff0c;需要在你的pom.xml文件中添加Spring Boot的邮件发送器依赖。 2. 配置邮件服务器&#xff1a;在application.properties或app…

Python 机器学习 基础 之 学习 基础环境搭建

Python 机器学习 基础 之 学习 基础环境搭建 目录 Python 机器学习 基础 之 学习 基础环境搭建 一、简单介绍 二、什么是机器学习 三、python 环境的搭建 1、Python 安装包下载 2、这里以 下载 Python 3.10.9 为例 3、安装 Python 3.10.9 4、检验 python 是否安装成功&…

设计模式第二次测试 | 数据库连接池设计(原型模式、创建者模式、适配器模式)

需求中文如下&#xff1a;原本是英文&#xff0c;用百度翻译转换而来 我们需要设计一个工具&#xff0c;它负责创建一个与数据库软件MySQL的连接池。 连接池中有数百个连接可供客户端使用。 所有连接对象都有相同的内容&#xff0c;但它们是不同的对象。 连接对象的创建是资源密…

【嵌入式笔试题】进程线程笔试题

非常经典的笔试题。 1.进程&线程(16道) 1.1异步IO和同步IO区别? 答案:如果是同步IO,当一个IO操作执行时,应用程序必须等待,直到此IO执行完。 相反,异步IO操作在后台运行,IO操作和应用程序可以同时运行,提高系统性能,提 高IO流量。 解读:在同步文件IO中,线…

Sarcasm detection论文解析 |用于微博讽刺检测的上下文增强卷积神经网络

论文地址 论文地址&#xff1a;Context-augmented convolutional neural networks for twitter sarcasm detection - ScienceDirect 论文首页 笔记大纲 用于微博讽刺检测的上下文增强卷积神经网络 &#x1f4c5;出版年份:2018 &#x1f4d6;出版期刊:Neurocomputing &#x1f…

Django后台项目开发实战五

完成两个功能&#xff1a; HR 可以维护候选人信息面试官可以录入面试反馈 第五阶段 创建 interview 应用&#xff0c;实现候选人面试评估表的增删改功能&#xff0c;并且按照页面分组来展示不同的内容&#xff0c;如候选人基础信息&#xff0c;一面&#xff0c;二面的面试结…

在离线环境中将 CentOS 7.5 原地升级并迁移至 RHEL 7.9

《OpenShift / RHEL / DevSecOps 汇总目录》 说明 本文将说明如何在离线环境中将 CentOS 7.5 升级并迁移至 RHEL 7.9。为了简化准备过程&#xff0c;本文前面将在在线环境中安装用到的各种所需验证软件&#xff0c;而在后面升级迁移的时候再切换到由 ISO 构成的离线 Yum Repo…

20240430,类模板案例-数组类封装,STL初识,STRING容器(构造函数,赋值)

我真的碎掉了&#xff0c;主要是我很缺那点钱啊现在&#xff0c;我真的碎掉了我碎掉了碎掉了碎掉了 0.8 类模板案例-数组类封装 需求&#xff1a;1&#xff0c;存储&#xff1a;内置和自定义数据类型&#xff1b; 2&#xff0c;存到堆区&#xff1b; 3&#xff0c;构造函数传入…