怎么改网站上的logo,公司申请网站建设,网站建设设计流程,赣州市建设局网站目录
一、密码学介绍 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…目录
一、密码学介绍 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 版的标准算法会应用于网页、H5 的 app、小程序中一般使用第三方库或者自己实现。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个步骤计算得到
确定绝对值的二进制表示(如对于-18先确定18的二进制表示)找到数值的一补数(或反码)换句话说就是每个0都变成1每个1都变成0给结果加1。
按位非操作符用 波浪符(~) 表示它的作用是返回数值的 一补数(反码)。按位非是 ECMAScript 中为数不多的几个二进制数学操作符之一。看下面的例子
let num1 25; //二进制 00000000000000000000000000011001 8421码计算或者直接除以2取余数
let num2 ~num1; //二进制 11111111111111111111111111100110(补码)11111111111111111111111111100110
-1 1111111111111111111111111110010110000000000000000000000000011010
符号位 2^42^32^1 1682 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^42^32^12^0 16821 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^42^32^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^42^32^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^42^32^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^22^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 编码细节
每个 Base64 字符代表原数据中的 6bitBase64 编码后的字符数是 4 的倍数编码的字节数是 3 的倍数时不需要填充
Base64 编码的特点
用 A-Z、a-z、0-9、/ 64个字符表示 作为填充使用Base64 编码是编码不是压缩编码后只会增加字节数标准的 Base64 每行为 76 个字符行末添加换行符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是一类密码学哈希函数用于产生数据的摘要通常是固定长度的二进制串。消息摘要算法接受任意长度的消息作为输入并输出固定长度的摘要。消息摘要算法具有以下特点
散列后的密文不可逆散列后的结果唯一。一般用于校验数据完整性、签名sign由于密文不可逆所以服务端也无法解密想要验证就需要跟前端一样的方式去重新签名一遍签名算法一般会把源数据和签名后的值一起提交到服务端要保证在签名时候的数据和提交上去的源数据一致哈希碰撞
常见的哈希算法包括但不限于
MD5Message Digest Algorithm 5 MD5 是一种广泛使用的哈希函数生成 128 位16 字节的哈希值。然而由于其存在安全性漏洞已不推荐用于加密目的而主要用于校验数据完整性等非加密场景。SHA-1Secure Hash Algorithm 1 SHA-1 生成 160 位20 字节的哈希值被广泛应用于数字签名、证书签名等场景。但是SHA-1 也已经被证明存在碰撞攻击的安全性问题因此也不再推荐用于安全目的。SHA-256、SHA-384、SHA-512 这些是安全哈希算法家族中的一部分分别生成 256 位、384 位和 512 位长度的哈希值。它们是目前广泛应用于数据完整性验证、数字签名等安全领域的哈希算法。RIPEMDRACE Integrity Primitives Evaluation Message DigestRIPEMD 系列是一组哈希函数分为 RIPEMD-128、RIPEMD-160、RIPEMD-256 和 RIPEMD-320分别生成不同长度的哈希值。它们在一些特定的应用场景中有一定的使用。BLAKE2BLAKE2 是一种高速、安全的哈希函数能够生成不同长度的哈希值。它在性能方面优于许多其他哈希算法并且在一些应用场景中取得了广泛的应用。WhirlpoolWhirlpool 是一种比较少见但仍在一些场景中使用的哈希函数生成 512 位长度的哈希值被认为具有较高的安全性。SHA-3Secure Hash Algorithm 3SHA-3 是美国国家标准与技术研究所NIST发布的一种哈希算法标准与 SHA-2 不同SHA-3 使用了基于 Keccak 构造的算法。SHA-3 提供了多种摘要长度的选择包括 224 位、256 位、384 位和 512 位。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、Base64CryptoJS 库默认输出 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、Base64CryptoJS 库默认输出 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-1SM3 的密文长度与 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 介绍
对称加密算法是一种加密技术使用 相同的密钥 对数据进行加密和解密。这意味着发送方和接收方必须共享相同的密钥用于加密和解密数据。对称加密算法具有加密速度快、计算效率高的特点适合对大量数据进行加密。一些常见的对称加密算法包括
DESData Encryption Standard DES 是一种早期的对称加密算法使用 56 位密钥对数据进行加密和解密。尽管 DES 在安全性上存在一些弱点但它为后续的加密算法奠定了基础。在实际使用中DES 密钥通常由 64 位长度的密钥中的第 8 位用作奇偶校验位因此实际上只有 56 位是用于加密和解密的密钥位数3DESTriple DES 3DES 是 DES 的增强版本它多次对数据进行 DES 加密通常使用两个或三个密钥。虽然 3DES 提供了更高的安全性但由于其计算复杂度较高已经逐渐被更先进的加密算法所取代。在使用 3DES 进行加密时通常会使用两个密钥K1 和 K2或三个密钥K1、K2 和 K3进行三次 DES 加密。如果使用两个密钥则每个密钥长度为 56 位总长度为 112 位。如果使用三个密钥则每个密钥长度为 56 位总长度为 168 位AESAdvanced Encryption Standard AES 是一种广泛使用的对称加密算法设计用于取代 DES。它支持不同的密钥长度包括 128 位、192 位和 256 位具有较高的安全性和较快的加密速度因此被广泛应用于各种安全领域。(AESAdvanced Encryption Standard算法的密钥长度要求AES-128128 位16 字节、AES-192192 位24 字节、AES-256256 位32 字节)BlowfishBlowfish 是一种对称加密算法支持变长密钥32 至 448 位并且具有高速和高度可配置性的特点。尽管 Blowfish 在许多场景下仍然被使用但它已经逐渐被更先进的算法所取代。RC4Rivest Cipher 4RC4 是一种流密码Stream Cipher算法具有简单、高效的特点。尽管 RC4 曾被广泛应用于 SSL/TLS、WEP 等协议中但由于其存在一些安全性问题如密钥漏洞和偏置攻击已经逐渐被淘汰。SM4 算法 也称为国密算法是由中国密码学家提出的一种分组加密算法被采纳为中国商用密码算法标准。它是一种对称加密算法用于对数据进行加密和解密。SM4 算法的主要特点包括①分组大小 SM4 使用 128 位16 字节的分组大小进行加密和解密操作。②密钥长度 SM4 算法支持密钥长度为 128 位16 字节。③轮数 SM4 算法采用了 32 轮的 Feistel 结构进行加密每轮包括逐位的非线性变换和线性变换。④S 盒 SM4 使用了一个固定的 8x8 的 S 盒用于非线性变换增强了算法的安全性。⑤密钥扩展 SM4 算法对输入的密钥进行扩展生成多轮加密过程中所需的轮密钥。⑥安全性 SM4 算法经过了严格的密码学分析和安全性评估被认为具有较高的安全性和抗攻击能力。SM4 的代码实现细节在专栏安卓逆向中做介绍。
6.2 加密模式和填充方式
在对称加密算法中加密模式和填充模式是两个重要的概念用于指定如何对数据进行加密和解密。
加密模式Encryption Mode 加密模式定义了在加密过程中如何处理数据块、处理块之间的关系以及如何处理最后一个块的方法。常见的加密模式包括 ECBElectronic Codebook模式将明文分成固定大小的块并独立地对每个块进行加密。 CBCCipher Block Chaining模式每个明文块先与前一个密文块进行异或运算然后再进行加密。 CFBCipher Feedback模式将前一个密文块作为加密器的输入产生密文块。 OFBOutput Feedback模式将前一个密文块作为加密器的输入产生密钥流再与明文进行异或运算得到密文。 CTRCounter模式使用一个计数器和密钥生成伪随机密钥流再与明文进行异或运算得到密文。 填充模式Padding Mode 填充模式用于在加密前将不满足块大小要求的数据块填充到合适的长度以便进行加密。常见的填充模式包括 PKCS#5 和 PKCS#7使用一定规则填充数据块通常采用的填充值是缺少的字节数。 Zero Padding填充的字节全部为零。 ANSI X.923除了最后一个字节外填充的字节为零最后一个字节表示填充的字节数。 ISO 10126填充的字节为随机值最后一个字节表示填充的字节数。 6.3 CryptoJS 中DES、DESede、AES算法实现
DES DES 密钥长度为 64bit实际使用长度是 56bit前面已经提到过分组长度为 64bitCryptoJS 中 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 对称加密算法注意事项 要复现一个对称加密算法需要得到明文、key、iv、mode、padding 明文、key、iv 需要注意解析方式而且不一定是字符串形式 如果明文中有两个分组的内容相同ECB 会得到完全一样的密文CBC 不会 加密算法的结果通常与明文等长或者更长如果变短了那可能是 gzip、protobuf 密文/明文的自定义输出/输入(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));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/DJKaXDwetsCnSUaz65LCFHU09OSEYee5oC iI0ql21EA306c91oT/fQpPngQGZHLUtDOUdJVlAKnicCvmR24NqyNKFuY8L0cnB1 zcax73RfCtf/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 T05IRh57mgKIjSqXbUQDfTpz3WhP99CkeBAZkctS0M5R0lWUAqeJwKZHbg2rI0\n oW5jwvRycHXNxrHvdF/4K1/XEA7AgMBAAECgYEAsGkDrYWps0bW7zKb1o4Qkojb\n etZ2HNJojlsHObaJOHbPGs7JXU4bmmdTz5LfSIacAoJCciMuTqCLrPEhfmkghPq\n U2MjyjfqYdXALoP7l/vt6QmjY/g1IAsaZN9nFhyjJ2WzgOx1f7gZj4NBSvTdSj7H\n m5E24zkmp7Qw1z6/mkCQQD7WSXAXcv2v3Vo6qi1FUlkzQgCQLFYqXNSOSPpno3y\n oohUFIkMj0bYGbVE1LzV30Rb6Z8e8yQAByw6l8RuGb2PAkEA9bwb2euyOe6CcqpE\n PNFc7UlOJAy5epVFKHbu0aNivVpU0hsphqjIGXJGHYTspyEOLqtzILqKPZr6pru\n WvJUlQJBAJoImQUZtlyCGs7wN/G5mN/ocscGpGikdLk16hdHbqbdpaoexCyYYUf\n xCHpicw75mW5d2V9Ngu6WZWS2rNqnOsCQCoMK//X8sEy7KNOOyrk8DIpxtqs4eix\n dil3oKk3OdgIsubYuvxNuRRjCnU6uGWKGUX9TUudiUgda89/gb6xkCQFm8gD6n\n AyNPPPKRq2M84cAbnvjdIAY3OFHfkaoWCtEj5DR0UDuVv7jN7re2D7id/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 Xiang2024最新版JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现