JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现

目录
  • 一、密码学介绍
    • 1.1 为什么要学密码学?
    • 1.2 密码学里面学哪一些
  • 二、字符编码
  • 三、位运算
  • 四、Hex 编码与 Base64 编码
    • 4.1 Hex 编码
    • 4.2 Base64 编码
  • 五、消息摘要算法
    • 5.1 简介
    • 5.2 JS中的MD5、SHA、HMAC、SM3
  • 六、对称加密算法
    • 6.1 介绍
    • 6.2 加密模式和填充方式
    • 6.3 CryptoJS 中DES、DESede、AES算法实现
    • 6.4 对称加密算法注意事项
    • 6.5 CryptoJS(其他算法)
  • 七、非对称加密算法

一、密码学介绍

1.1 为什么要学密码学?

数据请求中未知的参数可能是随机生成、标准算法加密的、魔改算法加密、自写算法加密的,如下图所示:
在这里插入图片描述
逆向中会接触到的标准算法加密,使用的语言一般在 JS、Java、C 中,JS 版的标准算法会应用于网页、H5app、小程序中,一般使用第三方库或者自己实现。Java 版的标准算法有现成的系统 API 调用,开发者想使用这些 API,必须使用固定的方法名去访问。C/C++ 没有现成的系统 API 调用,开发者要么自己去实现算法,要么调用别人写好的模块,算法的运行不依赖系统 API,因此方法名可以混淆。我们要做的就是根据各种标准算法的特征、实现细节,去识别是否标准算法。

补充: iOS 系统中有现成的 C 实现的 API 调用,开发者想使用这些 API,必须使用固定的方法名去访问 iOS 系统也可以自己实现标准算法的,处理方式与安卓的 so 相同。

密码学的学习非常地重要,后续我在安卓逆向的文章中会进一步加深讲解。

1.2 密码学里面学哪一些

消息摘要算法(散列函数、哈希函数): MD5、SHA、MAC、SM3
对称加密算法: DES、3DES、RC4、AES、SM4
非对称加密算法: RSA、SM2
数字签名算法: MD5withRSA、SHA1withRSA、SHA256withRSA

备注: 任何语言里面对于标准算法的实现都相同

二、字符编码

编码分为很多种: 字符编码、Hex 编码、URL 编码、Base64 编码…
字符编码学习笔记参考文章:https://blog.csdn.net/xw1680/article/details/126964362

备注: 字符和码值的对应关系是通过字符编码表决定的
ASCII :https://baike.baidu.com/item/ASCII/309296
UTF8: https://baike.baidu.com/item/UTF-8/481798
ANSI: https://blog.csdn.net/Liuqz2009/article/details/107861408
编码与解码:https://blog.csdn.net/u012485099/article/details/126037992

三、位运算

位操作符用于数值的底层操作,也就是操作内存中表示数据的比特(位)。ECMAScript 中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到64位表示,而是先把值转换为32位整数,再进行位操作,之后再把结果转换为64位。对开发者而言,就好像只有32位整数一样,因为64位整数存储格式是不可见的。既然知道了这些,就只需要考虑32位整数即可。

有符号整数使用32位的前31位表示整数值。第32位表示数值的符号,如0表示正,1表示负。这一位称为符号位(sign bit),它的值决定了数值其余部分的格式。正值以真正的二进制格式存储,即31位中的每一位都代表2的幂。第一位(称为第0位)表示2^0,第二位表示 2^1,依此类推。如果一个位是空的,则以0填充,相当于忽略不计。比如,数值18的二进制格式为 00000000000000000000000000010010,或更精简的10010。后者是用到的5个有效位,决定了实际的值。负值以一种称为二补数(或补码)的二进制编码存储。一个数值的二补数通过如下3个步骤计算得到:

  1. 确定绝对值的二进制表示(如,对于-18,先确定18的二进制表示);
  2. 找到数值的一补数(或反码),换句话说,就是每个0都变成1,每个1都变成0;
  3. 给结果加1。

按位非操作符用 波浪符(~) 表示,它的作用是返回数值的 一补数(反码)。按位非是 ECMAScript 中为数不多的几个二进制数学操作符之一。看下面的例子:

let num1 = 25;      //二进制 00000000000000000000000000011001 ==> 8421码计算或者直接除以2取余数
let num2 = ~num1;   //二进制 11111111111111111111111111100110(补码)
==>   11111111111111111111111111100110
==>-1 11111111111111111111111111100101
==>   10000000000000000000000000011010
==>符号位 2^4+2^3+2^1 ==> 16+8+2 ==> 26 符号位为1 故结果为 -26
console.log(num2);  // -26
//可以这样进行记忆:按位非的最终效果是对数值取反并减1
let num1 = 25;
let num2 = -num1 - 1;
console.log(num2)
//实际上,尽管两者返回的结果一样,但位操作的速度快得多。这是因为位操作是在数值的底层表示上完成的。
//总结:对一个数取反偶数次结果是它本身

按位与操作符用 和号(&) 表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。按位与操作在两个位都是1时返回1,在任何一位是0时返回0,可以用来取出指定的二进制位。 下面看一个例子:

let result = 25 & 3;
console.log(result); // 1
看下面的二进制计算过程:25 = 0000 0000 0000 0000 0000 0000 0001 10013 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------AND = 0000 0000 0000 0000 0000 0000 0000 0001   计算 let result = -5 & -3;?
-5 ==> 1000 0000 0000 0000 0000 0000 0000 0101==> 1111 1111 1111 1111 1111 1111 1111 1010==> 1111 1111 1111 1111 1111 1111 1111 1011-3 ==> 1000 0000 0000 0000 0000 0000 0000 0011==> 1111 1111 1111 1111 1111 1111 1111 1100==> 1111 1111 1111 1111 1111 1111 1111 1101-5   1111 1111 1111 1111 1111 1111 1111 1011  &
-3   1111 1111 1111 1111 1111 1111 1111 11011111 1111 1111 1111 1111 1111 1111 1001 补码
-1   1111 1111 1111 1111 1111 1111 1111 1000 反码1000 0000 0000 0000 0000 0000 0000 0111 取反 ==> -7

按位或操作符用 管道符(|) 表示,同样有两个操作数。按位或操作在至少一位是1时返回1,两位都是0时返回0,可以用来将指定的二进制位拼接。 仍然用按位与的示例,如果对25和3执行按位或,代码如下所示:

