常见的加密算法
md5
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
var txt = "sabercoco";
var enc = cryptoJs.MD5(txt).toString();
console.log(enc); // f39079f491a237b1e7744461d5a90d3c
console.log(enc.length); // 32
SHA1
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
var txt = "sabercoco";
var enc = cryptoJs.SHA1(txt).toString();
console.log(enc); // 847fc6a20616e20f115b24907dd6f1eb4ee22593
console.log(enc.length); // 40
SHA256
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
var txt = "sabercoco";
var enc = cryptoJs.SHA256(txt).toString();
console.log(enc); // 91a34bc29c0d23e416adf719ffec1dd948932d2e4f552655c0e378d56657b5db
console.log(enc.length); // 64
SHA512
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
var txt = "sabercoco";
var enc = cryptoJs.SHA512(txt).toString();
console.log(enc); // db8a11c3f0a068ff63dde5cc5f1ce12b5620426450e4e49647c05087da987eda238712a15e2219063ccee0d540b3ab1cf161019a03e0c4a3a46b9375eea5b1de
console.log(enc.length); // 128
HMAC-SHA1
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
var key = "112358";
var txt = "sabercoco";
var enc = cryptoJs.HmacSHA1(txt, key).toString();
console.log(enc); // 6cbe2510fa02462fd629290dd7ff3bd9dedb3c0d
console.log(enc.length); // 40
HMAC-SHA256
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
var key = "112358";
var txt = "sabercoco";
var enc = cryptoJs.HmacSHA256(txt, key).toString();
console.log(enc); // 60e4be597e1330f9bad71c26a0e049141afc8ce2deb9281a0565cb68e57d5013
console.log(enc.length); // 64
DES
库:crypto-js
const cryptoJs = require("./libs/node_modules/crypto-js");
const txt = "admin@123";
const key = cryptoJs.enc.Utf8.parse("666666");
cfg = {
mode: cryptoJs.mode.ECB,
padding: cryptoJs.pad.Pkcs7
}
let encPwd = cryptoJs.DES.encrypt(txt, key, cfg).toString();
console.log(encPwd); // MjXXkgE/lsbYKbt/WS39kQ==
AES
库:crypto-js
// AES加密之ECB模式
const cryptoJs = require("./libs/node_modules/crypto-js");
const password = "666666";
const key = "112358";
cfg = {
mode: cryptoJs.mode.ECB,
padding: cryptoJs.pad.Pkcs7
}
let encPwd = cryptoJs.AES.encrypt(password, key, cfg).toString();
console.log(encPwd); // U2FsdGVkX1+8PhV4eFuS5lDh3Hn3hwJfiP4bXnzp43w=
// AES加密之ECB模式
const cryptoJs = require("./libs/node_modules/crypto-js");
const password = "666666";
const key = cryptoJs.enc.Hex.parse("112358");
cfg = {
mode: cryptoJs.mode.ECB,
padding: cryptoJs.pad.Pkcs7
}
let encPwd = cryptoJs.AES.encrypt(password, key, cfg).toString();
console.log(encPwd); // QQeP4bXNsL8L84OhTM8V7g==
上面两段代码都是AES的ECB加密模式,有何不同呢?多次执行上面两段代码会发现第一段代码获得的加密值每次都在改变,而第二段代码获得的加密值是不变的,按理说,ECB模式下,同一个密钥,若原文相同,则密文必定相同。什么原因导致第一段代码每次获得的密文都不同呢?具体原因参考:
https://zhuanlan.zhihu.com/p/652543706
https://www.jianshu.com/p/0689506403e7/
// AES加密之CBC模式
const cryptoJs = require("./libs/node_modules/crypto-js");
let password = cryptoJs.enc.Utf8.parse("123456");
let key = cryptoJs.enc.Utf8.parse("11235810");
let iv = cryptoJs.enc.Utf8.parse("123456"); // 指定初始向量
cfg = {
mode: cryptoJs.mode.CBC,
padding: cryptoJs.pad.Pkcs7,
iv: iv
}
let encPwd = cryptoJs.AES.encrypt(password, key, cfg).toString();
console.log(encPwd);
RSA
库:jsencrypt
window = global;
const JsEnc = require("./libs/node_modules/jsencrypt");
pubkey = `MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJSl2lvedpCUgSKqZEteQjxkNiVumal6
YF3dAwDO2bt1VwmuIyhD6QYFS7bmsHP+twtV0dtfv69imAtXBsCrN5MCAwEAAQ==`
// 加密
let jse = new JsEnc();
jse.setPublicKey(pubkey);
let encStr = jse.encrypt("admin");
console.log("rsa加密数据:" + encStr);
// 解密
prikey = `MIIBOwIBAAJBAJSl2lvedpCUgSKqZEteQjxkNiVumal6YF3dAwDO2bt1VwmuIyhD
6QYFS7bmsHP+twtV0dtfv69imAtXBsCrN5MCAwEAAQJAQiFW97AMx12J5cYwQGd6
yCs8+4Is0n0jCh8dCur7aq2nNr7vZyPIHF75AToYoh3pvz3cMotiNeUGE8sD38di
4QIhANHUvF25qrX35jozYjjbnrFEcWkFCjYnGF1V+dpqZG0HAiEAtVrSUZVYMj77
uWRxYUGRVZAJOMCh/Wv9I1h1TfV0ChUCIBZ6sPdlhala9nozuQkYVibtiyzSEmb8
xzpwlITWsw2PAiEAgcKWMeGV1X3TRvdjWJ89iechcgYC4fzehufIJxu1B3ECIQDC
DgxrcCUAWobA188TSPKM2to67Aiq7RxziOpmTOlgoA==`
jse.setPrivateKey(prikey);
let str = jse.decrypt(encStr);
console.log("rsa解密数据:" + str);
SM 系列
SM 系列加密算法,即国密算法,按照是否可商用可分为如下两类,
可商用
SM2 对标 RSA
SM3 对标 hash
SM4 对标 AES
不可商用
SM1
SM7
SM9
ZUC(祖冲之加密算法)
sm2
库:sm-crypto
const sm = require('sm-crypto').sm2;
const txt = "admin"
// 生成密钥对
const keypair = sm.generateKeyPairHex();
const pubKey = keypair.publicKey;
const priKey = keypair.privateKey;
// 加密
const cipher = sm.doEncrypt(txt, pubKey);
console.log("cipher:")
console.log(cipher)
// 解密
const decryptcipher = sm.doDecrypt(cipher, priKey);
console.log("\ndecryptcipher:")
console.log(decryptcipher)
填充模式
NoPadding
不填充,往往需要原文长度和分组长度可以整除,比如,采用AES128算法,其数据块长度为16byte,那么原文长度L,应该满足:L=16n(n=1,2,3…)。
Pkcs7
Pkcs7的填充方式为当最后的分块数据长度不足数据块长度(BlockSize)时,缺几位就补几个几,比如,对于AES128算法,其数据块为16byte,如果数据为"00112233445566778899AA",一共11个字节,缺了5个字节,采用Pkcs7方式填充之后的数据为"00112233445566778899AA<0x05><0x05><0x05><0x05><0x05>",这里说明一下,为啥是<0x05>不是05或者5,其实这三个都可以,只是同一个符号的不同进制形式而已,05是八进制,5是十进制,由于我打不出来<0x05>对应的那个符号,于是就用<0x05>代替,所以当看到05这种填充的时候,不要以为是两个字节哦!
需要注意的一点是,如果明文数据长度刚好是分组长度的整数倍,按Pkcs7规则,仍需填充一个分组长度的数据,比如,
明文
ABCDEFGHHIJKLMNO0123456789012345
明文转ascii
414243444546474848494a4b4c4d4e4f30313233343536373839303132333435
Pkcs7填充后
414243444546474848494a4b4c4d4e4f3031323334353637383930313233343510101010101010101010101010101010
分块
block1:414243444546474848494a4b4c4d4e4f
block2:30313233343536373839303132333435
block3:10101010101010101010101010101010
加密模式
首先要明白加密模式是对称加密算法中的概念,我常接触的有如下两种:
ECB:电子密码本模式
CBC:加密块链模式
ECB
将加密的数据分成若干组,每组的大小跟密钥长度相同,然后每组都用相同的密钥进行加密。比如,DES算法,采用一个64位的密钥,如果采用该模式加密,就是将要加密的数据分成每组64位的数据,如果最后一组不够64位,那么就补齐为64位,然后每组数据都采用DES算法的64位密钥进行加密。
因为ECB方式每块使用的密钥都是相同的,所以非常容易获得密文进行密码破解。此外,因为每块是相互独立的,有时候甚至不用破解密码,只要简单的将其中一块替换就可以达到某些目的。
下面以具体案例展示ECB的加密过程:
明文:{uid:1728208,pass:'admin_123'}
密钥:0123456789ABCDEF
加密算法:AES
填充模式:Pkcs7
分块
block1:{uid:1728208,pas
block2:s:'admin_123'}\x02\x02
加密
const CryptoJS = require('./libs/node_modules/crypto-js');
const plaintext = 's:\'admin_123\'}\x02\x02';
const key = CryptoJS.enc.Hex.parse('0123456789ABCDEF');
const encrypted = CryptoJS.AES.encrypt(plaintext, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
const ciphertextHex = encrypted.ciphertext.toString();
console.log('加密后的密文(十六进制形式):', ciphertextHex.toUpperCase());
使用上面代码分别加密两个分组,结果如下:
34C9EF742A9C4E04A3CDBD47C14AE9295314115F3A6C6785DC0A4567C974F8AA
41D21D472932D6EFEF6543BFBBF9CDA55314115F3A6C6785DC0A4567C974F8AA
密文拼接
34C9EF742A9C4E04A3CDBD47C14AE9295314115F3A6C6785DC0A4567C974F8AA41D21D472932D6EFEF6543BFBBF9CDA55314115F3A6C6785DC0A4567C974F8AA
CBC
CBC模式的加密首先也是将明文分成固定长度(比如64位)的块(P0,P1…),然后将前面一个加密块输出的密文与下一个要加密的明文块进行XOR运算,将计算结果再用密钥进行加密得到密文。第一个明文块XOR运算的时候,因为前面没有加密的密文,所以需要一个初始化向量(iv),其实上一块的密文就是下一块的iv。跟ECB方式不一样,通过连接关系,使得密文跟明文不再是一一对应的关系,破解起来更困难,而且克服了只要简单调换密文块可能达到目的的攻击。