ph-utils
Version:
js 开发工具集,前后端都可以使用(commonjs和es module)
259 lines (258 loc) • 8.44 kB
JavaScript
/* eslint-disable no-undef */
/**
* 将原始的二进制数据转换为 Hex String
* @param bf 待转换的原始数据
* @param upper 是否需要转换为大写
* @returns
*/
export function bufferToHex(bf, upper = false) {
const u8Array = bf instanceof Uint8Array ? bf : new Uint8Array(bf);
const hashArray = Array.from(u8Array);
return hashArray
.map((b) => {
let hx = b.toString(16).padStart(2, "0");
return upper === true ? hx.toUpperCase() : hx;
})
.join("");
}
/**
* 将16进制的数据转换为 UInt8Array
* @param data 16进制的数据
* @returns
*/
function hexToBuffer(data) {
const len = data.length / 2;
const uint8Array = new Uint8Array(len);
for (let i = 0; i < len; i++) {
const byteHex = data.substring(i * 2, i * 2 + 2);
const byte = parseInt(byteHex.toLocaleLowerCase(), 16);
uint8Array[i] = byte;
}
return uint8Array;
}
/**
* 将原始数据转换为 Base64 编码
* @param bf 待转换的原始数据
* @returns
*/
function bufferToBase64(bf) {
const u8Array = bf instanceof Uint8Array ? bf : new Uint8Array(bf);
const hashArray = Array.from(u8Array);
return globalThis.btoa(String.fromCharCode.apply(null, hashArray));
}
/**
* 将 Base64 转换为 UInt8Array 数据
* @param data
* @returns
*/
function base64ToBuffer(data) {
return new Uint8Array(globalThis
.atob(data)
.split("")
.map((char) => char.charCodeAt(0)));
}
/**
* SHA 哈希算法
* @param message 待进行 hash 的数据
* @param upper 是否转换为大写, 默认为: false
* @param algorithm hash算法, 支持: SHA-1、SHA-256、SHA-384、SHA-512; 默认为: SHA-256
* @returns
*/
export async function sha(message, upper = false, algorithm = "SHA-256") {
const msgUint8 = new TextEncoder().encode(message);
const hashBuffer = await globalThis.crypto.subtle.digest(algorithm || "SHA-256", msgUint8);
return bufferToHex(hashBuffer, upper);
}
/**
* 哈希算法
* @param message 待进行 hash 的数据
* @param upper 是否转换为大写, 默认为: false
* @param algorithm hash算法, 支持: SHA-1、SHA-256、SHA-384、SHA-512; 默认为: SHA-256
* @returns
*/
export async function hash(message, upper = false, algorithm = "SHA-256") {
return sha(message, upper, algorithm);
}
/**
* 使用 HMAC 算法计算消息的哈希值
* @param message - 需要计算哈希的消息字符串
* @param secret - 用于生成 HMAC 的密钥
* @param algorithm - HMAC 使用的哈希算法,默认为 "SHA-256"
* @param upper - 是否将结果转换为大写,默认为 false
* @returns 返回十六进制格式的 HMAC 哈希值
*/
export async function hmacHash(message, secret, algorithm = "SHA-256", upper = false) {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: { name: algorithm } }, false, ["sign"]);
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
return bufferToHex(signature, upper);
}
function parseRsaKey(pem) {
const pemHeader = "-----BEGIN PUBLIC KEY-----";
const pemFooter = "-----END PUBLIC KEY-----";
if (pem.indexOf(pemHeader) !== -1 && pem.indexOf(pemFooter) !== -1) {
pem = pem.substring(pemHeader.length, pem.indexOf(pemFooter)).trim();
}
return pem;
}
/**
* 导入上下文密钥
* @param key 导入的密钥
* @param algorithmName 算法名称
* @param usages 该密钥可以用于哪些函数使用
* @returns
*/
// eslint-disable-next-line no-undef
async function importKey(key, algorithmName, usages, encoding = "hex") {
let name = "AES-CBC";
if (algorithmName == null) {
name = "AES-CBC";
}
else {
if (algorithmName.toUpperCase() === "AES") {
name = "AES-CBC";
}
else if (algorithmName.toUpperCase() === "RSA") {
name = "RSA-OAEP";
}
}
name = name.toUpperCase();
if (usages == null || usages.length === 0) {
usages = ["encrypt"];
}
let format = "raw";
let algorithm = { name };
if (name.includes("RSA")) {
format = "spki";
algorithm.hash = { name: "SHA-256" };
key = parseRsaKey(key);
}
const keyBuf = encoding === "base64" ? base64ToBuffer(key) : hexToBuffer(key);
// importKey时传 false 表明该密钥不能被导出
return Promise.all([
globalThis.crypto.subtle.importKey(format, keyBuf, algorithm, false, usages),
Promise.resolve({ name }),
]);
}
/**
* 加密
* @param algorithm 算法参数, 算法名称、向量等
* @param key 算法密钥
* @param message 待加密的数据
* @param encode 解密后返回数据格式
* @returns
*/
async function encrypt(algorithm, key, message, encode = "hex") {
if (typeof message === "string") {
message = new TextEncoder().encode(message);
}
const encrypted = await globalThis.crypto.subtle.encrypt(algorithm, key, message);
if (encode === "hex") {
return bufferToHex(encrypted);
}
else if (encode === "hexUpper") {
return bufferToHex(encrypted, true);
}
else if (encode === "base64") {
return bufferToBase64(encrypted);
}
else {
return encrypted;
}
}
/**
* 数据解密
* @param algorithm 解密算法
* @param key 解密密钥
* @param message 加密后的数据
* @returns
*/
async function decrypt(algorithm, key, message) {
const decrypted = await globalThis.crypto.subtle.decrypt(algorithm, key, message);
return new TextDecoder("utf-8").decode(decrypted);
}
/**
* AES 加密
* @param message 待加密的数据
* @param key 加解密密钥
* @param encode 加密后的数据转换的形式, hex - 转换为16进制字符串, hexUpper - 转换为16进制且大写, base64 - 转换为 base64 形式
* @param iv 加解密向量
* @returns [加密数据,向量]
*/
export async function aesEncrypt(message, key, encode = "hex", iv = null) {
let ciphertext = "";
let resIv = "";
// 导入密钥
const [cryptoKey, algorithm] = await importKey(key, "aes", ["encrypt"]);
if (iv == null) {
iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
}
else if (typeof iv === "string") {
iv = hexToBuffer(iv);
}
const encodeData = await encrypt({ ...algorithm, iv }, cryptoKey, message, encode);
ciphertext = encodeData;
resIv = bufferToHex(iv);
return { ciphertext, iv: resIv, key };
}
/**
* 根据加密后的数据类型使用对应函数最终转换为 UInt8Array
* @param message 原始加密后的数据
* @param type 类型: hex | base64
* @returns
*/
function parseEncryptData(message, type) {
let input;
if (typeof message === "string") {
if (type === "hex" || type === "hexUpper") {
input = hexToBuffer(message);
}
else {
input = base64ToBuffer(message);
}
}
else {
input = message;
}
return input;
}
/**
* AES 解密
* @param message 加密后的数据
* @param key 解密密钥
* @param iv 向量
* @param encode 加密后数据的形式: hex | base64
* @returns
*/
export async function aesDecrypt(message, key, iv, encode = "hex") {
// 导入密钥
const [cryptoKey, algorithm] = await importKey(key, "aes", ["decrypt"]);
const input = parseEncryptData(message, encode);
return await decrypt({ ...algorithm, iv: hexToBuffer(iv) }, cryptoKey, input);
}
/**
* RSA 加密
* @param key 公钥
* @param message 待加密数据
* @param encode 返回类型
* @returns
*/
export async function rsaEncrypt(message, publicKey, encode = "hex") {
// 导入密钥
const [cryptoKey, algorithm] = await importKey(publicKey, "rsa", ["encrypt"], "base64");
return await encrypt(algorithm, cryptoKey, message, encode);
}
/**
* RSA 解密
* @param key 私钥, 根据私钥解密
* @param message 加密后的数据
* @param encode 加密后的数据形式
* @returns
*/
export async function rsaDecrypt(privateKey, message, encode = "hex") {
// 导入密钥
const [cryptoKey, algorithm] = await importKey(privateKey, "rsa", [
"decrypt",
]);
return await decrypt({ ...algorithm }, cryptoKey, parseEncryptData(message, encode));
}