let result = 25 | 3;
console.log(result); // 27
计算过程如下:
25 = 0000 0000 0000 0000 0000 0000 0001 10013 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011 ==> 2^4+2^3+2^1+2^0 ==> 16+8+2+1 ==> 27
在参与计算的两个数中,有4位都是1,因此它们直接对应到结果上。二进制码11011等于27。-5 ==> 1000 0000 0000 0000 0000 0000 0000 0101==> 1111 1111 1111 1111 1111 1111 1111 1010==> 1111 1111 1111 1111 1111 1111 1111 1011-3 ==> 1000 0000 0000 0000 0000 0000 0000 0011==> 1111 1111 1111 1111 1111 1111 1111 1100==> 1111 1111 1111 1111 1111 1111 1111 1101-5   1111 1111 1111 1111 1111 1111 1111 1011  |
-3   1111 1111 1111 1111 1111 1111 1111 11011111 1111 1111 1111 1111 1111 1111 1111 补码
-1   1111 1111 1111 1111 1111 1111 1111 1110 反码1000 0000 0000 0000 0000 0000 0000 0001 取反 ==> -1

按位异或用 脱字符(^) 表示,同样有两个操作数。按位异或与按位或的区别是,它只在一位上是1的时候返回1(两位都是1或0,则返回0)。 对数值25和3执行按位异或操作:

let result = 25 ^ 3;
console.log(result); // 26
计算过程如下:25 = 0000 0000 0000 0000 0000 0000 0001 10013 = 0000 0000 0000 0000 0000 0000 0000 0011
-----------------------------------------------XOR = 0000 0000 0000 0000 0000 0000 0001 1010 ==> 2^4+2^3+2^1 ==> 26
二进制码11010等于26。(注意,这比对同样两个值执行按位或操作得到的结果小1。)计算 let result = 25 ^ -3;?25 = 0000 0000 0000 0000 0000 0000 0001 1001 
-3  = 1000 0000 0000 0000 0000 0000 0000 0011= 1111 1111 1111 1111 1111 1111 1111 1101 补码 ^ = 0000 0000 0000 0000 0000 0000 0001 1001= 1111 1111 1111 1111 1111 1111 1110 0100 补码 - 1= 1111 1111 1111 1111 1111 1111 1110 0011 反码= 1000 0000 0000 0000 0000 0000 0001 1100 ==> 2^4+2^3+2^2 ==> -28计算 let result = -25 ^ -3;?
-25 = 1000 0000 0000 0000 0000 0000 0001 1001 = 1111 1111 1111 1111 1111 1111 1110 0110= 1111 1111 1111 1111 1111 1111 1110 0111 补码
-3  = 1000 0000 0000 0000 0000 0000 0000 0011= 1111 1111 1111 1111 1111 1111 1111 1101 补码 ^ = 1111 1111 1111 1111 1111 1111 1110 0111 = 0000 0000 0000 0000 0000 0000 0001 1010 补码 符号位为0 表示正数 正数原码、反码、补码相同 ==> 故结果为:2^4+2^3+2^1 ==> 26计算 let result = -5 ^ -3;?
-5 ==> 1000 0000 0000 0000 0000 0000 0000 0101==> 1111 1111 1111 1111 1111 1111 1111 1010==> 1111 1111 1111 1111 1111 1111 1111 1011-3 ==> 1000 0000 0000 0000 0000 0000 0000 0011==> 1111 1111 1111 1111 1111 1111 1111 1100==> 1111 1111 1111 1111 1111 1111 1111 1101-5   1111 1111 1111 1111 1111 1111 1111 1011  ^
-3   1111 1111 1111 1111 1111 1111 1111 11010000 0000 0000 0000 0000 0000 0000 0110 补码 符号位为0 表示正数 正数原码、反码、补码相同 结果==> 2^2+2^1 ==> 6

补充:^ 的特点:一个数据对另一个数据按位异或两次,结果为该数本身。如下:

let a = 10
let b = 20
console.log(a ^ b ^ b)  //10
console.log(a ^ b ^ a) //20

左移操作符用两个 小于号(<<) 表示,会按照指定的位数将数值的所有位向左移动。比如,如果数值2(二进制10)向左移5位,就会得到64(二进制1000000),如下所示:

let oldValue = 2;              //等于二进制10
let newValue = oldValue << 5;  //等于二进制1000000,即十进制64
注意在移位后,数值右端会空出5位。左移会以0填充这些空位,让结果是完整的32位数值.
注意,左移会保留它所操作数值的符号。比如,如果-2左移5位,将得到-64,而不是正64。
这个是:左边最高位丢弃,右边补齐0
面试题: 请用最有效率的方式写出计算2乘以8的结果?

有符号右移由两个 大于号(>>) 表示,会将数值的所有32位都向右移,同时保留符号(正或负)。有符号右移实际上是左移的逆运算。比如,如果将64右移5位,那就是 2:

let oldValue = 64;             //等于二进制1000000
let newValue = oldValue >> 5;  //等于二进制10,即十进制2
同样,移位后就会出现空位。不过,右移后空位会出现在左侧,且在符号位之后。ECMAScript会用符号位的值来填充这些空位,
以得到完整的数值。
同时保留符号(正或负)这句话的意思是:最高位是0,左边补齐0;最高为是1,左边补齐1

无符号右移用3个大于号表示(>>>),会将数值的所有32位都向右移。对于正数,无符号右移与有符号右移结果相同。仍然以前面有符号右移的例子为例,64向右移动5位,会变成2:

let oldValue = 64;              //等于二进制1000000
let newValue = oldValue >>> 5;  //等于二进制10,即十进制2
无符号右移 无论最高位是0还是1,左边补齐0

对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补0,而不管符号位是什么。对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。无符号右移操作符将负数的二进制表示当成正数的二进制表示来处理。因为负数是其绝对值的二补数,所以右移之后结果变得非常之大,如下面的例子所示:

let oldValue = -64;              //等于二进制11111111111111111111111111000000
let newValue = oldValue >>> 5;   //等于十进制134217726
在对-64无符号右移5位后,结果是134 217 726。这是因为-64的二进制表示是 11111111111111111111111111000000,无符号右移
却将它当成正值。                                   

