> 文章列表 > 常用的加密方式(md5,base64,url,AES对称加密,RSA非对称加密)

常用的加密方式(md5,base64,url,AES对称加密,RSA非对称加密)

常用的加密方式(md5,base64,url,AES对称加密,RSA非对称加密)

文章目录

    • md5加密方式
          • crypto
          • crypto-js
      • tips:哈希算法:(md5的底层原理)
        • 哈希函数构造方法
        • 解决哈希冲突的方法:
        • 举个简单的例子:(简单通俗的理解一下哈希存储和查找元素)
    • AES加密
    • RSA加密
    • 其他加密方式

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();//base64const 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的设计思路:

  1. 服务端计算出一对秘钥pub/pri。将私钥保密,将公钥公开。
  2. 客户端请求服务端时,拿到服务端的公钥pub。
  3. 客户端通过AES计算出一个对称加密的秘钥X。 然后使用pub将X进行加密。
  4. 客户端将加密后的密文发送给服务端。服务端通过pri解密获得X。
  5. 然后两边的通讯内容就通过对称密钥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);