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

news2024/11/23 1:33:22

目录

  • 一、密码学介绍
    • 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 1001
   3 = 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 1101
     1111 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 1001
 3 = 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 1101
     1111 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 1001
   3 = 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 1101
     0000 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 0001
    let left = ascii >> 4
    let right = ascii & 0xf
    result += 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

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

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

相关文章

代理IP干货:如何正确使用防范风险?

在今天的数字时代&#xff0c;代理IP地址已成为互联网世界中不可或缺的一部分。无论您是寻求绕过地理限制、保护个人隐私还是执行网络任务&#xff0c;代理IP地址都发挥着关键作用。我们将为您探讨代理IP地址的重要性以及如何防范潜在的风险和威胁。 一、代理IP地址的潜在风险 …

CUDA编程技术概述

CUDA&#xff08;Compute Unified Device Architecture&#xff0c;统一计算设备架构&#xff09;是由英伟达&#xff08;NVIDIA&#xff09;公司推出的一种软硬件集成技术&#xff0c;是该公司对于GPGPU&#xff08;通用图形处理器计算&#xff09;的正式名称。透过这个技术&a…

微信小程序用户隐私协议保护指引自定义组件封装

这是一个微信小程序用户隐私协议保护指引自定义组件封装详细教程及代码。【建议收藏】 在做微信小程序有涉及表单提交&#xff0c;涉及用户信息收集时。提交代码会审核不过。 有需要了解到文档&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/framework/user-pr…

超分辨率遥感图像去云的扩散增强训练

GitHub - littlebeen/Cloud-removal-model-collection: A collection of the existing end-to-end cloud removal model readme 云恢复的扩散增强 基于ADM的超分辨率遥感图像去云扩散增强算法。 几种传统的CR模型可以参考https://github.com/littlebeen/Cloud-removal-model-co…

短链接推荐:一个可以监测用户行为的“营销神器”

客户对我的推广有兴趣吗&#xff1f;他喜欢我的产品吗&#xff1f;他打开了我的营销信息吗&#xff1f;这三个问题相信每一位推广者都遇到过。接下来&#xff0c;就将给大家介绍一位大聪明——它能帮你监测每一位用户的行为&#xff0c;让你分分秒秒掌握用户的心理&#xff01;…

深入了解Redis内存淘汰策略中的LRU算法应用

LRU算法简析 LRU&#xff08;Least Recently Used&#xff0c;最近最少使用&#xff09;算法是一种常见的内存淘汰策略&#xff0c;它根据数据的访问时间来决定哪些数据会被淘汰。LRU算法的核心思想是&#xff1a;最久未被访问的数据&#xff0c;被认为是最不常用的数据&#…

UE5 GAS开发P41-43 永久效果,去除永久效果,伤害区域,EnumClass,开始重叠与结束重叠事件

这一部分学习了怎么创建一个伤害性的地形(火焰地形,毒沼泽等都可以用这个方式创建) AuraEffectActor.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "GameplayEffect.h&q…

Linux驱动开发:掌握SPI通信机制

目录标题 1、SPI简介2、SPI通信机制3、Linux内核中的SPI支持4、SPI核心API5、SPI控制器驱动6、SPI设备驱动 7、编写SPI设备驱动8、调试SPI驱动 在Linux驱动开发中&#xff0c;串行外设接口(SPI)是一种常见的高速全双工通信协议&#xff0c;用于连接处理器和各种外设。本文将深入…

React【Day4下+5】

环境搭建 使用CRA创建项目&#xff0c;并安装必要依赖&#xff0c;包括下列基础包 Redux状态管理 - reduxjs/toolkit 、 react-redux路由 - react-router-dom时间处理 - dayjsclass类名处理 - classnames移动端组件库 - antd-mobile请求插件 - axios pnpm i reduxjs/toolkit r…

企业规模扩大,SD-WAN实现跨省快速组网

随着数字化时代的飞速发展&#xff0c;企业面临着前所未有的挑战与机遇。5G、VoIP、AI和物联网等新技术的兴起&#xff0c;不仅改变了商业格局&#xff0c;也对企业网络提出了更高的要求。随着企业规模的不断扩大&#xff0c;企业如何搭建跨省的、高性能、超融合、简化运维的组…