四、Hex 编码与 Base64 编码

4.1 Hex 编码

Hex 编码就是十六进制编码,是一种用16个字符表示任意二进制数据的方法,其实就是将字符所对应的码值转为十六进制后拼接。
Hex 编码的应用:1、密钥的编码 2、密文的编码

Hex 编码特点:

用 0-9、a-f 16个字符表示
字符编码是一个字节或几个字节对应一个字符,而Hex编码是4个bit对应一个字符
2个十六进制字符代表一个字节
在实际应用中,一定要分清楚得到的数据是哪种编码的,采用对应方式解析,才能得到正确的结果
编程中很多问题,需要从字节甚至二进制位的角度去考虑,才能明白

简单的 Hex 编码实现,参考代码如下:

let test_str = "AmoXiang666"
let table = "0123456789abcdef"
let result = ""for (let i = 0; i < test_str.length; i++) {// 使用charCodeAt()方法可以查看指定码元的字符编码。这个方法返回指定索引位置的码元值,索引以整数指定。let ascii = test_str.charCodeAt(i)// 65 ==> 0100 0001let left = ascii >> 4let right = ascii & 0xfresult += table[left] + table[right]
}
console.log(result)// let oldValue = 64;             //等于二进制1000000
// let newValue = oldValue >> 5;  //等于二进制10,即十进制2
// console.log(newValue)
// 416d6f5869616e67363636
// 416d6f5869616e67363636

使用 CryptoJS 实现,参考代码如下:

let cryptoJs = require("./CryptoJS")
// { stringify: [Function: stringify], parse: [Function: parse] }
// stringify: 编码 parse: 解码
console.log(cryptoJs.enc.Hex)
let wordArray = cryptoJs.enc.Utf8.parse("AmoXiang666")
console.log(wordArray)
console.log(cryptoJs.enc.Hex.stringify(wordArray))
console.log(cryptoJs.enc.Hex.parse("416d6f5869616e67363636").toString(cryptoJs.enc.Utf8))

补充:

Hex 编码的实现比较简单,且不是所有的实现都会出现码表
一般 Hex 编码都是比较标准的,不会进行魔改
URL 编码:URL 编码是 GET 请求中比较常见的,是将请求地址中的参数进行编码,尤其是对于中文参数。(其实就是 Hex 编码,只不过在每一个字节前加了一个%)
编码与解码的方式是公开的,只要知道码表即可

4.2 Base64 编码

Base64 是一种用 64个字符 表示任意二进制数据的方法,Base64 的应用:RSA密钥的编码、密文的编码、图片的编码。Base64 码表如下图所示:
在这里插入图片描述
Base64 的代码实现:

let cryptoJs = require('./CryptoJS');
let wordArray = cryptoJs.enc.Utf8.parse('AmoXiang666');
console.log(wordArray)
console.log(cryptoJs.enc.Base64.stringify(wordArray));

Base64 码表的妙用: 为了传输数据安全,通常会对 Base64 数据进行 URL 编码,或者会把 +/ 替换成 -_

Base64 编码细节:

  1. 每个 Base64 字符代表原数据中的 6bit
  2. Base64 编码后的字符数,是 4 的倍数
  3. 编码的字节数是 3 的倍数时,不需要填充

Base64 编码的特点:

  1. A-Z、a-z、0-9、+/ 64个字符表示,= 作为填充使用
  2. Base64 编码是编码,不是压缩,编码后只会增加字节数
  3. 标准的 Base64 每行为 76 个字符,行末添加换行符
  4. Base64 编码的码表可能会被魔改

Hex 和 Base64 的转换:

let CryptoJS = require('../CryptoJS')
let text = 'amo666'
// 64编码
let wordArray = CryptoJS.enc.Utf8.parse(text)
let b64 = CryptoJS.enc.Base64.stringify(wordArray)
console.log(b64)
let b64_wordArray = CryptoJS.enc.Base64.parse(b64)
console.log(CryptoJS.enc.Hex.stringify(b64_wordArray))

五、消息摘要算法

5.1 简介

消息摘要算法(Message Digest Algorithm)是一类密码学哈希函数,用于产生数据的摘要,通常是固定长度的二进制串。消息摘要算法接受任意长度的消息作为输入,并输出固定长度的摘要。消息摘要算法具有以下特点:

  1. 散列后的密文不可逆
  2. 散列后的结果唯一。一般用于校验数据完整性、签名sign,由于密文不可逆,所以服务端也无法解密,想要验证,就需要跟前端一样的方式去重新签名一遍,签名算法一般会把源数据和签名后的值一起提交到服务端,要保证在签名时候的数据和提交上去的源数据一致
  3. 哈希碰撞

常见的哈希算法包括但不限于:

  1. MD5(Message Digest Algorithm 5): MD5 是一种广泛使用的哈希函数,生成 128 位(16 字节)的哈希值。然而,由于其存在安全性漏洞,已不推荐用于加密目的,而主要用于校验数据完整性等非加密场景。
  2. SHA-1(Secure Hash Algorithm 1): SHA-1 生成 160 位(20 字节)的哈希值,被广泛应用于数字签名、证书签名等场景。但是,SHA-1 也已经被证明存在碰撞攻击的安全性问题,因此也不再推荐用于安全目的。
  3. SHA-256、SHA-384、SHA-512: 这些是安全哈希算法家族中的一部分,分别生成 256 位、384 位和 512 位长度的哈希值。它们是目前广泛应用于数据完整性验证、数字签名等安全领域的哈希算法。
  4. RIPEMD(RACE Integrity Primitives Evaluation Message Digest):RIPEMD 系列是一组哈希函数,分为 RIPEMD-128、RIPEMD-160、RIPEMD-256 和 RIPEMD-320,分别生成不同长度的哈希值。它们在一些特定的应用场景中有一定的使用。
  5. BLAKE2:BLAKE2 是一种高速、安全的哈希函数,能够生成不同长度的哈希值。它在性能方面优于许多其他哈希算法,并且在一些应用场景中取得了广泛的应用。
  6. Whirlpool:Whirlpool 是一种比较少见但仍在一些场景中使用的哈希函数,生成 512 位长度的哈希值,被认为具有较高的安全性。
  7. SHA-3(Secure Hash Algorithm 3):SHA-3 是美国国家标准与技术研究所(NIST)发布的一种哈希算法标准,与 SHA-2 不同,SHA-3 使用了基于 Keccak 构造的算法。SHA-3 提供了多种摘要长度的选择,包括 224 位、256 位、384 位和 512 位。
  8. HMAC 算法, 它是一种基于哈希函数的消息认证码算法。HMAC 通过将密钥与消息进行连续的哈希运算,结合了密钥的安全性和哈希函数的强度,从而提供了一种安全的消息认证方式。它通常使用的哈希函数包括 MD5、SHA-1、SHA-256 等,因此 HMAC 可以基于不同的哈希算法进行实现,如 HMAC-MD5、HMAC-SHA1、HMAC-SHA256 等。

