前言
之前文章有写关于加密的javascript用户密码加密,js密码加密,是很久之前写的了,有点过时。其实web支持前端加密了,喜大普奔,可以不用引入js进行前端加密。
Crypto对象
我们看下Crypto对象的兼容性
crypto.subtle对象
crypto 对象还支持一个名为 subtle 的属性,目前唯一的属性,返回的属性值是一个对象,称为 SubtleCrypto 对象,可以用来生成各种签名和密钥,所有方法均返回 Promise,包括:
SubtleCrypto.encrypt()
SubtleCrypto.decrypt()
SubtleCrypto.sign()
SubtleCrypto.verify()
SubtleCrypto.digest()
SubtleCrypto.generateKey()
SubtleCrypto.deriveKey()
SubtleCrypto.deriveBits()
SubtleCrypto.importKey()
SubtleCrypto.exportKey()
SubtleCrypto.wrapKey()
SubtleCrypto.unwrapKey()
SubtleCrypto.encrypt()
语法:
var result = crypto.encrypt(algo, key, cleartext);
algo 是一个使用加密函数的对象或者 DOMString,后者是 {"name": algo} 的缩写。支持的值是:
{"name": "AES-CBC", iv} iv 是具有16个随机字节的 ArrayBuffer 或 ArrayBufferView (这些应该由 RandomSource.getRandomValues() 生成)。
{"name": "AES-CTR", counter, length}
{"name": "AES-GCM", iv, additionalData, tagLength} (additionalData 和 tagLength 是可选的)
{"name": "RSA-OAEP", label} (label 是可选的)
key 是一个包含签名密钥的 CryptoKey。
cleartext 是一个包含需要加密的明文 ArrayBuffer 或者 ArrayBufferView 对象。
案例:
(() => {
/*
Store the calculated ciphertext and counter here, so we can decrypt the message later.
*/
let ciphertext;
let counter;
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the encrypt operation.
*/
function getMessageEncoding() {
const messageBox = document.querySelector("#aes-ctr-message");
let message = messageBox.value;
let enc = new TextEncoder();
return enc.encode(message);
}
/*
Get the encoded message, encrypt it and display a representation
of the ciphertext in the "Ciphertext" element.
*/
async function encryptMessage(key) {
let encoded = getMessageEncoding();
// The counter block value must never be reused with a given key.
counter = window.crypto.getRandomValues(new Uint8Array(16)),
ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-CTR",
counter,
length: 64
},
key,
encoded
);
let buffer = new Uint8Array(ciphertext, 0, 5);
const ciphertextValue = document.querySelector(".aes-ctr .ciphertext-value");
ciphertextValue.classList.add('fade-in');
ciphertextValue.addEventListener('animationend', () => {
ciphertextValue.classList.remove('fade-in');
});
ciphertextValue.textContent = `${buffer}...[${ciphertext.byteLength} bytes total]`;
}
/*
Fetch the ciphertext and decrypt it.
Write the decrypted message into the "Decrypted" box.
*/
async function decryptMessage(key) {
let decrypted = await window.crypto.subtle.decrypt(
{
name: "AES-CTR",
counter,
length: 64
},
key,
ciphertext
);
let dec = new TextDecoder();
const decryptedValue = document.querySelector(".aes-ctr .decrypted-value");
decryptedValue.classList.add('fade-in');
decryptedValue.addEventListener('animationend', () => {
decryptedValue.classList.remove('fade-in');
});
decryptedValue.textContent = dec.decode(decrypted);
}
/*
Generate an encryption key, then set up event listeners
on the "Encrypt" and "Decrypt" buttons.
*/
window.crypto.subtle.generateKey(
{
name: "AES-CTR",
length: 256
},
true,
["encrypt", "decrypt"]
).then((key) => {
const encryptButton = document.querySelector(".aes-ctr .encrypt-button");
encryptButton.addEventListener("click", () => {
encryptMessage(key);
});
const decryptButton = document.querySelector(".aes-ctr .decrypt-button");
decryptButton.addEventListener("click", () => {
decryptMessage(key);
});
});
})();
SubtleCrypto.sign()
//sign
signature = await window.crypto.subtle.sign(
{
name: "RSA-PSS",
saltLength: 32,
},
privateKey,
'加密数据data'
);
详细请看:https://developer.mozilla.org/zh-CN/docs/Web/API/Crypto
getRandomValues
上文有提到getRandomValues和 Math.random() 方法的区别在于,getRandomValues() 方法的随机种子生成器更加的无序,例如系统层面的无序源(有些硬件自带随机种子)。
然后不同浏览器下 getRandomValues() 方法生成的随机数可能是有区别的。
以及 getRandomValues() 方法的底层实现是没有缓存的,随机数都是实时生成的,因此,性能上是要比 Math.random() 差的,因此,如果是高并发的场景,同时随机数仅仅是用做随机,与安全和金钱不相关,请使用 Math.random() 而不是 getRandomValues()。
let randNumber = self.crypto.getRandomValues(new Uint32Array(1))[0];
// 一串随机整数,通常10位
console.log(randNumber);
UUID生成
除了生成随机数,Crypto对象还可以用来生成字符长度为36的 UUID (Universally Unique Identifier的缩写,表示唯一通用标识符)
let uuid = self.crypto.randomUUID();
console.log(uuid);
// 示意输出:2433df46-d77f-4eb9-bbdd-4cd99361fe08
不过这个 API 只有Chrome 92+,才支持。
关于uuid的生产,我前面有文章介绍 https://www.haorooms.com/post/nanoid_uuid
感兴趣的可以对比看一看。