当前位置: 首页 > news >正文

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各种常用算法的实现

相关文章:

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 CryptoJ…...

【分布式】万字图文解析——深入七大分布式事务解决方案

分布式事务 分布式事务是指跨多个独立服务或系统的事务管理&#xff0c;以确保这些服务中的数据变更要么全部成功&#xff0c;要么全部回滚&#xff0c;从而保证数据的一致性。在微服务架构和分布式系统中&#xff0c;由于业务逻辑往往会跨多个服务&#xff0c;传统的单体事务…...

apache2配置多站点

环境 ubuntu 14.04 apache2 Server version: Apache/2.4.7 (Ubuntu) Server built: Apr 3 2019 18:04:25 步骤 修改/etc/apache2/sites-enabled/000-default.conf 增加VirtualHost段&#xff0c;指定不同的ServerName、DocumentRoot等参数 <VirtualHost *:80>…...

基于PyQt Python的深度学习图像处理界面开发(一)

Python标准库更多的适合处理后台任务&#xff0c;唯一的图形库tkinter使用起来很不方便&#xff0c;所以后来出现了针对Python图形界面开发的扩展库&#xff0c;例如PyQt。 在介绍PyQt之前&#xff0c;必须先简单介绍一下Qt。Qt是一个C可视化开发平台&#xff0c;是一个跨平台的…...

【Linux网络】Linux网络编程套接字,UDP与TCP

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;Linux “ 登神长阶 ” &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀Linux网络编程套接字 &#x1f4d2;1. 端口号&#x1f4dc;2. 初识TCP协议与UDP协议&#x1…...

Vue3 -- 强制统一包管理器工具【企业级项目配置保姆级教程6】

引言: 团队开发项目的时候,需要统一包管理器工具,因为不同包管理器工具下载同一个依赖,可能版本不一样,导致项目出现bug问题,因此包管理器工具需要统一管理!!所以就需要我们强制统一包管理器工具。 创建scripts目录和preinstall.js文件: 在根目录创建scritps/preinstal…...

Winform实现自制浏览器JavaScript注入

让我们一起走向未来 &#x1f393;作者简介&#xff1a;全栈领域优质创作者 &#x1f310;个人主页&#xff1a;百锦再新空间代码工作室 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[1504566…...

【工具插件类教学】在 Unity 中使用 iTextSharp 实现 PDF 文件生成与导出

目录 一、准备工作 1. 安装 iTextSharp 2. 准备资源文件 二、创建 ExportPDFTool 脚本 1、初始化 PDF 文件,设置字体 2、添加标题、内容、表格和图片 三、使用工具类生成 PDF 四、源码地址 在 Unity 项目中,我们有时会需要生成带有文本、表格和图片的 PDF 文件,以便…...

javascript用来干嘛的?赋予网站灵魂的语言

javascript用来干嘛的&#xff1f;赋予网站灵魂的语言 在互联网世界中&#xff0c;你所浏览的每一个网页&#xff0c;背后都有一群默默工作的代码在支撑着。而其中&#xff0c;JavaScript就像是一位技艺精湛的魔术师&#xff0c;它赋予了网页生命力&#xff0c;让原本静态的页…...

Flutter Getx状态管理

在 Flutter 开发中&#xff0c;状态管理是一个非常重要的话题。随着应用变得更加复杂&#xff0c;状态管理的方式也变得越来越多。Flutter 提供了多种状态管理的解决方案&#xff0c;如 Provider、Riverpod、BLoC 等&#xff0c;而在这些选项中&#xff0c;GetX 作为一个轻量级…...

《成法》读书笔记

稻盛和夫的《成法》是一部关于个人和企业成功哲学的作品&#xff0c;结合了他在经营京瓷和KDDI&#xff0c;以及重建日航&#xff08;JAL&#xff09;过程中的经验和智慧。 以下是这本书的读书笔记&#xff0c;涵盖其核心思想和重要概念&#xff1a; 1. 以“心”为本 内容概…...

TensorFlow 2.0 环境配置

官方文档&#xff1a;CUDA Installation Guide for Windows 官方文档有坑&#xff0c;windows的安装指南直接复制了linux的指南内容&#xff1a;忽略这些离谱的信息即可。 可以从官方文档知悉&#xff0c;cuda依赖特定版本的C编译器。但是我懒得为了一个编译器就下载整个visua…...

Ekman理论回归

Scientific reportsEkman revisited: Surface currents to the left of the winds in the Northern HemisphereVagn Walfrid Ekman1905年的理论描述了地球旋转受到风的作用&#xff0c;摩擦边界层中的流场&#xff0c;北半球总是在海表风的右侧&#xff0c;南半球总是在海表风的…...

算法演练----24点游戏

给定4个整数&#xff0c;数字范围在1~13之间任意使用-*/&#xff08;&#xff09;&#xff0c;构造出一个表达式&#xff0c;使得最终结果为24&#xff0c; 方法一 算法分析&#xff1a;加括号和取出重复表达式 # 导入精确除法模块&#xff0c;使得在Python2中除法运算的行为更…...

【学习心得】Python好库推荐——tiktoken

一、tiktoken是什么&#xff1f; tiktoken是一个快速BPE分词器&#xff0c;是由 OpenAI 开发的一个用于文本处理的 Python 库&#xff0c;主要用于将文本编码为数字序列&#xff08;称为 "tokens"&#xff09;&#xff0c;或将数字序列解码为文本。这一过程被称为 &q…...

MacBook不额外安装软件,怎样投屏到安卓手机上?