对比:
在这里插入图片描述
这些哈希算法在不同的应用场景中具有不同的特点和适用性,选择合适的哈希算法取决于具体的需求以及安全性要求。

5.2 JS中的MD5、SHA、HMAC、SM3

CryptoJS 字符串解析: 如果加密函数传入的参数是 string 类型的数据,将使用默认的 Utf8.parse 来进行解析,示例代码:

let CryptoJS = require('../CryptoJS');
// ① string转wordArray
console.log(CryptoJS.enc.Utf8.parse('AmoXiang666'));
console.log(CryptoJS.enc.Hex.parse('416d6f5869616e67363636'));
console.log(CryptoJS.enc.Base64.parse('QW1vWGlhbmc2NjY='));// ② wordArray转string
let wordArray = CryptoJS.enc.Utf8.parse('AmoXiang666');
console.log(wordArray.toString());  // Hex
console.log(wordArray + '');        // Hex
console.log(wordArray.toString(CryptoJS.enc.Base64));
console.log(wordArray.toString(CryptoJS.enc.Utf8));
// console.log(CryptoJS.enc.Utf8.stringify(wordArray));
// console.log(CryptoJS.enc.Base64.stringify(wordArray));
wordArray = CryptoJS.enc.Utf8.parse('AmoXiang666');
let hex = wordArray.toString();
console.log(hex);
console.log(CryptoJS.enc.Hex.parse(hex).toString(CryptoJS.enc.Base64));
console.log(CryptoJS.MD5('AmoXiang666').toString());

MD5: 加密后的字节数组可以编码成 Hex、Base64,CryptoJS 库默认输出 Hex,没有任何输入,也能计算 hash 值,碰到加 salt 的 MD5,可以直接输入空的值,得到结果去 CMD5 查询一下,有可能就得到 salt,示例代码:

let CryptoJS = require('../CryptoJS');
let wordArray = CryptoJS.enc.Utf8.parse('AmoXiang666');
console.log(CryptoJS.MD5(wordArray) + ''); //默认加密结果为hex编码
console.log(CryptoJS.MD5(wordArray).toString(CryptoJS.enc.Base64)); //转换为base64编码let hexMd5 = CryptoJS.MD5(wordArray) + '';
console.log(hexMd5)
wordArray = CryptoJS.enc.Hex.parse(hexMd5); //字节数组
console.log(wordArray)
console.log(CryptoJS.enc.Base64.stringify(wordArray));
console.log(CryptoJS.MD5() + ''); //没有任何输入,也能计算 hash 值let CryptoJS = require('../CryptoJS');
console.log(CryptoJS.MD5('AmoXiang666').toString()); //默认会使用CryptoJS.enc.Utf8.parse
let xiaArr = CryptoJS.enc.Utf8.parse('AmoXiang666');
console.log(CryptoJS.MD5(xiaArr).toString());

SHA:

// 简单写法
let CryptoJS = require('../CryptoJS');
let wordArray = CryptoJS.enc.Utf8.parse('AmoXiang666');
console.log(CryptoJS.SHA1(wordArray) + '');
console.log(CryptoJS.SHA256(wordArray) + '');
console.log(CryptoJS.SHA512(wordArray) + '');
console.log(CryptoJS.SHA224(wordArray) + '');
console.log(CryptoJS.SHA384(wordArray) + '');
console.log(CryptoJS.SHA3(wordArray) + '');// 另外的写法
let SHA1 = CryptoJS.algo.SHA1.create();
SHA1.update('AmoXiang666');
let cipherText = SHA1.finalize() + '';
console.log(cipherText);
//SHA1.reset(); 重置
SHA1.update('jerry');
console.log(SHA1.finalize() + '');

HMAC算法: HMAC 算法与 MD 和 SHA 的区别是多了一个密钥,密钥可以随机给,HMAC 的密文长度与 MD 和 SHA 是一致的,同样加密后的字节数组可以编码成 Hex、Base64,CryptoJS 库默认输出 Hex,没有任何输入,也能计算 hash 值。

let CryptoJS = require('../CryptoJS');
console.log(CryptoJS.HmacMD5('', 'key') + '');
console.log(CryptoJS.HmacMD5('AmoXiang666', 'key') + '');
console.log(CryptoJS.HmacSHA1('AmoXiang666', 'key') + '');
console.log(CryptoJS.HmacSHA1('AmoXiang666', 'key').toString(CryptoJS.enc.Base64));let hmacSHA1 = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1.create(), 'key');
hmacSHA1.update('AmoXiang666');
console.log(hmacSHA1.finalize() + '');

SM3算法: 国密算法有很多种,其中 SM3 是类似于 SHA256 的消息摘要算法,SM3 的输入长度与 SHA 算法一致,最大为 264-1,SM3 的密文长度与 SHA256 一致,同样加密后的字节数组可以编码成 Hex、Base64,没有任何输入,也能计算 hash 值。

//这一步是先将输入数据转成utf-8编码的字节流,然后再转成16进制可见字符
var dataBy = Hex.utf8StrToBytes('AmoXiang666');
var sm3 = new SM3Digest();
sm3.update(dataBy,0,dataBy.length);	//数据很多的话,可以分多次update
var sm3Hash = sm3.doFinal();	//得到的数据是个byte数组
var sm3HashHex = Hex.encode(sm3Hash,0,sm3Hash.length);	//编码成16进制可见字符
console.log(sm3HashHex);

六、对称加密算法

6.1 介绍

