文章目录
md5加密方式
一种被广泛使用的单向哈希算法不可逆,可以产生出一个128位(16字节的散列值
crypto
import { createHash } from 'crypto';
//十六进制的字符串形式
static hexDigits: string[] = [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
];
//md5加密方式
static encodeByMD5(originString: string): string {
if (originString) {
try {
//创建实例,指定md5为哈希加密方式
const hash = createHash('md5');
//返回值为buffer类型的字节数组(第一个值为要加密的字符串,第二个规定为utf8形式)
const results = hash.update(originString, 'utf8').digest();
//调用方法将字节数组转为字符串
const resultString: string = this.byteArrayToHexString(results);
return resultString;
} catch (ex) {
console.error(ex);
}
}
return '';
}
//接收一个buffer类型的字节数组,返回值为string类型
static byteArrayToHexString(b: Buffer): string {
let resultSb = '';
//for循环遍历字节数组
for (let i = 0; i < b.length; i++) {
//调用方法进行转换
resultSb += this.byteToHexString(b[i]);
}
return resultSb;
}
static byteToHexString(b: number): string {
//先对数字进行了处理,如果它是一个负数,则将其调整为一个无符号的 8 位整数(因为 JavaScript 中的数字是以 IEEE 754 格式的浮点数来存储的,所以需要显式地转成 8 位整数)。然后,分别计算该数的个位和十位上的十六进制值,将其转换为对应的字符,拼接起来成为一个 2 位的十六进制字符串返回。
let n: number = b;
if (n < 0) {
n = 256 + n;
}
const d1 = n / 16; //个位十六进制值
const d2 = n % 16; //十位十六进制值
//转为整数类型
const d3 = parseInt(d1.toString());
const d4 = parseInt(d2.toString());
//两位十六进制字符串
return this.hexDigits[d3] + this.hexDigits[d4];
}
static md5(source: string): string {
const hash = createHash('md5');
hash.update(source, 'utf8');
return hash.digest('hex');//上面的实现可以通过传递一个hex直接实现
}
crypto-js
下载依赖:
npm install crypto-js
import * as CryptoJS from 'crypto-js';
static md5(source:string){
//md5加密
const wordArray = CryptoJS.enc.Utf8.parse('2022JueJin')
CryptoJS.MD5(wordArray).toString();
//base64
const base64 = CryptoJS.enc.Base64.stringify(CryptoJS.MD5('2022JueJin'))
const base64two = Buffer.from(source).toString('base64'),
//第一种加密出来是只有base64加密
//第二种多加了一层url加密
}
//url加密实现原理
static urlEncode(str: string): string {
let encodedStr = encodeURIComponent(str);
encodedStr = encodedStr.replace(/[-_.!~*'()]/g, (char) => {
const hexCode = char.charCodeAt(0).toString(16).toUpperCase();
return `%${hexCode}`;
});
return encodedStr;
}
tips:哈希算法:(md5的底层原理)
这里只做简单的介绍,有兴趣可以深入了解.
哈希法又称为:散列法,杂凑法,关键字地址计算法,相对应的表称为哈希表,散列表或杂凑表.
基本思想:首先在元素的关键字k和元素的存储位置p之间简历一个对应关系H,使得p=H(k),H称为焊锡函数,创建哈希表时,把关键字为k的元素直接存入地址为H(k)的单元.查找时候利用哈希函数计算出位置.
哈希函数构造方法
数字分析法,平方取中法,分段叠加法,除留余数法,伪随机数法
解决哈希冲突的方法:
开放定址法,再哈希法,链地址法,建立公共溢出区
举个简单的例子:(简单通俗的理解一下哈希存储和查找元素)
已知一组关键字序列(19,14,23 ,01 ,68 ,20,84 ,27,55,11,10,79),给出按哈希函数H(key)=key%13和线性探测处理冲突构造所得哈希表ht[0…15]。
如图所示。其中,每个关键字下面带圈的数字,是放置该关键字时所进行的地址计算次数,或者说是放置该关键字时所进行的关键字比较次数(同时也是查找该关键字时所进行的关键字比较次数)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fgbeNhEZ-1681466387252)(C:\Users\57429\AppData\Roaming\Typora\typora-user-images\1681380409994.png)]
查找19时,通过计算H(19)= 6,ht[6]. key非空且值为19查找成功则查我关键字19,仅需要计算一次地址就可以找到。 在找14时,通过计算H(14)=1,ht[1],key非空且值为14查找成功,查找 23时,通过计算H(23)= 10,ht[ 10]. key非空且值为23查找成功,则查找关键字23.仅需要计算一次地址就可以找到。同样,查找关键字68 ,20,11 ,均需要计算一次地址就可以找到。
查找关键字01时,通过计算H(01)= 1,ht[1].key非空且值为14≠01,则找第一次冲突处理后的地址h=(1+1)% 16=2,此时,ht[2]. key非空且值为01,查找成功,因此查找关键字01时,需要计算二次地址才可以找到。
查找关键字55时,通过计算H(55)=3,h[3. key非空且值为68≠55,则找第一次冲突处 h=(3+1)%16=4.此时,ht[4].key非空且值为27≠55,则查找两次冲突后处理地址为h2=(3+2)%16=5,ht[5].key非空且值为55,查找成功,因此查找55,需要计算三次.
AES加密
AES为高级加密标准(Advanced Encryption Standard,AES),是一种 对称加密算法 ,根据加密算法不同,密钥的长度和IV的长度不同,aes-128-cbc,那么cipher文件中的key和iv必须为16字节,aes-192-cbc key和iv必须为24字节,aes-256-cbc key和iv必须为32字节。
import * as CryptoJS from 'crypto-js';
const message = CryptoJS.enc.Utf8.parse('JueJin2022')
const secretPassphrase = CryptoJS.enc.Utf8.parse('0123456789asdfgh')
const iv = CryptoJS.enc.Utf8.parse('0123456789asdfgh')
const encrypted = CryptoJS.AES.encrypt(message, secretPassphrase, {
mode: CryptoJS.mode.CBC,
paddding: CryptoJS.pad.Pkcs7,
iv
}).toString()
console.log(encrypted)
使用aes-128-cbc加密和解密字符串123456,密钥为:nnnnnnnnnnnnnnnn
let a2 = require("crypto");
//aes128加密
function aes128encrypt(data, key){
let t1 = a2.createCipheriv('aes-128-cbc',key, "nnnnnnnnnnnnnnnn");
let crypted = t1.update(data,'utf-8','hex');
crypted += t1.final('hex');
return crypted;
}
//aes128解密
function unaes256encrypt(crypted,key){
let t2 = a2.createDecipheriv('aes-128-cbc', key, "nnnnnnnnnnnnnnnn");
let decrypted = t2.update(crypted, 'hex', 'utf8');
decrypted += t2.final('utf8');
return decrypted;
}
输出结果为123456
console.log(unaes256encrypt(aes256encrypt("123456","nnnnnnnnnnnnnnnn"),"nnnnnnnnnnnnnnnn"));
CryptoJS.AES.encrypt() 可以传入 3 个参数: 第 1 个为需要加密的明文; 第 2 个是秘钥,长度可以是 128、192 或 256 bit; 第 3 个为一个配置对象,可以添加一些配置。常见的配置属性有:
- mode:加密模式。默认为 CBC,还支持且常用的是 ECB。 CBC 模式需要偏移向量 iv,而 ECB 不需要。
- paddding:填充方式。默认为 Pkcs7;
- iv:偏移向量 ;
注意,明文、秘钥和偏移向量一般先用诸如 CryptoJS.enc.Utf8.parse() 转成 WordArray 对象再传入,这样做得到结果与不转换直接传入是不一样的。
RSA加密
RSA 是一种 非对称加密算法 ,使用公钥(公开密钥PK)加密、私钥(秘密密钥SK)解密;
import * as crypto from 'crypto';
export class RSA {
private _privateKey: string;
private _publicKey: string;
private keySize: number;
constructor(keySize: number) {
//RSA允许你选择公钥的大小。512位的密钥被视为不安全的;768位的密钥不用担心受到除了国家安全管理(NSA)外 的其他事物的危害
this.keySize = keySize || 1024;
//从 node.js 的 v10.12.0 开始,可以使用内部模块 crypto.generateKeyPairSync 方法生成公私钥
const keyPair = crypto.generateKeyPairSync('rsa', {
modulusLength: this.keySize,
//配置加密参数(填充方式,加密模式等)
publicKeyEncoding: {
type: 'spki',
format: 'pem',
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
},
});
this._privateKey = keyPair.privateKey;
this._publicKey = keyPair.publicKey;
}
// 设置 privateKey 的 setter 方法
set privateKey(privateKey: string) {
this._privateKey = privateKey;
}
// 设置 publicKey 的 setter 方法
set publicKey(publicKey: string) {
this._publicKey = publicKey;
}
// 获取 privateKey 的 getter 方法
get getPrivateKey(): string {
return this._privateKey;
}
// 获取 publicKey 的 getter 方法
get getPublicKey(): string {
return this._publicKey;
}
// 加密
encrypt(data): string {
const buffer = Buffer.from(data, 'utf-8');
const encryptedBuffer = crypto.publicEncrypt(this._publicKey, buffer);
return encryptedBuffer.toString('base64');
}
// 解密
decrypt(data): string {
const buffer = Buffer.from(data, 'base64');
const decryptedBuffer = crypto.privateDecrypt(this._privateKey, buffer);
return decryptedBuffer.toString('utf-8');
}
}
该示例中,定义了一个 RSA 类,通过 crypto 模块来实现 RSA 加密算法。构造函数中,设置了加密密钥的长度,同时使用 crypto.generateKeyPairSync() 方法来生成公钥和私钥。其中,modulusLength 参数表示密钥的长度,publicKeyEncoding 和 privateKeyEncoding 分别表示公钥和私钥的格式。
在 encrypt 方法中,将输入的字符串转换为 Buffer 对象,并使用 crypto.publicEncrypt() 方法进行加密,传入公钥进行加密。最后将加密后的结果转换为指定编码方式的字符串并返回。
在 decrypt 方法中,先将输入的字符串转换为 Buffer 对象,然后使用 crypto.privateDecrypt() 方法进行解密,传入私钥进行解密。最后将解密后的结果转换为指定编码方式的字符串并返回。
需要注意的是,加密和解密使用的是不同的公钥和私钥。在使用时,需要新建一个 RSA 实例,并将其公钥用于加密,私钥用于解密。同时也需要注意密钥的保密性,确保密钥不会泄露。
//使用方法
async Rsa(req) {
const rsa = new RSA(1024);
console.log(rsa, 'rsa1');
const pub = rsa.getPublicKey;
const pri = rsa.getPrivateKey;
console.log(pub, 'pub');
console.log(pri, 'pri');
rsa.privateKey = 'pri';
rsa.publicKey = 'pub';
console.log(rsa, 'rsa2');
//加密
const b = rsa.encrypt('1234');
//解密
const a = rsa.decrypt(b);
return { a, b };
}
//以上封装的类RSA基本上就是node-rsa的实现原理,传参方面略有不同,更深层次可以查看源码.
node-rsa
npm install node-rsa
import * as RSA from 'node-rsa';
import * as crypto from 'crypto';
//如果不指定导出格式,公钥默认是pkcs8,私钥是pkcs1
const key = new NodeRSA({b: 512});//生成512位秘钥
let pubkey = key.exportKey('pkcs8-public');//导出公钥
let prikey = key.exportKey('pkcs8-private');//导出私钥
//生成两个实例
let pubKey = new NodeRSA(pubKey,'pkcs8-public');//导入公钥
let priKey = new NodeRSA(priKey,'pkcs8-private');//导入私钥
// --- RSA ---
// rsa加密
static rsaEncode(code: string) {
const privateRSA = new RSA(Encrypt.RSA.PRIVATE_KEY);
return privateRSA.encrypt(code, 'base64');
}
// rsa解密
static rsaDecode(code: string) {
const privateRSA = new RSA(Encrypt.RSA.PRIVATE_KEY);
return privateRSA.decrypt(code, 'utf8');
}
看到了一个很好的C/S的设计思路:
- 服务端计算出一对秘钥pub/pri。将私钥保密,将公钥公开。
- 客户端请求服务端时,拿到服务端的公钥pub。
- 客户端通过AES计算出一个对称加密的秘钥X。 然后使用pub将X进行加密。
- 客户端将加密后的密文发送给服务端。服务端通过pri解密获得X。
- 然后两边的通讯内容就通过对称密钥X以对称加密算法来加解密。
其他加密方式
字符串SHA256加密
SHA256是安全散列算法(Secure Hash Algorithm),对于任意长度的消息,SHA256都会产生一个256bit长的哈希值,长度为32个字节,通常用一个长度为64的十六进制字符串来表示
let a1 = require("crypto-js");
let a2 = require("crypto");
let res3 = a1.SHA256("123456").toString();
let res4 = a2.createHash("sha256").update("123456").digest('hex');
//使用res3或res4都可以,加密结果一样
console.log(res3);
console.log(res4);
字符串HMac加密
let a1 = require("crypto-js");
let a2 = require("crypto");
let res5 = a1.HmacMD5("123456", "apple").toString();
let res6 = a2.createHmac("md5","apple").update("123456").digest('hex');
//使用res5或res6都可以,加密结果一样
console.log(res5);
console.log(res6);