提起iPhone或MacBook的投屏&#xff0c;人们总会想到airplay功能。但离开了苹果生态&#xff0c;其他品牌的手机电脑就未必配备airplay功能了。 如果想要将MacBook的电脑屏幕共享到安卓手机或平板上&#xff0c;到底要怎样做&#xff1f;需要安装什么软件吗&#xff1f; 不需要…...

flink sql + kafka + mysql 如何构建实时数仓

构建一个基于 Flink SQL、Kafka 和 MySQL 的实时数据仓库(Data Warehouse)架构,可以通过流处理的方式实现高效、实时的数据集成与分析。以下是如何利用这三者构建实时数仓的步骤与实现: 架构概述 Kafka:作为流数据平台,负责接收和传输来自不同源系统(如应用日志、传感器…...

Go语言开发基于SQLite数据库实现用户表查询详情接口(三)

背景 上一章 Go语言开发基于SQLite数据库实现用户表新增接口(二) 这一章我们实现用户表的查询详情接口 代码实现 mapper层 type UserMapper interface {GetById(id uint64) (*model.User, error)}type userMapper struct { }func (m *userMapper) GetById(id uint64) (*mod…...

(11)(2.1.7) FETtec OneWire ESCs(二)

文章目录 前言 3 组态 4 可选功能 5 SITL模拟 6 故障排除 前言 &#xff01;Note 此功能在固件版本4.1.1及更高版本上可用。 3 组态 FTW掩码 SERVO_FTW_MASK 参数选择将哪些伺服输出&#xff08;如果有的话&#xff09;路由到 FETtec ESC。更改此参数后需要重新启动。…...

Springboot maven常见依赖、配置文件笔记

pom.xml文件 一、<parent> 在Maven项目中&#xff0c;pom.xml 文件中的 <parent> 元素用于定义父项目的坐标。使用 <parent> 可以实现继承机制&#xff0c;子项目可以从父项目继承配置信息&#xff0c;比如依赖管理、插件配置等。这样可以避免在多个子项目…...

Ceph后端两种存储引擎介绍

Ceph是一个可靠的、自治的、可扩展的分布式存储系统&#xff0c;它支持文件系统存储、块存储、对象存储三种不同类型的存储&#xff0c;以满足多样存储的需求。在Ceph的存储架构中&#xff0c;FileStore和BlueStore是两种重要的后端存储引擎&#xff0c;下面将分别进行详细介绍…...

C++入门基础知识140—【关于C++ 类构造函数 析构函数】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 类构造函数 & 析构函数的相关内容…...

基于标签相关性的多标签学习

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

macOS系统下使用SQLark连接达梦数据库

目录 下载 SQLark 客户端 安装 SQLark 客户端 SQLark用户界面 使用SQLark连接达梦数据库 1. 选择达梦数据库 2. 输入连接信息 3. 测试连接 4. 创建连接 5. 验证连接成功 作为一名数据库开发者&#xff0c;在macOS系统上找到一款支持达梦数据库的数据库开发和管理工具&…...

爬虫如何解决短效代理被封的问题?

在数据采集的征途上&#xff0c;短效代理如同一把双刃剑&#xff0c;它既能为我们带来速度和效率&#xff0c;也可能因为频繁更换IP地址而遭遇被封禁的风险。那么&#xff0c;作为数据采集er的我们&#xff0c;该如何巧妙应对&#xff0c;确保爬虫的稳定运行呢&#xff1f;今天…...

Jmeter基础篇(22)服务器性能监测工具Nmon的使用

一、前言 我们在日常做压测的过程中&#xff0c;不仅仅需要监控TPS&#xff0c;响应时间&#xff0c;报错率等这些系统基础性能数据&#xff0c;还需要对服务器的性能&#xff08;如CPU、磁盘、内存、网络IO等&#xff09;做监控&#xff0c;以求对系统运行过程中的硬件性能有…...

Spring Boot 集成 RabbitMQ:消息生产与消费详解

在现代微服务架构中&#xff0c;消息队列扮演着至关重要的角色&#xff0c;RabbitMQ 是其中一种广泛使用的消息中间件。本文将详细介绍如何在 Spring Boot 项目中集成 RabbitMQ&#xff0c;实现消息的生产和消费&#xff0c;并提供相应的代码示例。 一、环境准备与依赖配置 首…...

JSON-RPC-CXX深度解析:C++中的远程调用利器

一、引言 1. JSON-RPC 简介及其在 C 开发中的重要性 JSON-RPC&#xff08;JavaScript Object Notation - Remote Procedure Call&#xff09;是一种基于 JSON 格式的远程过程调用协议。在 C 开发中&#xff0c;它扮演着至关重要的角色。随着软件系统的日益复杂和分布式架构的…...

【后端速成Vue】模拟实现翻译功能

前言&#xff1a; 本期将会介绍 Vue 中的 watch 侦听器&#xff0c;它语法是怎么样的呢&#xff1f;具有怎样的功能呢&#xff1f;最后用模拟实现百度翻译来更进一步练习 watch 侦听器 篮球哥找工作专属IT岗位内部推荐&#xff1a; 专属内推链接&#xff1a;内推通道 1、认识翻…...

人脸识别技术:从算法到深度学习的全面解析

一、人脸识别技术综述 人脸识别技术作为一种重要的生物识别技术&#xff0c;在当今社会中具有举足轻重的地位。它广泛应用于各个领域&#xff0c;如金融领域的实名认证、安保领域的门禁系统、通行领域的火车站和地铁站检票、泛娱乐领域的美颜相机和短视频特效处理、公安和司法…...