对称加密算法是一种加密技术,使用 相同的密钥 对数据进行加密和解密。这意味着发送方和接收方必须共享相同的密钥,用于加密和解密数据。对称加密算法具有加密速度快、计算效率高的特点,适合对大量数据进行加密。一些常见的对称加密算法包括:

  1. DES(Data Encryption Standard): DES 是一种早期的对称加密算法,使用 56 位密钥对数据进行加密和解密。尽管 DES 在安全性上存在一些弱点,但它为后续的加密算法奠定了基础。(在实际使用中,DES 密钥通常由 64 位长度的密钥中的第 8 位用作奇偶校验位,因此实际上只有 56 位是用于加密和解密的密钥位数)
  2. 3DES(Triple DES): 3DES 是 DES 的增强版本,它多次对数据进行 DES 加密,通常使用两个或三个密钥。虽然 3DES 提供了更高的安全性,但由于其计算复杂度较高,已经逐渐被更先进的加密算法所取代。(在使用 3DES 进行加密时,通常会使用两个密钥(K1 和 K2)或三个密钥(K1、K2 和 K3)进行三次 DES 加密。如果使用两个密钥,则每个密钥长度为 56 位,总长度为 112 位。如果使用三个密钥,则每个密钥长度为 56 位,总长度为 168 位)
  3. AES(Advanced Encryption Standard): AES 是一种广泛使用的对称加密算法,设计用于取代 DES。它支持不同的密钥长度,包括 128 位、192 位和 256 位,具有较高的安全性和较快的加密速度,因此被广泛应用于各种安全领域。(AES(Advanced Encryption Standard)算法的密钥长度要求:AES-128:128 位(16 字节)、AES-192:192 位(24 字节)、AES-256:256 位(32 字节))
  4. Blowfish:Blowfish 是一种对称加密算法,支持变长密钥(32 至 448 位),并且具有高速和高度可配置性的特点。尽管 Blowfish 在许多场景下仍然被使用,但它已经逐渐被更先进的算法所取代。
  5. RC4(Rivest Cipher 4):RC4 是一种流密码(Stream Cipher)算法,具有简单、高效的特点。尽管 RC4 曾被广泛应用于 SSL/TLS、WEP 等协议中,但由于其存在一些安全性问题,如密钥漏洞和偏置攻击,已经逐渐被淘汰。
  6. SM4 算法, 也称为国密算法,是由中国密码学家提出的一种分组加密算法,被采纳为中国商用密码算法标准。它是一种对称加密算法,用于对数据进行加密和解密。SM4 算法的主要特点包括:①分组大小: SM4 使用 128 位(16 字节)的分组大小进行加密和解密操作。②密钥长度: SM4 算法支持密钥长度为 128 位(16 字节)。③轮数: SM4 算法采用了 32 轮的 Feistel 结构进行加密,每轮包括逐位的非线性变换和线性变换。④S 盒: SM4 使用了一个固定的 8x8 的 S 盒,用于非线性变换,增强了算法的安全性。⑤密钥扩展: SM4 算法对输入的密钥进行扩展,生成多轮加密过程中所需的轮密钥。⑥安全性: SM4 算法经过了严格的密码学分析和安全性评估,被认为具有较高的安全性和抗攻击能力。SM4 的代码实现细节,在专栏安卓逆向中做介绍。

6.2 加密模式和填充方式

在对称加密算法中,加密模式和填充模式是两个重要的概念,用于指定如何对数据进行加密和解密。

加密模式(Encryption Mode): 加密模式定义了在加密过程中如何处理数据块、处理块之间的关系以及如何处理最后一个块的方法。常见的加密模式包括:

ECB(Electronic Codebook)模式:将明文分成固定大小的块,并独立地对每个块进行加密。
CBC(Cipher Block Chaining)模式:每个明文块先与前一个密文块进行异或运算,然后再进行加密。
CFB(Cipher Feedback)模式:将前一个密文块作为加密器的输入,产生密文块。
OFB(Output Feedback)模式:将前一个密文块作为加密器的输入,产生密钥流,再与明文进行异或运算得到密文。
CTR(Counter)模式:使用一个计数器和密钥生成伪随机密钥流,再与明文进行异或运算得到密文。

填充模式(Padding Mode): 填充模式用于在加密前将不满足块大小要求的数据块填充到合适的长度,以便进行加密。常见的填充模式包括:

PKCS#5 和 PKCS#7:使用一定规则填充数据块,通常采用的填充值是缺少的字节数。
Zero Padding:填充的字节全部为零。
ANSI X.923:除了最后一个字节外,填充的字节为零,最后一个字节表示填充的字节数。
ISO 10126:填充的字节为随机值,最后一个字节表示填充的字节数。

6.3 CryptoJS 中DES、DESede、AES算法实现

DES: DES 密钥长度为 64bit(实际使用长度是 56bit,前面已经提到过),分组长度为 64bit,CryptoJS 中 DES 算法的实现:

let CryptoJS = require('../CryptoJS');let plainText = CryptoJS.enc.Utf8.parse('AmoXiang666');
// 00110001 00110010 00110011 00110100 00110101 00110110 00110111 00111000   12345678
// 00110000 00110011 00110010 00110101 00110100 00110111 00110110 00111001   03254769
// let key = CryptoJS.enc.Utf8.parse('12345678'); //密钥
let key = CryptoJS.enc.Utf8.parse('03254769'); //两个完全不同的密钥可以加密得到相同的结果
let iv = CryptoJS.enc.Utf8.parse('88888888');
let cfg = {iv: iv, //如果加密模式是ECB,则不需要加iv,加了也用不上mode: CryptoJS.mode.CBC, //填充的模式padding: CryptoJS.pad.Pkcs7 //cfg中没有传mode与padding,默认使用CBC的加密模式,Pkcs7的填充方式
};
let cipherObj = CryptoJS.DES.encrypt(plainText, key, cfg); //加密
console.log(cipherObj)
// 加密的结果cipherObj是一个对象,调用toString()方法默认转Base64编码的密文
console.log(cipherObj.toString());
// 转hex可以使用下面的方式
console.log(cipherObj.ciphertext.toString());//6cfaefd865294e2970c639c3eedc4b4e
key = CryptoJS.enc.Utf8.parse('12345678');
//let key = CryptoJS.enc.Utf8.parse('03254769');
iv = CryptoJS.enc.Utf8.parse('88888888');
let cipherText = CryptoJS.enc.Hex.parse('6cfaefd865294e2970c639c3eedc4b4e').toString(CryptoJS.enc.Base64);
cfg = {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7
};
let plainObj = CryptoJS.DES.decrypt(cipherText, key, cfg);
console.log(plainObj.toString(CryptoJS.enc.Utf8));