防火墙技术基础篇:认识安全策略、安全区域、域间转发及报文转发流程

防火墙技术基础篇&#xff1a;认识安全策略、安全区域、域间转发及报文转发流程 一、安全策略匹配机制 简单通俗的讲&#xff0c;防火墙设备最基本的用途就是定义数据如何转发&#xff0c;靠什么定义呢&#xff1f;最基本的就是安全策略&#xff0c;当流量来到防火墙之后首先…

LeetCode_链表的回文结构

✨✨所属专栏&#xff1a;LeetCode刷题专栏✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 题目描述&#xff1a; 对于一个链表&#xff0c;请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法&#xff0c;判断其是否为回文结构。给定一个链表的头指针A&#xff0c;请返回一个bo…

Edge下载文件提示无法安全下载的解决方法

问题描述&#xff1a;最近Edge在下载文件时总是提示&#xff1a;无法安全下载&#xff0c;本文记录一下解决方法。 提示截图&#xff1a; 解决方式一&#xff1a; 1. 点击下图红框的三个点&#xff0c;选择保留 2. 选择仍然保留 解决方式二&#xff1a; 第一种方式每下载一次…

微信小程序中,plugins 配置项如何配置多个插件

在微信小程序中&#xff0c;如果需要配置多个插件&#xff0c;你可以在 app.json 文件的 plugins 配置项中为每个插件指定一个唯一的自定义名称&#xff0c;并分别提供它们的 version 和 provider 信息。下面是一个配置多个插件的示例&#xff1a; json复制代码 { "pages…

Python 0基础_变现_38岁_day 16(文件操作)

在python&#xff0c;使用内置函数open()进行文件的一些读写操作 文件操作格式&#xff1a;open(文件路径&#xff0c;访问模式&#xff0c;字符编码) 前面两个参数是必备参数&#xff0c;后面的字符编码为选填&#xff0c;但是大多数情况下都会协商字符编码 访问模式 r 只读 w…

如何看待AIGC技术

目录 1.概述 2.技术应用 2.1.媒体与内容创作 2.2.教育与学习 ​​​​​​​2.3.艺术创作 ​​​​​​​2.4.游戏产业 ​​​​​​​2.5.工业设计 ​​​​​​​2.6.对未来社会的影响 2.7.可能的发展方向 ​​​​​​​2.8.小结 3.伦理与风险 3.1.AIGC技术面临…

科研工作学习中常用的录制动图软件——screenToGif

一、前言 俗话说&#xff0c;字不如表&#xff0c;表不如图&#xff0c;静图不如动图。 动图给人的直观感受&#xff0c;还是很不错的。在曾经的学生期间&#xff0c;进行组会汇报&#xff1b;还是如今工作中&#xff0c;给领导汇报。我经常使用screenToGif这款软件&#xff…

基于STM32F4系列的ETH IAP在线升级程序

目录 1、前言 2、以太网的移植&#xff08;无操作系统&#xff09; 3、移植FATS 系统 4、移植ETH 驱动及 DP83848驱动 5、Tftp 服务程序 6、注意事项 ​7、代码 资料下载地址&#xff1a;基于STM32F4系列的ETH IAP在线升级程序 1、前言 此bootloader程序可以通过http…

数字科技助力垃圾分类展厅,增强内容交互新体验!

如今&#xff0c;许多行业都开始运用数字技术&#xff0c;探索其在展览展示领域中的应用&#xff0c;其中垃圾分类展厅作为现代城市文明建设的重要一环&#xff0c;也通过这些技术的运用&#xff0c;打造出了更加生动且富有科技感的展示空间&#xff0c;它不仅提升公众对垃圾分…

TCP详解

2.1TCP 由IETF的RFC793定义的传输控制协议&#xff08;Transmission Control Protocol&#xff0c;TCP&#xff09;是一种基于字节流的传输层通信协议。在传输数据前需要在发送与接收者之间建立连接&#xff0c;通过相应机制保证其建立连接的可靠性。 TCP协议具备以下特性&am…