不可逆加密
理解
加密过程不需要使用密钥,输入明文后直接经过加密算法处理成密文,加密后的数据无法被解密。
一般的使用场景是用户的密码存储,后台数据库存储前端返回的密文,每次匹对的也是密文。前端主要负责明文的加密。
加密算法的处理我们可以在原有基础上进行多次的加密或加盐或移位来增加安全性
常用的不可逆加密算法有MD5与SHA-1。
MD5安装及使用
可以从网上下载js-md5.js使用script引入
另外一种方法就是当你是模块化开发的时候使用包管理工具(npm、yarn)安装并引入。代码如下:
npm install js-md5
//js
// 对应处理es6及以上的js自行安装babel,详细方法这里不展示
import md5 from 'js-md5'
//支持utf-8
md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
// 它还支持字节`Array`, `Uint8Array`, `ArrayBuffer`
md5([]); // d41d8cd98f00b204e9800998ecf8427e
md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
// 不同的输出方法
md5(''); // d41d8cd98f00b204e9800998ecf8427e
md5.hex(''); // d41d8cd98f00b204e9800998ecf8427e
md5.array(''); // [212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126]
md5.digest(''); // [212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126]
md5.arrayBuffer(''); // ArrayBuffer
md5.buffer(''); // ArrayBuffer, 不建议, 这可能会与node.js中的Buffer混淆。请改用arrayBuffer。
md5.base64(''); // 1B2M2Y8AsgTpgAmY7PhCfg==
// 我们可以在原有的明文的基础上进行加盐或多次加密
let KEY = '&%*$8@!!%'
let pwd = 123456
md5(md5(password + KEY))
SHA-1安装及使用
可以从网上下载js-sha1.js使用script引入
另外一种方法就是当你是模块化开发的时候使用包管理工具(npm、yarn)安装并引入。代码如下:
npm install js-sha1
//js
// 对应处理es6及以上的js自行安装babel,详细方法这里不展示
import sha1 from 'js-sha1'
//支持utf-8
sha1('中文'); // 7be2d2d20c106eee0836c9bc2b939890a78e8fb3
// 它还支持字节`Array`, `Uint8Array`, `ArrayBuffer`
sha1([]); // da39a3ee5e6b4b0d3255bfef95601890afd80709
sha1(new Uint8Array([])); // da39a3ee5e6b4b0d3255bfef95601890afd80709
// 不同的输出方法
sha1(''); // da39a3ee5e6b4b0d3255bfef95601890afd80709
sha1.hex(''); // da39a3ee5e6b4b0d3255bfef95601890afd80709
sha1.array(''); // [218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
sha1.digest(''); // [218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]
sha1.arrayBuffer(''); // ArrayBuffer
// 我们可以在原有的明文的基础上进行加盐或多次加密
let KEY = '&%*$8@!!%'
let pwd = 123456
sha1(sha1(password + KEY))
MD5与SHA-1的区别
由于MD5 与SHA-1均是从MD4 发展而来,它们的结构和强度等特性有很多相似之处,下图是对MD5 与SHA-1的结构比较。SHA-1与MD5 的最大区别在于其摘要比MD5 摘要长 32bit。对于强行攻击,产生任何一个报文使之摘要等于给定报文摘要的难度:MD5 是2128 数量级的操作,SHA-1 是2160数量级的操作。产生具有相同摘要的两个报文的难度:MD5是 264 是数量级的操作,SHA-1 是280 数量级的操作。因而,SHA-1对强行攻击的强度更大。但由于SHA-1 的循环步骤比MD5 多(80:64)且要处理的缓存大(160 比特:128 比特),SHA-1的运行速度比MD5 慢。
对称加密
理解
采用单钥密码的加密方法,同一个密钥可以同时用来加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
常用的算法有:DES、3DES、AES等。
- DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合;
- 3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高;
- AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高,支持128、192、256、512位密钥的加密;
算法特征
1、加密方和解密方使用同一个密钥;
2、加密解密的速度比较快,适合数据比较长时的使用;
3、密钥传输的过程不安全,且容易被破解,密钥管理也比较麻烦;
crypto-js安装及使用
要用 AES 、DES、Rabbit、RC4、Triple DES 等加解密算法加密我们都可以使用 crypto-js组件实现。
它 是一个纯 javascript 写的加密算法类库 ,可以非常方便地在 javascript 进行 MD5(上文提到)、SHA1(上文提到)、SHA2、SHA3、RIPEMD-160 哈希散列,进行 AES、DES、Rabbit、RC4、Triple DES 加解密。也是目前使用人数很多,更新修复频率很快的组件。
我们可以采用以下指令进行下载安装,或者到其gitbug上面下载源码引入使用
npm install crypto-js --save
由于涉及到比较多的加密算法,我们可以模块化引入,需要用到某个加密算法就对应引入
import sha256 from 'crypto-js/sha256';
import hmacSHA512 from 'crypto-js/hmac-sha512';
import Base64 from 'crypto-js/enc-base64';
也可以直接引入整个js,如果要用到多个加密库
import CryptoJS from 'crypto-js'
console.log(CryptoJS.HmacSHA1("Message", "Key"));
// 加密
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();
// 解密
var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var originalText = bytes.toString(CryptoJS.enc.Utf8);
console.log(originalText); // 'my message'
WordArray (An array of 32-bit words.)
使用AES加密前,先了解下WordArray,我把它理解成CryptoJS中定义的 新的 数据类型,叫“单词数组”。
1.1 : 初始化
var wordArray = CryptoJS.lib.WordArray.create();//创建一个空的 WordArray对象
1.2 : WordArray 对象 —>16进制字符串
var string = wordArray.toString();//默认CryptoJS.enc.Hex,即16进制字符串
var string = wordArray.toString(CryptoJS.enc.Utf8);//utf-8字符串
1.3 : 16进制字符串 —>WordArray对象
var wordArray = CryptoJS.enc.Hex.parse(hexString);
1.4 : WordArray对象—>utf8字符串
var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
//等价于1.2中 wordArray.toString(CryptoJS.enc.Utf8);
1.5 : utf8字符串—>WordArray对象
var wordArray = CryptoJS.enc.Utf8.parse(utf8String);
1.6 : WordArray对象—>Base64字符串
var base64String = CryptoJS.enc.Base64.stringify(wordArray);
1.7 : Base64字符串—>WordArray对象
var wordArray = CryptoJS.enc.Base64.parse(base64String);
AES例子
import CryptoJS from 'crypto-js'
var iv = CryptoJS.lib.WordArray.random(128 / 8).toString(CryptoJS.enc.Hex)
// 加密
export const encrypt = (word) => {
var key = CryptoJS.enc.Latin1.parse("46cc793c53dc451b"); //重点是 key要转换为WordArray对象,加密时要用。
var srcs = CryptoJS.enc.Utf8.parse(word); //将utf8字符串转换为 WordArray对象
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
//iv: CryptoJS.enc.Hex.parse(iv), //初始向量
mode: CryptoJS.mode.ECB,//模式
padding: CryptoJS.pad.Pkcs7 //偏移量
//不同的IV加密后的字符串是不同的,加密和解密需要相同的IV。
});
return encrypted.toString(); //密码对象的Base64字符串
return encrypted;//密码对象(Obejct类型,非WordArray类型),Base64编码。
}
// 解密
//params: 注意参数word 必须为 Base64编码的对象或者字符串。
export const decrypt = (word) => {
var key = CryptoJS.enc.Latin1.parse("46cc793c53dc451b");
var decrypt = CryptoJS.AES.decrypt(word, key, {
//iv: CryptoJS.enc.Hex.parse(iv),
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypt.toString(CryptoJS.enc.Utf8) //WordArray对象转utf8字符串
}
DES的加密方法大致一样在加密时候把AES改为DES即可。
加密模式还有另外一种CBC。关于CBC跟ECB的加密模式可自行了解。
注意iv的处理,和key是一样的
key需要使用CryptoJS.enc.Latin1.parse,其他什么CryptoJS.enc.Utf8都不行千万注意
下面是我根据网上查到的部分代码写的一个AES加解密的demo
import CryptoJS from 'crypto-js'
CryptoJS.enc.u8array = {
stringify: (wordArray) => {
// Shortcuts
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
// Convert
var u8 = new Uint8Array(sigBytes);
for (var i = 0; i < sigBytes; i++) {
var byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
u8[i] = byte;
}
return u8;
},
parse: (u8arr) => {
// Shortcut
var len = u8arr.length;
// Convert
var words = [];
for (var i = 0; i < len; i++) {
words[i >>> 2] |= (u8arr[i] & 0xff) << (24 - (i % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, len);
}
}
// 加密方法 传入明文的Uint8Array数组
export const getAesString = (array, shareKey) => {
// console.log(shareKey)
var key = CryptoJS.enc.Latin1.parse(shareKey)
var acontent = array
// 将明文转换成WordArray
var contentWA = CryptoJS.enc.u8array.parse(acontent)
// 插件要求明文是base64格式
// var dcBase64String = contentWA.toString(CryptoJS.enc.Base64)
// 加密 选定mode是CFB类型,无偏移量
var encrypted = CryptoJS.AES.encrypt(contentWA, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
// 将密文转回uint8数组
var bv = CryptoJS.enc.u8array.stringify(encrypted.ciphertext)
// console.log(bv)
return bv
}
export const getDAesString = (array, shareKey) => {
// console.log(array)
// console.log(shareKey)
var key = CryptoJS.enc.Latin1.parse(shareKey)
var acontent = array
// 将密文转换成WordArray
var contentWA = CryptoJS.enc.u8array.parse(acontent)
// 插件要求密文是base64格式
var dcBase64String = contentWA.toString(CryptoJS.enc.Base64)
// 解密 选定mode是CFB类型,无偏移量
var decrypted = CryptoJS.AES.decrypt(dcBase64String, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
// 将解密后的明文转回Uint8Array数组
var bv = CryptoJS.enc.u8array.stringify(decrypted)
return bv
}
非对称加密
理解
非对称加密算法有两个密钥,公开密钥(publickey) 和私有密(privatekey)且它们是一对的。
如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
反过来私有密钥对数据进行加密,只有用对应的公开密钥对才能解密。
特点
算法强度复杂,安全性依赖于算法与密钥。
缺点
由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。
主要算法有:RSA、Elgamal、ESA、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等。不同算法的实现机制不同,可参考对应算法的详细资料。
RSA(jsencrypt的安装使用)
安装
npm install jsencrypt
使用
import JSEncrypt from 'jsencrypt/bin/jsencrypt'
//加密
function encryptData(getPublicKey, data) {
// 实例化对象
let encrypt = new JsEncrypt();
// 设置公钥
encrypt.setPublicKey(getPublicKey);
// 返回加密的数据
return encrypt.encrypt(data);
}
//解密
function decryptData(privateKey, data) {
// 实例化对象
let decrypt = new JsEncrypt();
// 设置公钥
decrypt.setPrivateKey(privateKey);
// 返回解密密的数据
return decrypt.decrypt(data)
}
RSA这里就不详细说明了贴上对应npm的地址大家可以到其官网查看:http://travistidwell.com/jsencrypt/
ECC椭圆曲线加密算法(curve25519-js)
Curve25519 是目前最高水平的 Diffie-Hellman函数,适用于广泛的场景。在密码学中,Curve25519是一个椭圆曲线提供128位安全性,设计用于椭圆曲线Diffie-Hellman(ECDH)密钥协商方案。它是最快的ECC曲线之一,并未被任何已知专利所涵盖。
使用理解
curve25519-js 可用64位长度的16进制字符串生成公钥跟私钥。
A、B端各生成属于自己的32字节的公钥、私钥。
A、B端可获取到对方的公钥。
加密:使用自己的私钥和对方的公钥由curve25519计算生成32字节的共享密钥进行AES内容的加密。
解密:使用对方的公钥跟自己的私钥生成共享密钥进行AES解密。
安装与使用
安装
npm i curve25519-js
使用
import { sharedKey } from 'curve25519-js';
const ALICE_PRIV = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
const BOB_PUB = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
const alicePriv = Uint8Array.from(Buffer.from(ALICE_PRIV, 'hex'));
const bobPub = Uint8Array.from(Buffer.from(BOB_PUB, 'hex'));
const secret = sharedKey(alicePriv, bobPub);
console.log('Secret:', Buffer.from(secret).toString('hex'))
demo
下面我会展示ECC配合AES加密的例子
//util.js
import {
sharedKey,
generateKeyPair
} from "curve25519-js"
import CryptoJS from 'crypto-js'
// 生成公私钥
export const getKeyPair = (uid) => {
const hex = uuid2(64, 16);//生成UUid
const key = generateKeyPair(toUint8Array(hex)); //generateKeyPair需传入为Uint8Array类型的方法
const keyPair = {
private: toBuffer(key.private).toUpperCase(),
public: toBuffer(key.public).toUpperCase()
}
...
//把key存储起来(db或本地缓存)
return keyPair
}
//生成共享秘钥
export const getSharedKey = (publicKey, privateKey) => {
return toBuffer(sharedKey(toUint8Array(privateKey), toUint8Array(publicKey))).substring(0, 16).toUpperCase()
}
//生成UUid len 长度 radix 进制
export const uuid2 = (len, radix) => {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/=".split("")
var uuid = [],
i
radix = radix || chars.length
if (len) {
for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
} else {
var r;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
uuid[14] = "4";
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | (Math.random() * 16);
uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join("");
}
//转换为ArrayBuffer类型
function toBuffer(res) {
return Buffer.from(res).toString("hex")
}
//转换为Uint8Array类型
function toUint8Array(res) {
return Uint8Array.from(Buffer.from(res, "hex"));
}
CryptoJS.enc.u8array = {
stringify: (wordArray) => {
// Shortcuts
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
// Convert
var u8 = new Uint8Array(sigBytes);
for (var i = 0; i < sigBytes; i++) {
var byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
u8[i] = byte;
}
return u8;
},
parse: (u8arr) => {
// Shortcut
var len = u8arr.length;
// Convert
var words = [];
for (var i = 0; i < len; i++) {
words[i >>> 2] |= (u8arr[i] & 0xff) << (24 - (i % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, len);
}
}
// 加密方法 传入明文的uint8数组
export const getAesString = (array, shareKey) => {
// console.log(shareKey)
var key = CryptoJS.enc.Latin1.parse(shareKey)
var acontent = array
// 将明文转换成WordArray
var contentWA = CryptoJS.enc.u8array.parse(acontent)
// 插件要求明文是base64格式
// var dcBase64String = contentWA.toString(CryptoJS.enc.Base64)
// 加密 选定mode是CFB类型,无偏移量
var encrypted = CryptoJS.AES.encrypt(contentWA, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
// 将密文转回uint8数组
var bv = CryptoJS.enc.u8array.stringify(encrypted.ciphertext)
// console.log(bv)
return bv
}
export const getDAesString = (array, shareKey) => {
// console.log(array)
// console.log(shareKey)
var key = CryptoJS.enc.Latin1.parse(shareKey)
var acontent = array
// 将密文转换成WordArray
var contentWA = CryptoJS.enc.u8array.parse(acontent)
// 插件要求密文是base64格式
var dcBase64String = contentWA.toString(CryptoJS.enc.Base64)
// 解密 选定mode是CFB类型,无偏移量
var decrypted = CryptoJS.AES.decrypt(dcBase64String, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
// 将解密后的明文转回uint8数组
var bv = CryptoJS.enc.u8array.stringify(decrypted)
return bv
}
部分知识参考自:
https://blog.csdn.net/z834410038/article/details/70231668?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3
https://www.jianshu.com/p/659be55f8297