DESede: DESede 算法明文按 64 位进行分组加密,密钥长度为 24 字节,本质为三次 DES 加解密,DES 加密(使用密钥前8个字节),DES 解密(使用密钥中8个字节),DES 加密(使用密钥后8个字节),CryptoJS 中 DESede 算法的实现:

let CryptoJS = require('../CryptoJS');let plainText = CryptoJS.enc.Utf8.parse('AmoXiang666');
let key = CryptoJS.enc.Utf8.parse('123456783333333388888888');
let iv = CryptoJS.enc.Utf8.parse('88888888');
var cfg = {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7
};
let cipherObj = CryptoJS.TripleDES.encrypt(plainText, key, cfg);
console.log(cipherObj.toString());
console.log(cipherObj.ciphertext.toString());

AES: 根据密钥长度不同,分为 AES128、AES192、AES256
在这里插入图片描述
CryptoJS 中 AES 算法的实现:

let CryptoJS = require('../CryptoJS');
let plainText = CryptoJS.enc.Utf8.parse('AmoXiang666');
let key = CryptoJS.enc.Utf8.parse('1234567890abcdef12345678');
let iv = CryptoJS.enc.Utf8.parse('1234567890abcdef');
var cfg = {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7
};
let cipherObj = CryptoJS.AES.encrypt(plainText, key, cfg);
console.log(cipherObj.toString());
console.log(cipherObj.ciphertext.toString());

6.4 对称加密算法注意事项

  1. 要复现一个对称加密算法,需要得到明文、key、iv、mode、padding

  2. 明文、key、iv 需要注意解析方式,而且不一定是字符串形式

  3. 如果明文中有两个分组的内容相同,ECB 会得到完全一样的密文,CBC 不会

  4. 加密算法的结果通常与明文等长或者更长,如果变短了,那可能是 gzip、protobuf

  5. 密文/明文的自定义输出/输入(cfg 中 format 的指定)

    let CryptoJS = require('../CryptoJS');
    let plainText = CryptoJS.enc.Utf8.parse('AmoXiang666');
    let key = CryptoJS.enc.Utf8.parse('1234567890abcdef12345678');
    let iv = CryptoJS.enc.Utf8.parse('1234567890abcdef');
    // let cfg = {
    //     iv: iv,
    //     mode: CryptoJS.mode.CBC,
    //     padding: CryptoJS.pad.Pkcs7,
    //     format: CryptoJS.format.Hex
    // };
    let format = {stringify: function (data) {let e = {ct: data.ciphertext.toString(CryptoJS.enc.Base64),miaoshu: "这是我们的自定义输出内容"};return JSON.stringify(e)},parse: function (data) {let json = JSON.parse(data);let newVar = CryptoJS.lib.CipherParams.create({ciphertext:CryptoJS.enc.Base64.parse(json.ct)});return newVar}
    };
    //
    let cfg = {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7,format: format
    };
    //
    let cipherObj = CryptoJS.AES.encrypt(plainText, key, cfg);
    console.log(cipherObj.toString());
    let cipherText = cipherObj.toString();
    let plainObj = CryptoJS.AES.decrypt(cipherText, key, cfg);
    console.log(plainObj.toString(CryptoJS.enc.Utf8));
    
  6. CryptoJS 自动生成 key、iv、salt。参考文章链接:https://www.jianshu.com/p/0689506403e7

    let CryptoJS = require('../CryptoJS');
    let format = {stringify: function (data){let e = {ct: data.ciphertext.toString(),iv: data.iv.toString(),salt: data.salt.toString(),};return JSON.stringify(e)},parse: function (data){let json = JSON.parse(data);return  CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(json.ct),iv: CryptoJS.enc.Hex.parse(json.iv),salt: CryptoJS.enc.Hex.parse(json.salt),});}
    };
    var cfg = {format: format
    };
    let cipherObj = CryptoJS.AES.encrypt('AmoXiang666', '12345678123456781234567812345678', cfg);
    let cipherText = cipherObj.toString();
    console.log(cipherText);let plainObj = CryptoJS.AES.decrypt(cipherText, '12345678123456781234567812345678', cfg);
    console.log(plainObj.toString(CryptoJS.enc.Utf8));
    

6.5 CryptoJS(其他算法)

示例代码:

let CryptoJS = require('../CryptoJS');
console.log(CryptoJS.RIPEMD160('AmoXiang666').toString());
console.log(CryptoJS.HmacRIPEMD160('AmoXiang666', 'keykeykey').toString());
console.log(CryptoJS.PBKDF2('AmoXiang666', 'keykeykey1234', {keySize: 4, iterations: 2000}).toString());
console.log(CryptoJS.EvpKDF('AmoXiang666', 'keykeykey1234', {keySize: 4, iterations: 2000}).toString());console.log(CryptoJS.RC4);
console.log(CryptoJS.RC4Drop);
console.log(CryptoJS.Rabbit);
console.log(CryptoJS.RabbitLegacy);

SM4 算法, JS 实现:

//sm4-1.0.js
function sm4_encrypt_ecb() {let inputBytes = Hex.decode('0123456789abcdeffedcba9876543210');let key = Hex.decode('0123456789abcdeffedcba9876543210');let sm4 = new SM4();let cipher = sm4.encrypt_ecb(key, inputBytes);console.log(Hex.encode(cipher, 0, cipher.length));
}function sm4_encrypt_cbc() {let inputBytes = Hex.decode('0123456789abcdeffedcba9876543210');let key = Hex.decode('0123456789abcdeffedcba9876543210');let iv = Hex.decode('0123456789abcdeffedcba9876543210');let sm4 = new SM4();let cipher = sm4.encrypt_cbc(key, iv, inputBytes);console.log(Hex.encode(cipher, 0, cipher.length));
}
sm4_encrypt_ecb();
sm4_encrypt_cbc();

七、非对称加密算法

非对称加密算法: 加密解密使用不同密钥的算法,典型算法,RSA、SM2

非对称加密算法通常有一个密钥对,称为公钥和私钥

公钥加密的数据,私钥才能解密
私钥加密的数据,公钥才能解密

密钥对需要生成,不是随便写的,RSA 密钥对的生成 http://web.chacuo.net/netrsakeypair

私钥的格式:

PKCS1格式通常开头是 -----BEGIN RSA PRIVATE KEY-----
PKCS8格式通常开头是 -----BEGIN PRIVATE KEY-----Java中使用的私钥通常是PKCS8格式

RSA密钥的形式: Base64 编码形式(PEM格式)、Hex 编码形式。公钥是可以公开的,私钥保密,私钥包含公钥,从公钥无法推导出私钥,数字签名算法使用私钥签名,此时私钥会出现在客户端。

RSA 常见加密库的使用(jsencrypt.js、RSA.js): 加密后的字节数组可以编码成 Hex、Base64。

//jsencrypt.js
function getEncrypt(password, publickey){var jsEncrypt = new JSEncrypt();jsEncrypt.setPublicKey(publickey);return jsEncrypt.encrypt(password);
}var publicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxRQHxL/8xZ1EaNmQBGZnpMiCY" +"7gRzog6nDjfBJacytEiVJnJRuq1V/D+JKaXDwetsCnSUaz65LCFHU09OSEYee5oC" +"iI0ql21EA306c91oT/fQpPngQGZHLUtDOUdJVlAKnicCvmR24NqyNKFuY8L0cnB1" +"zcax73Rf+Ctf/lxAOwIDAQAB";console.log(getEncrypt("AmoXiang666", publicKeyBase64));

RSA 加密处理安全,但是性能极差,单次加密长度有限制,一般用于加密较短的数据,需要加密较长的数据时,会和对称加密算法结合使用。RSA 填充细节:

NOPadding:明文最多字节数为密钥字节数,密文与密钥等长,填充字节0,加密后的密文不变
PKCS1Padding:明文最大字节数为密钥字节数-11,密文与密钥等长,每一次的填充不一样,使得加密后的密文会变

多种加密算法的常见结合套路:

① 随机生成 AES 密钥 AESKey 
② AESKey 密钥用于 AES 加密数据,得到数据密文 cipherText 
③ 使用 RSA 对 AESKey 加密,得到密钥密文 cipherKey 
④ 提交密钥密文 cipherKey 和数据密文 cipherText 给服务器

JS 数字签名算法库的使用:

var signData = "AmoXiang666";
//PKCS1格式的密钥 前缀 -----BEGIN RSA PRIVATE KEY-----    后缀 -----END RSA PRIVATE KEY-----
//PKCS8格式的密钥 前缀 -----BEGIN PRIVATE KEY-----    后缀 -----END PRIVATE KEY-----
var privateKeyBase64 = "-----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhki" +"G9w0BAQEFAASCAmEwggJdAgEAAoGBAPFFAfEv/zFnURo2\n" +"ZAEZmekyIJjuBHOiDqcON8ElpzK0SJUmclG6rVX8P4kppcPB62wKdJRrPrksIUdT\n" +"T05IRh57mgKIjSqXbUQDfTpz3WhP99Ck+eBAZkctS0M5R0lWUAqeJwK+ZHbg2rI0\n" +"oW5jwvRycHXNxrHvdF/4K1/+XEA7AgMBAAECgYEAsGkDrYWps0bW7zKb1o4Qkojb\n" +"etZ2HNJ+ojlsHObaJOHbPGs7JXU4bmmdTz5LfSIacAoJCciMuTqCLrPEhfmkghPq\n" +"U2MjyjfqYdXALoP7l/vt6QmjY/g1IAsaZN9nFhyjJ2WzgOx1f7gZj4NBSvTdSj7H\n" +"m5E24zkm+p7Qw1z6/mkCQQD7WSXAXcv2v3Vo6qi1FUlkzQgCQLFYqXNSOSPpno3y\n" +"oohUFIkMj0bYGbVE1LzV30Rb6Z8e8yQAByw6l8RuGb2PAkEA9bwb2euyOe6CcqpE\n" +"PNFc+7UlOJAy5epVFKHbu0aNivVpU0hsphqjIGXJGHYTspyEOLqtzILqKPZr6pru\n" +"WvJUlQJBAJoImQUZtlyCGs7wN/G5mN/ocscGpGikd+Lk16hdHbqbdpaoexCyYYUf\n" +"xCHpicw75mW5d2V9Ngu6WZWS2rNqnOsCQCoMK//X8sEy7KNOOyrk8DIpxtqs4eix\n" +"dil3oK+k3OdgIsubYuvxNuR+RjCnU6uGWKGUX9TUudiUgda89/gb6xkCQFm8gD6n\n" +"AyN+PPPKRq2M84+cAbnvjdIAY3OFHfkaoWCtEj5DR0UDuVv7jN7+re2D7id/GkAe\n" +"FAmhvYQwwLnifrw=-----END PRIVATE KEY-----";function doSign() {var signature = KEYUTIL.getKey(privateKeyBase64);var hSig = signature.signString(signData, "sha256");return hex2b64(hSig);
}console.log(doSign());

SM2 算法库的使用: 参考 gmjs-master

说明

文章转载自@Amo Xiang,2024最新版JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 OpenAI 进行数据探索性分析(EDA)

#探索性数据分析&#xff08;Exploratory Data Analysis, 简称 EDA&#xff09;是数据分析中不可或缺的环节&#xff0c;帮助分析师快速了解数据的分布、特征和潜在模式。传统的 EDA 通常需要手动编写代码或使用工具完成。现在&#xff0c;通过 OpenAI 的 GPT-4 模型&#xff0…

2、计算机网络七层封包和解包的过程

计算机网络osi七层模型 1、网络模型总体预览2、数据链路层4、传输层5.应用层 1、网络模型总体预览 图片均来源B站&#xff1a;网络安全收藏家&#xff0c;没有本人作图 2、数据链路层 案例描述&#xff1a;主机A发出一条信息&#xff0c;到路由器A&#xff0c;这里封装目标MAC…

使用 PyTorch-BigGraph 构建和部署大规模图嵌入的完整教程

当涉及到图数据时&#xff0c;复杂性是不可避免的。无论是社交网络中的庞大互联关系、像 Freebase 这样的知识图谱&#xff0c;还是推荐引擎中海量的数据量&#xff0c;处理如此规模的图数据都充满挑战。 尤其是当目标是生成能够准确捕捉这些关系本质的嵌入表示时&#xff0c;…

国标GB28181视频平台EasyCVR视频融合平台H.265/H.264转码业务流程

在当今数字化、网络化的视频监控领域&#xff0c;大中型项目对于视频监控管理平台的需求日益增长&#xff0c;特别是在跨区域、多设备、高并发的复杂环境中。EasyCVR视频监控汇聚管理平台正是为了满足这些需求而设计的&#xff0c;它不仅提供了全面的管理功能&#xff0c;还支持…

使用win32com将ppt(x)文件转换为pdf文件

本文来记录下如何使用win32com将ppt(x)文件转换为pdf文件 文章目录 win32com概述win32com优缺点代码实例本文小结 win32com概述 Pywin32 是一个用于与 Microsoft Windows 操作系统交互的 Python 扩展模块&#xff0c;它提供了对多个 Windows API 的访问&#xff0c;包括对 Mic…

java itext后端生成pdf导出

public CustomApiResult<String> exportPdf(HttpServletRequest request, HttpServletResponse response) throws IOException {// 防止日志记录获取session异常request.getSession();// 设置编码格式response.setContentType("application/pdf;charsetUTF-8")…

HarmonyOs实战项目=>App首页架构沉浸式效果

1.沉浸式效果方案采用> 窗口全屏布局方案不隐藏状态栏和导航条(不隐藏避让区) 2.沉浸式效果截图 3.主要代码截图 4.大功告成&#xff0c;代码见资源

华为HCCDA云技术认证--网络服务

大家好呀&#xff01;我是reload。今天继续带大家学习华为HCCDA云技术认证&#xff0c;涵盖华为云最为核心的计算、存储、网络、数据库、安全、部署等服务。今天学习网络服务相关内容。 登录华为云官网&#xff1a;https://www.huaweicloud.com/ &#xff0c;进入首页&#xff…

如何提升自己的情商?

在当今社会&#xff0c;情商的重要性越来越被人们所认识和重视。无论是职场发展、人际关系&#xff0c;还是自我成长&#xff0c;情商都起着关键的作用。那么&#xff0c;如何提升自己的情商呢&#xff1f; 一、自我认知&#xff1a;了解自己&#xff0c;方能明智处世 自我认…

SSM post接口传递json 报错 HTTP状态 415 - 不支持的媒体类型

这篇文章是写给哪些在小破站学习ssm教程的兄弟们&#xff0c;我们都是萌新&#xff0c;大佬就让行吧感谢理解&#xff01; 本文章主要讲解B站赵伟风SSM教程第108节(JSON数据的接收) 我所有的配置都跟老师一样&#xff0c;老师就很顺利发出去了&#xff0c;我的就是一直415&am…

《Python编程实训快速上手》第七天--文件与文件路径

该章节将使用Python在硬盘上创建、读取和保存文件 一、文件与文件路径 1、Windows中使用\以及macOS和Linux中使用/ 使用pathlib模块中的Path()函数进行文件名和目录的拼接,返回文件路径字符串 from pathlib import Path print(Path("spam","bacon",&qu…

StarRocks 架构

StarRocks 是什么&#xff1f;&#xff08; What is StarRocks?&#xff09; StarRocks 是 MPP 的查询引擎&#xff0c;用来做实时查询&#xff0c;提供亚秒级的查询性能。 兼容 MYSQL 协议&#xff0c;可以和大部分 BI 工具进行无缝衔接。 Apache 2.0 开源产品。 使用场景&…

A038-基于SpringBoot的乡村养老服务管理系统登录

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

WPF应用程序的生命周期-笔记

应用程序的生命周期 窗体的生命周期: window是一个控件 window中的事件:从window创建到关闭我们称之为窗体的生命周期 每一个窗体的生命周期都是独立的,窗体与窗体之间的生命周期都是独立的 最早加载的事件 控件的事件触发 基本所有的控件都继承与Framework 在WPF中基本所有的…

H.265流媒体播放器EasyPlayer.js视频流媒体播放器关于直播流播放完毕是否能监听到

EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0c;也能支持WebS…

智能停车解决方案之停车场室内导航系统(二):核心技术与系统架构构建

hello~这里是维小帮&#xff0c;如有项目需求和技术交流欢迎大家私聊我们&#xff01;点击文章最下方获取智慧停车场方案~撒花&#xff01; 随着城市化进程的加速&#xff0c;停车难问题日益凸显。智能停车系统作为缓解停车压力的有效手段&#xff0c;其核心技术与架构的构建至…

美赛优秀论文阅读--2023C题

文章目录 1.题目说明2.我对于这个题目信息的理解3.优秀论文学习3.1摘要3.2相关的算法模型 4.总结 1.题目说明 今天阅读的这个文章来自于这个2023年的这个美赛的这个C题的论文&#xff1b; 我们的这个题目可以到网上去找&#xff0c;这个还是比较容易找到的&#xff0c;大致就…

无人机飞手入门指南

无人机飞手入门指南旨在为初学者提供一份全面的学习路径和实践建议&#xff0c;帮助新手快速掌握无人机飞行技能并了解相关法规知识。以下是一份详细的入门指南&#xff1a; 一、了解无人机基础知识 1. 无人机构造&#xff1a;了解无人机的组成部分&#xff0c;如机身、螺旋桨…

【JavaEE初阶 — 多线程】定时器的应用及模拟实现

目录 1. 标准库中的定时器 1.1 Timer 的定义 1.2 Timer 的原理 1.3 Timer 的使用 1.4 Timer 的弊端 1.5 ScheduledExecutorService 2. 模拟实现定时器 2.1 实现定时器的步骤 2.1.1 定义类描述任务 定义类描述任务 第一种定义方法 …

AIGC----生成对抗网络(GAN)如何推动AIGC的发展

AIGC: 生成对抗网络(GAN)如何推动AIGC的发展 前言 随着人工智能领域的迅猛发展&#xff0c;AI生成内容&#xff08;AIGC&#xff0c;AI Generated Content&#xff09;正成为创意产业和技术领域的重要组成部分。在AIGC的核心技术中&#xff0c;生成对抗网络&#xff08;GAN&am…