mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-28 20:02:44 +08:00
可视化编排添加文本处理,添加堆成和非对称加解密
This commit is contained in:
parent
97a0ce2f99
commit
5e2994fe6d
150
plugin/lib/RSAEncryption.js
Normal file
150
plugin/lib/RSAEncryption.js
Normal file
@ -0,0 +1,150 @@
|
||||
import NodeForge from "node-forge";
|
||||
import { forgeBytesToStr, strToForgeBytes, dataConv } from "@/script/Common.js";
|
||||
|
||||
const RSAPadDict = {
|
||||
"PKCS#1": {
|
||||
getMaxSize: (keySize) => Math.floor((keySize - 1) / 8) - 11,
|
||||
coding: (chunks, secretKey, method) =>
|
||||
secretKey[method](chunks, "RSAES-PKCS1-V1_5"),
|
||||
},
|
||||
OAEP: {
|
||||
getMaxSize: (keySize) => Math.floor(keySize / 8) - 42,
|
||||
coding: (chunks, secretKey, method) =>
|
||||
secretKey[method](chunks, "RSA-OAEP"),
|
||||
},
|
||||
"OAEP/SHA-256": {
|
||||
getMaxSize: (keySize) => Math.floor(keySize / 8) - 66,
|
||||
coding: (chunks, secretKey, method) =>
|
||||
secretKey[method](chunks, "RSA-OAEP", {
|
||||
md: NodeForge.md.sha256.create(),
|
||||
}),
|
||||
},
|
||||
"OAEP/SHA-256/MGF1-SHA-1": {
|
||||
getMaxSize: (keySize) => Math.floor(keySize / 8) - 74,
|
||||
coding: (chunks, secretKey, method) =>
|
||||
secretKey[method](chunks, "RSA-OAEP", {
|
||||
md: NodeForge.md.sha256.create(),
|
||||
mgf1: {
|
||||
md: NodeForge.md.sha1.create(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const forgeRSAEncrypt = (bytes, padding, publicKey, outputCodec) => {
|
||||
publicKey = NodeForge.pki.publicKeyFromPem(publicKey);
|
||||
let keySize = publicKey.n.bitLength();
|
||||
let chunks = "";
|
||||
let encrypted = "";
|
||||
let maxBlockSize = RSAPadDict[padding].getMaxSize(keySize);
|
||||
for (let i = 0; i < bytes.length / maxBlockSize; i++) {
|
||||
chunks = bytes.substring(i * maxBlockSize, (i + 1) * maxBlockSize);
|
||||
chunks = RSAPadDict[padding].coding(chunks, publicKey, "encrypt");
|
||||
encrypted += chunks;
|
||||
}
|
||||
return outputCodec === "Base64"
|
||||
? NodeForge.util.encode64(encrypted)
|
||||
: NodeForge.util.bytesToHex(encrypted);
|
||||
};
|
||||
|
||||
const forgeRSADecrypt = (cipher, padding, privateKey, inputCodec) => {
|
||||
let bytes =
|
||||
inputCodec === "Base64"
|
||||
? NodeForge.util.decode64(cipher)
|
||||
: NodeForge.util.hexToBytes(cipher);
|
||||
privateKey = NodeForge.pki.privateKeyFromPem(privateKey);
|
||||
let keySize = privateKey.n.bitLength();
|
||||
let maxBlockSize = keySize / 8;
|
||||
let decrypted = "";
|
||||
let chunks = "";
|
||||
for (let i = 0; i < bytes.length / maxBlockSize; i++) {
|
||||
chunks = bytes.substring(i * maxBlockSize, (i + 1) * maxBlockSize);
|
||||
chunks = RSAPadDict[padding].coding(chunks, privateKey, "decrypt");
|
||||
decrypted += chunks;
|
||||
}
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
export const RSA = {
|
||||
name: "RSA",
|
||||
params: [
|
||||
{
|
||||
type: "list",
|
||||
options: ["PKCS#1", "OAEP", "OAEP/SHA-256", "OAEP/SHA-256/MGF1-SHA-1"],
|
||||
value: "PKCS#1",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "公钥",
|
||||
value: ["", "Pem"],
|
||||
options: ["Pem", "Hex", "Base64"],
|
||||
id: "publicKey",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "私钥",
|
||||
value: ["", "Pem"],
|
||||
options: ["Pem", "Hex", "Base64"],
|
||||
id: "privateKey",
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: ["Hex", "Base64"],
|
||||
value: "Base64",
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: "action",
|
||||
name: "生成密钥",
|
||||
component: "GenerateKeypair",
|
||||
},
|
||||
{
|
||||
type: "action",
|
||||
name: "CTF",
|
||||
component: "RsaCtf",
|
||||
},
|
||||
],
|
||||
encrypt: (
|
||||
msg,
|
||||
padding = "PKCS#1",
|
||||
[pubKey = "", pubKeyCodec = "Pem"],
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
[priKey = "", priKeyCodec = "Pem"],
|
||||
outputCodec = "Base64",
|
||||
{ inputCodec }
|
||||
) => {
|
||||
if (!pubKey) throw "缺少公钥";
|
||||
if (pubKeyCodec !== "Pem") {
|
||||
pubKey =
|
||||
"-----BEGIN PUBLIC KEY-----\n" +
|
||||
dataConv(pubKey, pubKeyCodec, "Base64") +
|
||||
"\n-----END PUBLIC KEY-----";
|
||||
}
|
||||
pubKey = pubKey.replace(/\\n/g, "\n");
|
||||
let bytes = strToForgeBytes(msg, inputCodec);
|
||||
let cipher = forgeRSAEncrypt(bytes, padding, pubKey, outputCodec);
|
||||
if (!cipher) throw "加密失败";
|
||||
return cipher;
|
||||
},
|
||||
decrypt: (
|
||||
cipher,
|
||||
padding = "PKCS#1",
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
[pubKey = "", pubKeyCodec = "Pem"],
|
||||
[priKey = "", priKeyCodec = "Pem"],
|
||||
codec = "Base64"
|
||||
) => {
|
||||
if (!priKey) return "缺少私钥";
|
||||
if (priKeyCodec !== "Pem") {
|
||||
priKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n" +
|
||||
dataConv(priKey, priKeyCodec, "Base64") +
|
||||
"\n-----END PRIVATE KEY-----";
|
||||
}
|
||||
priKey = priKey.replace(/\\n/g, "\n");
|
||||
let msg = forgeRSADecrypt(cipher, padding, priKey, codec);
|
||||
if (!msg) throw "解密失败";
|
||||
return forgeBytesToStr(msg);
|
||||
},
|
||||
};
|
149
plugin/lib/ShangmiEncryption.js
Normal file
149
plugin/lib/ShangmiEncryption.js
Normal file
@ -0,0 +1,149 @@
|
||||
import { strToBytesArray, bytesArrayToStr, dataConv } from "@/script/Common.js";
|
||||
import { processSecret } from "./SymmetricEncryption.js";
|
||||
import SM from "sm-crypto";
|
||||
|
||||
export const SM4 = {
|
||||
name: "SM4",
|
||||
params: [
|
||||
{
|
||||
type: "text",
|
||||
name: "Key",
|
||||
value: ["", "Utf8"],
|
||||
options: ["Utf8", "Hex", "Base64"],
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "IV",
|
||||
value: ["", "Utf8"],
|
||||
options: ["Utf8", "Hex", "Base64"],
|
||||
id: "IV",
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: ["ECB", "CBC"],
|
||||
value: "ECB",
|
||||
banList: {
|
||||
ECB: ["IV"],
|
||||
},
|
||||
id: "mode",
|
||||
},
|
||||
{ type: "list", options: ["pkcs#7", "none"], value: "pkcs#7" },
|
||||
{ type: "list", options: ["Base64", "Hex"], value: "Base64" },
|
||||
],
|
||||
encrypt: (
|
||||
msg,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
padding = "pkcs#7",
|
||||
outputCodec = "Base64",
|
||||
{ inputCodec }
|
||||
) => {
|
||||
let bytesArray = strToBytesArray(msg, inputCodec);
|
||||
return dataConv(
|
||||
SM.sm4.encrypt(bytesArray, processSecret(key, 16, "hex"), {
|
||||
mode: mode.toLowerCase(),
|
||||
iv: processSecret(iv, 16, "hex"),
|
||||
padding: padding,
|
||||
}),
|
||||
"Hex",
|
||||
outputCodec
|
||||
);
|
||||
},
|
||||
decrypt: (
|
||||
cipher,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
padding = "pkcs#7",
|
||||
codec = "Base64"
|
||||
) => {
|
||||
let bytesArray = SM.sm4.decrypt(
|
||||
dataConv(cipher, codec, "Hex"),
|
||||
processSecret(key, 16, "hex"),
|
||||
{
|
||||
mode: mode.toLowerCase(),
|
||||
iv: processSecret(iv, 16, "hex"),
|
||||
padding: padding,
|
||||
output: "array",
|
||||
}
|
||||
);
|
||||
return bytesArrayToStr(bytesArray);
|
||||
},
|
||||
};
|
||||
|
||||
export const SM2 = {
|
||||
name: "SM2",
|
||||
params: [
|
||||
{
|
||||
type: "text",
|
||||
name: "公钥",
|
||||
value: ["", "Base64"],
|
||||
options: ["Hex", "Base64"],
|
||||
id: "publicKey",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "私钥",
|
||||
value: ["", "Base64"],
|
||||
options: ["Hex", "Base64"],
|
||||
id: "privateKey",
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: [
|
||||
{ label: "C1C3C2", value: 1 },
|
||||
{ label: "C1C2C3", value: 0 },
|
||||
],
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: ["Hex", "Base64"],
|
||||
value: "Base64",
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: "action",
|
||||
name: "生成密钥",
|
||||
component: "GenerateKeypair",
|
||||
},
|
||||
],
|
||||
encrypt: (
|
||||
msg,
|
||||
[pubKey = "", pubKeyCodec = "Base64"],
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
[priKey = "", priKeyCodec = "Base64"],
|
||||
cipherMode = 1,
|
||||
outputCodec = "Base64",
|
||||
{ inputCodec }
|
||||
) => {
|
||||
if (!pubKey) throw "缺少公钥";
|
||||
pubKey = dataConv(pubKey, pubKeyCodec, "Hex");
|
||||
let cipher = SM.sm2.doEncrypt(
|
||||
strToBytesArray(msg, inputCodec),
|
||||
pubKey,
|
||||
cipherMode
|
||||
);
|
||||
if (!cipher) throw "加密失败";
|
||||
return dataConv(cipher, "Hex", outputCodec);
|
||||
},
|
||||
decrypt: (
|
||||
cipher,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
[pubKey = "", pubKeyCodec = "Base64"],
|
||||
[priKey = "", priKeyCodec = "Base64"],
|
||||
cipherMode = 1,
|
||||
codec = "Base64"
|
||||
) => {
|
||||
if (!priKey) return "缺少私钥";
|
||||
priKey = dataConv(priKey, priKeyCodec, "Hex");
|
||||
cipher = dataConv(cipher, codec, "Hex");
|
||||
let msg = SM.sm2.doDecrypt(cipher, priKey, cipherMode, {
|
||||
output: "array",
|
||||
});
|
||||
if (!msg?.length) throw "解密失败";
|
||||
return bytesArrayToStr(msg);
|
||||
},
|
||||
};
|
280
plugin/lib/SymmetricEncryption.js
Normal file
280
plugin/lib/SymmetricEncryption.js
Normal file
@ -0,0 +1,280 @@
|
||||
import CryptoJS from "crypto-js";
|
||||
import NodeForge from "node-forge";
|
||||
import {
|
||||
strToWordArray,
|
||||
forgeBytesToStr,
|
||||
wordArrayToStr,
|
||||
dataConv,
|
||||
} from "@/script/Common.js";
|
||||
|
||||
let symmetricEncryptionParams = [
|
||||
{
|
||||
type: "text",
|
||||
name: "Key",
|
||||
value: ["", "Utf8"],
|
||||
options: ["Utf8", "Hex", "Base64"],
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "IV",
|
||||
value: ["", "Utf8"],
|
||||
options: ["Utf8", "Hex", "Base64"],
|
||||
// name 会改变元素样式,额外增加ID作为唯一标识
|
||||
id: "IV",
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: Object.keys(CryptoJS.mode),
|
||||
value: "ECB",
|
||||
// 根据banList禁用指定ID的元素,例如选中ECB时,禁用IV,ban和被ban的元素必须要有id
|
||||
banList: {
|
||||
ECB: ["IV"],
|
||||
},
|
||||
id: "mode",
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: [128, 192, 256],
|
||||
value: 128,
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
options: Object.keys(CryptoJS.pad),
|
||||
value: "Pkcs7",
|
||||
id: "padding",
|
||||
},
|
||||
{ type: "list", options: ["Base64", "Hex"], value: "Base64" },
|
||||
];
|
||||
|
||||
// AES添加GCM模式
|
||||
let symmetricEncryptionParamsAes = JSON.parse(
|
||||
JSON.stringify(symmetricEncryptionParams)
|
||||
);
|
||||
symmetricEncryptionParamsAes[2].options.push("GCM");
|
||||
symmetricEncryptionParamsAes[2].banList.GCM = ["padding"];
|
||||
|
||||
// secret 格式为 hex字符串,方便补位
|
||||
const adjustSecLen = (hexSecret, len) => {
|
||||
return hexSecret.length >= len * 2
|
||||
? hexSecret.slice(0, len * 2)
|
||||
: hexSecret + "00".repeat(len - hexSecret.length / 2);
|
||||
};
|
||||
|
||||
export const processSecret = (secretWithCodec, len, to = "wordArray") => {
|
||||
let [secret, codec] = secretWithCodec;
|
||||
let hexSecret = dataConv(secret, codec, "Hex");
|
||||
let filledSecret = adjustSecLen(hexSecret, len);
|
||||
return to === "wordArray"
|
||||
? CryptoJS.enc.Hex.parse(filledSecret)
|
||||
: filledSecret;
|
||||
};
|
||||
|
||||
// key/iv bytes
|
||||
const forgeAesGcmEncrypt = (msg, key, iv, outputCodec, inputCodec) => {
|
||||
var cipher = NodeForge.cipher.createCipher("AES-GCM", key);
|
||||
cipher.start({
|
||||
iv: iv,
|
||||
});
|
||||
cipher.update(NodeForge.util.createBuffer(msg, inputCodec));
|
||||
cipher.finish();
|
||||
var encrypted = cipher.output;
|
||||
return {
|
||||
enc: dataConv(encrypted.toHex(), "Hex", outputCodec),
|
||||
tag: dataConv(cipher.mode.tag.toHex(), "Hex", outputCodec),
|
||||
};
|
||||
};
|
||||
|
||||
// key/iv bytes
|
||||
const forgeAesGcmDecrypt = (cipher, key, iv, tag, inputCodec) => {
|
||||
cipher = NodeForge.util.createBuffer(
|
||||
NodeForge.util.hexToBytes(dataConv(cipher, inputCodec, "Hex"))
|
||||
);
|
||||
var decipher = NodeForge.cipher.createDecipher("AES-GCM", key);
|
||||
decipher.start({
|
||||
iv: iv,
|
||||
tag: NodeForge.util.hexToBytes(dataConv(tag, inputCodec, "Hex")),
|
||||
});
|
||||
decipher.update(cipher);
|
||||
var pass = decipher.finish();
|
||||
if (pass) {
|
||||
return decipher.output.getBytes();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const AES = {
|
||||
name: "AES",
|
||||
params: symmetricEncryptionParamsAes,
|
||||
encrypt: (
|
||||
msg,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
keySize = 128,
|
||||
padding = "Pkcs7",
|
||||
outputCodec = "Base64",
|
||||
{ inputCodec }
|
||||
) => {
|
||||
if (mode === "GCM") {
|
||||
key = NodeForge.util.hexToBytes(processSecret(key, keySize / 8, "hex"));
|
||||
iv = NodeForge.util.hexToBytes(processSecret(iv, 16, "hex"));
|
||||
let result = forgeAesGcmEncrypt(msg, key, iv, outputCodec, inputCodec);
|
||||
return JSON.stringify(result, null, 2);
|
||||
} else {
|
||||
let encrypted = CryptoJS.AES.encrypt(
|
||||
strToWordArray(msg, inputCodec),
|
||||
processSecret(key, keySize / 8, "wordArray"),
|
||||
{
|
||||
iv: processSecret(iv, 16, "wordArray"),
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
}
|
||||
);
|
||||
return CryptoJS.enc[outputCodec].stringify(encrypted.ciphertext);
|
||||
}
|
||||
},
|
||||
decrypt: (
|
||||
cipher,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
keySize = 128,
|
||||
padding = "Pkcs7",
|
||||
codec = "Base64"
|
||||
) => {
|
||||
if (mode === "GCM") {
|
||||
let format = `密文格式应为 {"enc":"hex or base64","tag":"hex or base64"}`;
|
||||
try {
|
||||
var { enc, tag } = JSON.parse(cipher);
|
||||
} catch (_) {
|
||||
throw new Error(format);
|
||||
}
|
||||
if (!enc || !tag) {
|
||||
throw new Error(format);
|
||||
}
|
||||
key = NodeForge.util.hexToBytes(processSecret(key, keySize / 8, "hex"));
|
||||
iv = NodeForge.util.hexToBytes(processSecret(iv, 16, "hex"));
|
||||
let decrypted = forgeAesGcmDecrypt(enc, key, iv, tag, codec);
|
||||
return forgeBytesToStr(decrypted);
|
||||
} else {
|
||||
cipher = dataConv(cipher, codec, "Base64");
|
||||
const decrypt = CryptoJS.AES.decrypt(
|
||||
cipher,
|
||||
processSecret(key, keySize / 8, "wordArray"),
|
||||
{
|
||||
iv: processSecret(iv, 16, "wordArray"),
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
}
|
||||
);
|
||||
return wordArrayToStr(decrypt);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const DES = {
|
||||
name: "DES",
|
||||
params: symmetricEncryptionParams.filter((x, i) => i !== 3),
|
||||
encrypt: (
|
||||
msg,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
padding = "Pkcs7",
|
||||
outputCodec = "Base64",
|
||||
{ inputCodec }
|
||||
) => {
|
||||
const encrypted = CryptoJS.DES.encrypt(
|
||||
strToWordArray(msg, inputCodec),
|
||||
processSecret(key, 8, "wordArray"),
|
||||
{
|
||||
iv: processSecret(iv, 8, "wordArray"),
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
}
|
||||
);
|
||||
return CryptoJS.enc[outputCodec].stringify(encrypted.ciphertext);
|
||||
},
|
||||
decrypt: (
|
||||
cipher,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
padding = "Pkcs7",
|
||||
codec = "Base64"
|
||||
) => {
|
||||
cipher = dataConv(cipher, codec, "Base64");
|
||||
const decrypt = CryptoJS.DES.decrypt(
|
||||
cipher,
|
||||
processSecret(key, 8, "wordArray"),
|
||||
{
|
||||
iv: processSecret(iv, 8, "wordArray"),
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
}
|
||||
);
|
||||
return wordArrayToStr(decrypt);
|
||||
},
|
||||
};
|
||||
|
||||
export const TripleDES = {
|
||||
name: "TripleDES",
|
||||
params: symmetricEncryptionParams.filter((x, i) => i !== 3),
|
||||
encrypt: (
|
||||
msg,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
padding = "Pkcs7",
|
||||
outputCodec = "Base64",
|
||||
{ inputCodec }
|
||||
) => {
|
||||
const encrypted = CryptoJS.TripleDES.encrypt(
|
||||
strToWordArray(msg, inputCodec),
|
||||
processSecret(key, 8, "wordArray"),
|
||||
{
|
||||
iv: processSecret(iv, 8, "wordArray"),
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
}
|
||||
);
|
||||
return CryptoJS.enc[outputCodec].stringify(encrypted.ciphertext);
|
||||
},
|
||||
decrypt: (
|
||||
cipher,
|
||||
key,
|
||||
iv = "",
|
||||
mode = "ECB",
|
||||
padding = "Pkcs7",
|
||||
codec = "Base64"
|
||||
) => {
|
||||
cipher = dataConv(cipher, codec, "Base64");
|
||||
const decrypt = CryptoJS.TripleDES.decrypt(
|
||||
cipher,
|
||||
processSecret(key, 8, "wordArray"),
|
||||
{
|
||||
iv: processSecret(iv, 8, "wordArray"),
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
}
|
||||
);
|
||||
return wordArrayToStr(decrypt);
|
||||
},
|
||||
};
|
||||
|
||||
export const rc4 = {
|
||||
name: "RC4",
|
||||
params: [{ type: "text", name: "Key", value: "" }],
|
||||
encrypt: (msg, key, { inputCodec }) =>
|
||||
CryptoJS.RC4.encrypt(strToWordArray(msg, inputCodec), key).toString(),
|
||||
decrypt: (cipher, key) => wordArrayToStr(CryptoJS.RC4.decrypt(cipher, key)),
|
||||
};
|
||||
|
||||
export const rabbit = {
|
||||
name: "Rabbit",
|
||||
params: [{ type: "text", name: "Key", value: "" }],
|
||||
encrypt: (msg, key, { inputCodec }) =>
|
||||
CryptoJS.Rabbit.encrypt(strToWordArray(msg, inputCodec), key).toString(),
|
||||
decrypt: (cipher, key) =>
|
||||
wordArrayToStr(CryptoJS.Rabbit.decrypt(cipher, key)),
|
||||
};
|
5
plugin/lib/quickcomposer.js
Normal file
5
plugin/lib/quickcomposer.js
Normal file
@ -0,0 +1,5 @@
|
||||
const quickcomposer = {
|
||||
textProcessing: require("./quickcomposer/textProcessing"),
|
||||
};
|
||||
|
||||
module.exports = quickcomposer;
|
383
plugin/lib/quickcomposer/textProcessing.js
Normal file
383
plugin/lib/quickcomposer/textProcessing.js
Normal file
@ -0,0 +1,383 @@
|
||||
const sm2 = require("sm-crypto").sm2;
|
||||
const sm3 = require("sm-crypto").sm3;
|
||||
const sm4 = require("sm-crypto").sm4;
|
||||
const CryptoJS = require("crypto-js");
|
||||
const NodeForge = require("node-forge");
|
||||
|
||||
// 数据编码转换
|
||||
const dataConv = (str, fromCodec, toCodec) => {
|
||||
// 特殊处理 PEM 格式
|
||||
if (fromCodec.toLowerCase() === "pem") {
|
||||
const pemContent = str
|
||||
.replace(/-----(BEGIN|END)[^-]+-----/g, "")
|
||||
.replace(/[\r\n]/g, "");
|
||||
return Buffer.from(pemContent, "base64").toString(toCodec.toLowerCase());
|
||||
}
|
||||
// 其他格式直接转换
|
||||
return Buffer.from(str, fromCodec.toLowerCase()).toString(
|
||||
toCodec.toLowerCase()
|
||||
);
|
||||
};
|
||||
|
||||
// 处理密钥和IV
|
||||
const processSecret = (key, codec, len) => {
|
||||
// 转换成 hex 并填充到指定长度
|
||||
const hexStr = dataConv(key, codec, "hex")
|
||||
.padEnd(len * 2, "0")
|
||||
.slice(0, len * 2);
|
||||
return CryptoJS.enc.Hex.parse(hexStr);
|
||||
};
|
||||
|
||||
const textProcessing = {
|
||||
// base64 编码
|
||||
base64Encode: function (text) {
|
||||
return dataConv(text, "utf8", "base64");
|
||||
},
|
||||
// base64 解码
|
||||
base64Decode: function (text) {
|
||||
return dataConv(text, "base64", "utf8");
|
||||
},
|
||||
// URL 编码
|
||||
urlEncode: function (text) {
|
||||
return encodeURIComponent(text);
|
||||
},
|
||||
// URL 解码
|
||||
urlDecode: function (text) {
|
||||
return decodeURIComponent(text);
|
||||
},
|
||||
// html 编码
|
||||
htmlEncode: function (text) {
|
||||
return text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
},
|
||||
// html 解码
|
||||
htmlDecode: function (text) {
|
||||
return text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'");
|
||||
},
|
||||
// 十六进制
|
||||
hexEncode: function (text) {
|
||||
return dataConv(text, "utf8", "hex");
|
||||
},
|
||||
// 十六进制解码
|
||||
hexDecode: function (text) {
|
||||
return dataConv(text, "hex", "utf8");
|
||||
},
|
||||
// 对称加解密
|
||||
symmetricCrypto: function (config) {
|
||||
const {
|
||||
text,
|
||||
algorithm,
|
||||
mode = "CBC",
|
||||
padding = "Pkcs7",
|
||||
key = { value: "", codec: "Utf8" },
|
||||
keyLength = 128,
|
||||
operation = "encrypt",
|
||||
format = "Base64",
|
||||
iv = { value: "", codec: "Utf8" },
|
||||
} = config;
|
||||
|
||||
// 处理密钥和IV
|
||||
const processedKey = processSecret(key.value, key.codec, keyLength / 8);
|
||||
const processedIV =
|
||||
mode === "ECB"
|
||||
? undefined
|
||||
: processSecret(iv?.value || key.value, iv?.codec || key.codec, 16);
|
||||
|
||||
// SM4 使用专门的库
|
||||
if (algorithm === "SM4") {
|
||||
const hexKey = processedKey.toString();
|
||||
// 处理输入文本格式
|
||||
let inputText = text;
|
||||
if (operation === "decrypt") {
|
||||
inputText =
|
||||
format === "Base64"
|
||||
? CryptoJS.enc.Base64.parse(text).toString(CryptoJS.enc.Hex)
|
||||
: text;
|
||||
}
|
||||
|
||||
if (mode === "CBC") {
|
||||
const hexIv = processedIV ? processedIV.toString() : "0".repeat(32);
|
||||
const result =
|
||||
operation === "encrypt"
|
||||
? sm4.encrypt(inputText, hexKey, {
|
||||
mode: "cbc",
|
||||
iv: hexIv,
|
||||
padding: padding.toLowerCase(),
|
||||
})
|
||||
: sm4.decrypt(inputText, hexKey, {
|
||||
mode: "cbc",
|
||||
iv: hexIv,
|
||||
padding: padding.toLowerCase(),
|
||||
});
|
||||
// 处理输出格式
|
||||
return operation === "encrypt" && format === "Base64"
|
||||
? CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(result))
|
||||
: result;
|
||||
}
|
||||
|
||||
const result =
|
||||
operation === "encrypt"
|
||||
? sm4.encrypt(inputText, hexKey, {
|
||||
padding: padding.toLowerCase(),
|
||||
})
|
||||
: sm4.decrypt(inputText, hexKey, {
|
||||
padding: padding.toLowerCase(),
|
||||
});
|
||||
// 处理输出格式
|
||||
return operation === "encrypt" && format === "Base64"
|
||||
? CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(result))
|
||||
: result;
|
||||
}
|
||||
|
||||
// 准备加密配置
|
||||
const cryptoConfig = {
|
||||
mode: CryptoJS.mode[mode],
|
||||
padding: CryptoJS.pad[padding],
|
||||
};
|
||||
|
||||
// 添加 IV(如果需要)
|
||||
if (mode !== "ECB" && processedIV) {
|
||||
cryptoConfig.iv = processedIV;
|
||||
}
|
||||
|
||||
// 加密/解密操作
|
||||
if (operation === "encrypt") {
|
||||
let encrypted;
|
||||
const inputText = CryptoJS.enc.Utf8.parse(text);
|
||||
|
||||
switch (algorithm) {
|
||||
case "AES":
|
||||
if (mode === "GCM") {
|
||||
const cipher = NodeForge.cipher.createCipher(
|
||||
"AES-GCM",
|
||||
processedKey.toString()
|
||||
);
|
||||
cipher.start({
|
||||
iv: cryptoConfig.iv ? cryptoConfig.iv.toString() : "",
|
||||
tagLength: 128,
|
||||
});
|
||||
cipher.update(NodeForge.util.createBuffer(text, "utf8"));
|
||||
cipher.finish();
|
||||
return {
|
||||
enc: cipher.output.toHex(),
|
||||
tag: cipher.mode.tag.toHex(),
|
||||
};
|
||||
}
|
||||
encrypted = CryptoJS.AES.encrypt(
|
||||
inputText,
|
||||
processedKey,
|
||||
cryptoConfig
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw "不支持的算法";
|
||||
}
|
||||
return encrypted.ciphertext.toString(CryptoJS.enc[format]);
|
||||
} else {
|
||||
// 解密
|
||||
if (algorithm === "AES" && mode === "GCM") {
|
||||
try {
|
||||
const { enc, tag } = JSON.parse(text);
|
||||
const decipher = NodeForge.cipher.createDecipher(
|
||||
"AES-GCM",
|
||||
processedKey.toString()
|
||||
);
|
||||
decipher.start({
|
||||
iv: cryptoConfig.iv ? cryptoConfig.iv.toString() : "",
|
||||
tag: NodeForge.util.createBuffer(tag, "hex"),
|
||||
});
|
||||
decipher.update(NodeForge.util.createBuffer(enc, "hex"));
|
||||
const pass = decipher.finish();
|
||||
return pass ? decipher.output.toString() : null;
|
||||
} catch (e) {
|
||||
throw "解密失败";
|
||||
}
|
||||
}
|
||||
|
||||
// 将输入转换为 CipherParams 格式
|
||||
const ciphertext = CryptoJS.enc[format].parse(text);
|
||||
const cipherParams = CryptoJS.lib.CipherParams.create({
|
||||
ciphertext: ciphertext,
|
||||
});
|
||||
|
||||
let decrypted;
|
||||
switch (algorithm) {
|
||||
case "AES":
|
||||
decrypted = CryptoJS.AES.decrypt(
|
||||
cipherParams,
|
||||
processedKey,
|
||||
cryptoConfig
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw "不支持的算法";
|
||||
}
|
||||
return CryptoJS.enc.Utf8.stringify(decrypted);
|
||||
}
|
||||
},
|
||||
// RSA 加密
|
||||
rsaEncrypt: function (text, key) {
|
||||
return crypto.publicEncrypt(key, Buffer.from(text)).toString("base64");
|
||||
},
|
||||
// RSA 解密
|
||||
rsaDecrypt: function (text, key) {
|
||||
return crypto.privateDecrypt(key, Buffer.from(text, "base64")).toString();
|
||||
},
|
||||
// SM4 加密
|
||||
sm4Encrypt: function (text, key) {
|
||||
// 将密钥转换为 16 进制字符串
|
||||
const hexKey = Buffer.from(key).toString("hex");
|
||||
return sm4.encrypt(text, hexKey);
|
||||
},
|
||||
// SM4 解密
|
||||
sm4Decrypt: function (text, key) {
|
||||
// 将密钥转换为 16 进制字符串
|
||||
const hexKey = Buffer.from(key).toString("hex");
|
||||
return sm4.decrypt(text, hexKey);
|
||||
},
|
||||
// SM2 加密
|
||||
sm2Encrypt: function (text, key) {
|
||||
return sm2.encrypt(text, key);
|
||||
},
|
||||
// SM2 解密
|
||||
sm2Decrypt: function (text, key) {
|
||||
return sm2.decrypt(text, key);
|
||||
},
|
||||
// MD5 哈希
|
||||
md5Hash: function (text) {
|
||||
return crypto.createHash("md5").update(text).digest("hex");
|
||||
},
|
||||
// SHA256 哈希
|
||||
sha256Hash: function (text) {
|
||||
return crypto.createHash("sha256").update(text).digest("hex");
|
||||
},
|
||||
// SM3 哈希
|
||||
sm3Hash: function (text) {
|
||||
return sm3(text);
|
||||
},
|
||||
// 字符串反转
|
||||
reverseString: function (text) {
|
||||
return text.split("").reverse().join("");
|
||||
},
|
||||
// 字符串替换
|
||||
replaceString: function (text, oldStr, newStr) {
|
||||
return text.replace(oldStr, newStr);
|
||||
},
|
||||
// 字符串截取
|
||||
substring: function (text, start, end) {
|
||||
return text.substring(start, end);
|
||||
},
|
||||
// 正则提取
|
||||
regexExtract: function (text, regex) {
|
||||
const match = text.match(regex);
|
||||
return match ? match[0] : "";
|
||||
},
|
||||
// 非对称加解密
|
||||
asymmetricCrypto: function (config) {
|
||||
const {
|
||||
text,
|
||||
algorithm,
|
||||
operation,
|
||||
format = "Base64",
|
||||
publicKey = { key: "", codec: "Pem" },
|
||||
privateKey = { key: "", codec: "Pem" },
|
||||
padding = "RSAES-PKCS1-V1_5",
|
||||
cipherMode = 1,
|
||||
} = config;
|
||||
|
||||
if (algorithm === "SM2") {
|
||||
if (operation === "encrypt") {
|
||||
if (!publicKey.key) throw "缺少公钥";
|
||||
// 转换公钥格式
|
||||
const hexPubKey =
|
||||
publicKey.codec === "Hex"
|
||||
? publicKey.key
|
||||
: dataConv(publicKey.key, publicKey.codec, "Hex");
|
||||
// 加密
|
||||
const cipher = sm2.doEncrypt(text, hexPubKey, cipherMode);
|
||||
// 转换输出格式
|
||||
return format === "Base64" ? dataConv(cipher, "Hex", "Base64") : cipher;
|
||||
} else {
|
||||
if (!privateKey.key) throw "缺少私钥";
|
||||
// 转换私钥格式
|
||||
const hexPriKey =
|
||||
privateKey.codec === "Hex"
|
||||
? privateKey.key
|
||||
: dataConv(privateKey.key, privateKey.codec, "Hex");
|
||||
// 转换输入格式
|
||||
const hexCipher =
|
||||
format === "Base64" ? dataConv(text, "Base64", "Hex") : text;
|
||||
// 解密
|
||||
const msg = sm2.doDecrypt(hexCipher, hexPriKey, cipherMode, {
|
||||
output: "utf8",
|
||||
});
|
||||
if (!msg) throw "解密失败";
|
||||
return msg;
|
||||
}
|
||||
} else if (algorithm === "RSA") {
|
||||
if (operation === "encrypt") {
|
||||
if (!publicKey.key) throw "缺少公钥";
|
||||
// 转换公钥格式
|
||||
let formattedPubKey = publicKey.key;
|
||||
if (publicKey.codec !== "Pem") {
|
||||
formattedPubKey =
|
||||
"-----BEGIN RSA PUBLIC KEY-----\n" +
|
||||
dataConv(publicKey.key, publicKey.codec, "Base64") +
|
||||
"\n-----END RSA PUBLIC KEY-----";
|
||||
}
|
||||
formattedPubKey = formattedPubKey.replace(/\\n/g, "\n");
|
||||
|
||||
// 创建 RSA 公钥对象
|
||||
const publicKeyObj = NodeForge.pki.publicKeyFromPem(formattedPubKey);
|
||||
// 将文本转换为二进制数据
|
||||
const binaryData = NodeForge.util.encodeUtf8(text);
|
||||
// 使用指定的填充方式加密
|
||||
const encrypted = publicKeyObj.encrypt(binaryData, padding);
|
||||
// 转换输出格式
|
||||
return format === "Base64"
|
||||
? dataConv(encrypted, "binary", "Base64")
|
||||
: dataConv(encrypted, "binary", "Hex");
|
||||
} else {
|
||||
if (!privateKey.key) throw "缺少私钥";
|
||||
// 转换私钥格式
|
||||
let formattedPriKey = privateKey.key;
|
||||
if (privateKey.codec !== "Pem") {
|
||||
formattedPriKey =
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n" +
|
||||
dataConv(privateKey.key, privateKey.codec, "Base64") +
|
||||
"\n-----END RSA PRIVATE KEY-----";
|
||||
}
|
||||
formattedPriKey = formattedPriKey.replace(/\\n/g, "\n");
|
||||
|
||||
// 创建 RSA 私钥对象
|
||||
const privateKeyObj = NodeForge.pki.privateKeyFromPem(formattedPriKey);
|
||||
// 转换输入格式
|
||||
const binary =
|
||||
format === "Base64"
|
||||
? dataConv(text, "Base64", "binary")
|
||||
: dataConv(text, "Hex", "binary");
|
||||
// 解密
|
||||
try {
|
||||
const decrypted = privateKeyObj.decrypt(binary, padding);
|
||||
// 将二进制数据转换回文本
|
||||
return NodeForge.util.decodeUtf8(decrypted);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw "解密失败";
|
||||
}
|
||||
}
|
||||
}
|
||||
throw "不支持的算法";
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = textProcessing;
|
57
plugin/package-lock.json
generated
57
plugin/package-lock.json
generated
@ -4,13 +4,15 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "plugin",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"jimp": "^0.22.12",
|
||||
"lodash": "^4.17.21",
|
||||
"node-forge": "^1.3.1",
|
||||
"ses": "^1.10.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"tree-kill": "^1.2.2"
|
||||
}
|
||||
},
|
||||
@ -554,6 +556,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@ -724,6 +732,12 @@
|
||||
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/load-bmfont": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/load-bmfont/-/load-bmfont-1.4.2.tgz",
|
||||
@ -806,6 +820,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
||||
"license": "(BSD-3-Clause OR GPL-2.0)",
|
||||
"engines": {
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/omggif": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/omggif/-/omggif-1.0.10.tgz",
|
||||
@ -992,6 +1015,15 @@
|
||||
"@endo/env-options": "^1.1.8"
|
||||
}
|
||||
},
|
||||
"node_modules/sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@ -1512,6 +1544,11 @@
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@ -1626,6 +1663,11 @@
|
||||
"resolved": "https://registry.npmmirror.com/jpeg-js/-/jpeg-js-0.4.4.tgz",
|
||||
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
|
||||
},
|
||||
"load-bmfont": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/load-bmfont/-/load-bmfont-1.4.2.tgz",
|
||||
@ -1680,6 +1722,11 @@
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
},
|
||||
"omggif": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/omggif/-/omggif-1.0.10.tgz",
|
||||
@ -1803,6 +1850,14 @@
|
||||
"@endo/env-options": "^1.1.8"
|
||||
}
|
||||
},
|
||||
"sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
|
||||
"requires": {
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
|
@ -1,10 +1,13 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"jimp": "^0.22.12",
|
||||
"lodash": "^4.17.21",
|
||||
"node-forge": "^1.3.1",
|
||||
"ses": "^1.10.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"tree-kill": "^1.2.2"
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ window.convertFilePathToUtoolsPayload = convertFilePathToUtoolsPayload;
|
||||
|
||||
window.getuToolsLite = require("./lib/utoolsLite");
|
||||
window.quickcommand = require("./lib/quickcommand");
|
||||
window.quickcomposer = require("./lib/quickcomposer");
|
||||
window.getQuickcommandTempFile = require("./lib/getQuickcommandTempFile");
|
||||
window.imageProcessor = require("./lib/imageprocessor");
|
||||
window.showUb = require("./lib/showDocs");
|
||||
@ -97,6 +98,7 @@ window.removeHtmlTags = (value) => {
|
||||
return quickcommand.htmlParse(value).querySelector("body").innerText;
|
||||
};
|
||||
|
||||
|
||||
window.hexEncode = (text) => Buffer.from(text, "utf8").toString("hex");
|
||||
window.hexDecode = (text) => Buffer.from(text, "hex").toString("utf8");
|
||||
window.base64Decode = (text) => Buffer.from(text, "base64").toString("utf8");
|
||||
@ -123,6 +125,7 @@ let getSandboxFuns = () => {
|
||||
path,
|
||||
os,
|
||||
child_process,
|
||||
quickcomposer,
|
||||
};
|
||||
Object.keys(shortCodes).forEach((f) => {
|
||||
sandbox[f] = shortCodes[f];
|
||||
|
@ -163,16 +163,23 @@ export default defineComponent({
|
||||
|
||||
/* 布局更加紧凑 */
|
||||
/* 输入框高度及字体 */
|
||||
.command-composer :deep(.q-field--filled .q-field__control),
|
||||
.command-composer :deep(.q-field--filled .q-field__control > *),
|
||||
.command-composer :deep(.q-field--filled:not(.q-textarea) .q-field__control),
|
||||
.command-composer
|
||||
:deep(.q-field--filled:not(.q-field--labeled) .q-field__native) {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
:deep(.q-field--filled:not(.q-textarea) .q-field__control > *),
|
||||
.command-composer
|
||||
:deep(.q-field--filled:not(.q-field--labeled):not(.q-textarea)
|
||||
.q-field__native) {
|
||||
max-height: 36px !important;
|
||||
min-height: 36px !important;
|
||||
}
|
||||
|
||||
.command-composer :deep(.q-field--filled .q-field__control),
|
||||
.command-composer :deep(.q-field--filled .q-field__control > *),
|
||||
.command-composer :deep(.q-field--filled .q-field__native) {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 输入框图标大小 */
|
||||
.command-composer :deep(.q-field--filled .q-field__control .q-icon) {
|
||||
font-size: 18px;
|
||||
|
@ -130,6 +130,8 @@ import KeyEditor from "./KeyEditor.vue";
|
||||
import UBrowserEditor from "./ubrowser/UBrowserEditor.vue";
|
||||
import VariableInput from "./VariableInput.vue";
|
||||
import AxiosConfigEditor from "./http/AxiosConfigEditor.vue";
|
||||
import SymmetricCryptoEditor from "./crypto/SymmetricCryptoEditor.vue";
|
||||
import AsymmetricCryptoEditor from "./crypto/AsymmetricCryptoEditor.vue";
|
||||
import { validateVariableName } from "js/common/variableValidator";
|
||||
|
||||
export default defineComponent({
|
||||
@ -139,6 +141,8 @@ export default defineComponent({
|
||||
UBrowserEditor,
|
||||
VariableInput,
|
||||
AxiosConfigEditor,
|
||||
SymmetricCryptoEditor,
|
||||
AsymmetricCryptoEditor,
|
||||
},
|
||||
props: {
|
||||
command: {
|
||||
@ -351,7 +355,7 @@ export default defineComponent({
|
||||
|
||||
.command-item:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-1px);
|
||||
/* transform: translateY(-1px); */
|
||||
}
|
||||
|
||||
.composer-card :deep(.q-field__label) {
|
||||
|
398
src/components/editor/composer/crypto/AsymmetricCryptoEditor.vue
Normal file
398
src/components/editor/composer/crypto/AsymmetricCryptoEditor.vue
Normal file
@ -0,0 +1,398 @@
|
||||
<template>
|
||||
<div class="asymmetric-crypto-editor">
|
||||
<!-- 加密/解密切换 -->
|
||||
<q-btn-toggle
|
||||
v-model="operation"
|
||||
:options="[
|
||||
{ label: '加密', value: 'encrypt' },
|
||||
{ label: '解密', value: 'decrypt' },
|
||||
]"
|
||||
spread
|
||||
dense
|
||||
no-caps
|
||||
unelevated
|
||||
toggle-color="primary"
|
||||
/>
|
||||
|
||||
<!-- 文本输入 -->
|
||||
<div class="row">
|
||||
<VariableInput
|
||||
v-model="text"
|
||||
:label="operation === 'encrypt' ? '要加密的文本' : '要解密的文本'"
|
||||
:command="{
|
||||
icon:
|
||||
operation === 'encrypt' ? 'enhanced_encryption' : 'no_encryption',
|
||||
}"
|
||||
class="col-12"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 算法选择 -->
|
||||
<q-select
|
||||
v-model="algorithm"
|
||||
:options="algorithms"
|
||||
label="加密算法"
|
||||
dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
<!-- RSA填充选择 -->
|
||||
<q-select
|
||||
v-if="algorithm === 'RSA'"
|
||||
v-model="padding"
|
||||
:options="paddings"
|
||||
label="填充方式"
|
||||
dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
<!-- SM2密文格式选择 -->
|
||||
<q-select
|
||||
v-if="algorithm === 'SM2'"
|
||||
v-model="cipherMode"
|
||||
:options="[
|
||||
{ label: 'C1C3C2', value: 1 },
|
||||
{ label: 'C1C2C3', value: 0 },
|
||||
]"
|
||||
label="密文格式"
|
||||
dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
<!-- 格式选择 -->
|
||||
<q-select
|
||||
v-model="format"
|
||||
:options="operation === 'encrypt' ? outputFormats : inputFormats"
|
||||
:label="operation === 'encrypt' ? '输出格式' : '输入格式'"
|
||||
dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 密钥输入区域 -->
|
||||
<div class="col-6 key-input">
|
||||
<div class="key-wrapper">
|
||||
<q-input
|
||||
v-model="publicKey"
|
||||
type="textarea"
|
||||
filled
|
||||
autogrow
|
||||
label="公钥"
|
||||
class="key-textarea"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
<q-btn-dropdown
|
||||
flat
|
||||
dense
|
||||
:label="publicKeyCodec"
|
||||
class="codec-dropdown"
|
||||
>
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="codec in keyCodecs"
|
||||
:key="codec.value"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="publicKeyCodec = codec.value"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ codec.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 key-input">
|
||||
<div class="key-wrapper">
|
||||
<q-input
|
||||
v-model="privateKey"
|
||||
type="textarea"
|
||||
filled
|
||||
autogrow
|
||||
label="私钥"
|
||||
class="key-textarea"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
<q-btn-dropdown
|
||||
flat
|
||||
dense
|
||||
:label="privateKeyCodec"
|
||||
class="codec-dropdown"
|
||||
>
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="codec in keyCodecs"
|
||||
:key="codec.value"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="privateKeyCodec = codec.value"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ codec.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "../VariableInput.vue";
|
||||
import { formatJsonVariables } from "js/composer/formatString";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AsymmetricCryptoEditor",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operation: "encrypt",
|
||||
text: "",
|
||||
algorithm: "RSA",
|
||||
padding: "RSAES-PKCS1-V1_5",
|
||||
cipherMode: 1,
|
||||
publicKey: "",
|
||||
privateKey: "",
|
||||
publicKeyCodec: "Pem",
|
||||
privateKeyCodec: "Pem",
|
||||
format: "Base64",
|
||||
algorithms: [
|
||||
{ label: "RSA", value: "RSA" },
|
||||
{ label: "SM2", value: "SM2" },
|
||||
],
|
||||
paddings: [
|
||||
{ label: "PKCS#1 v1.5", value: "RSAES-PKCS1-V1_5" },
|
||||
{ label: "OAEP", value: "RSA-OAEP" },
|
||||
{ label: "OAEP/SHA-256", value: "RSA-OAEP-256" },
|
||||
],
|
||||
keyCodecs: [
|
||||
{ label: "PEM", value: "Pem" },
|
||||
{ label: "Base64", value: "Base64" },
|
||||
{ label: "Hex", value: "Hex" },
|
||||
],
|
||||
outputFormats: [
|
||||
{ label: "Base64", value: "Base64" },
|
||||
{ label: "Hex", value: "Hex" },
|
||||
],
|
||||
inputFormats: [
|
||||
{ label: "Base64", value: "Base64" },
|
||||
{ label: "Hex", value: "Hex" },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateConfig() {
|
||||
const code = `quickcomposer.textProcessing.asymmetricCrypto(${formatJsonVariables(
|
||||
{
|
||||
text: this.text,
|
||||
algorithm: this.algorithm,
|
||||
operation: this.operation,
|
||||
format: this.format,
|
||||
publicKey: {
|
||||
key: this.publicKey,
|
||||
codec: this.publicKeyCodec,
|
||||
},
|
||||
privateKey: {
|
||||
key: this.privateKey,
|
||||
codec: this.privateKeyCodec,
|
||||
},
|
||||
padding: this.algorithm === "RSA" ? this.padding : undefined,
|
||||
cipherMode: this.algorithm === "SM2" ? this.cipherMode : undefined,
|
||||
},
|
||||
["text"]
|
||||
)})`;
|
||||
|
||||
this.$emit("update:model-value", code);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
operation() {
|
||||
this.format = "Base64";
|
||||
this.updateConfig();
|
||||
},
|
||||
algorithm() {
|
||||
if (this.algorithm === "RSA") {
|
||||
this.padding = "PKCS#1";
|
||||
} else {
|
||||
this.cipherMode = 1;
|
||||
}
|
||||
this.updateConfig();
|
||||
},
|
||||
// 监听所有可能改变的值
|
||||
text() {
|
||||
this.updateConfig();
|
||||
},
|
||||
padding() {
|
||||
this.updateConfig();
|
||||
},
|
||||
cipherMode() {
|
||||
this.updateConfig();
|
||||
},
|
||||
publicKey() {
|
||||
this.updateConfig();
|
||||
},
|
||||
privateKey() {
|
||||
this.updateConfig();
|
||||
},
|
||||
publicKeyCodec() {
|
||||
this.updateConfig();
|
||||
},
|
||||
privateKeyCodec() {
|
||||
this.updateConfig();
|
||||
},
|
||||
format() {
|
||||
this.updateConfig();
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.asymmetric-crypto-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.col-grow {
|
||||
flex: 1 1 0;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
/* 密钥输入区域样式 */
|
||||
.key-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: calc(50% - 8px);
|
||||
max-width: calc(50% - 8px);
|
||||
}
|
||||
|
||||
.key-wrapper {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.key-textarea {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 确保输入框占满容器 */
|
||||
.key-textarea :deep(.q-field) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 确保文本区域占满输入框 */
|
||||
.key-textarea :deep(.q-field__native) {
|
||||
min-height: 120px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/* 编码选择下拉按钮样式 */
|
||||
.codec-dropdown {
|
||||
min-width: 45px;
|
||||
max-width: 45px;
|
||||
font-size: 10px;
|
||||
padding: 2px 4px;
|
||||
height: 20px;
|
||||
line-height: 16px;
|
||||
border-radius: 4px;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
/* 下拉菜单项样式 */
|
||||
.codec-dropdown :deep(.q-btn-dropdown__arrow) {
|
||||
font-size: 10px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.codec-dropdown :deep(.q-list) {
|
||||
min-width: 60px;
|
||||
background: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.codec-dropdown :deep(.q-item) {
|
||||
min-height: 24px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.codec-dropdown :deep(.q-item__label) {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.body--dark .codec-dropdown {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.body--dark .codec-dropdown :deep(.q-list) {
|
||||
background: #1d1d1d;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 确保选择器行在空间不够时换行美观 */
|
||||
@media (max-width: 600px) {
|
||||
.col-grow {
|
||||
flex: 1 1 calc(50% - 8px);
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.key-input {
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.q-btn-toggle {
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.body--dark .q-btn-toggle {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
/* 确保下拉按钮内容垂直居中 */
|
||||
.codec-dropdown :deep(.q-btn__content) {
|
||||
min-height: unset;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 调整下拉按钮的内容间距 */
|
||||
.codec-dropdown :deep(.q-btn__wrapper) {
|
||||
padding: 0 4px;
|
||||
min-height: unset;
|
||||
}
|
||||
</style>
|
432
src/components/editor/composer/crypto/SymmetricCryptoEditor.vue
Normal file
432
src/components/editor/composer/crypto/SymmetricCryptoEditor.vue
Normal file
@ -0,0 +1,432 @@
|
||||
<template>
|
||||
<div class="symmetric-crypto-editor q-gutter-y-sm">
|
||||
<!-- 加密/解密切换 -->
|
||||
<q-btn-toggle
|
||||
v-model="operation"
|
||||
:options="[
|
||||
{ label: '加密', value: 'encrypt' },
|
||||
{ label: '解密', value: 'decrypt' },
|
||||
]"
|
||||
spread
|
||||
dense
|
||||
no-caps
|
||||
unelevated
|
||||
toggle-color="primary"
|
||||
/>
|
||||
|
||||
<!-- 文本输入 -->
|
||||
<div class="row">
|
||||
<VariableInput
|
||||
v-model="text"
|
||||
:label="operation === 'encrypt' ? '要加密的文本' : '要解密的文本'"
|
||||
:command="{
|
||||
icon:
|
||||
operation === 'encrypt' ? 'enhanced_encryption' : 'no_encryption',
|
||||
}"
|
||||
class="col-8"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
<q-select
|
||||
v-model="format"
|
||||
:options="operation === 'encrypt' ? outputFormats : inputFormats"
|
||||
:label="operation === 'encrypt' ? '输出格式' : '输入格式'"
|
||||
dense
|
||||
filled
|
||||
class="col-4"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 算法选择 -->
|
||||
<q-select
|
||||
v-model="algorithm"
|
||||
:options="algorithms"
|
||||
label="加密算法"
|
||||
dense
|
||||
filled
|
||||
class="col-select"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
<!-- AES密钥长度选择 -->
|
||||
<q-select
|
||||
v-if="showKeyLength"
|
||||
v-model="keyLength"
|
||||
:options="[
|
||||
{ label: '128位', value: 128 },
|
||||
{ label: '192位', value: 192 },
|
||||
{ label: '256位', value: 256 },
|
||||
]"
|
||||
label="密钥长度"
|
||||
dense
|
||||
filled
|
||||
class="col-select"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
<!-- 模式选择 -->
|
||||
<q-select
|
||||
v-model="mode"
|
||||
:options="modes"
|
||||
label="加密模式"
|
||||
dense
|
||||
filled
|
||||
class="col-select"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
<!-- Padding选择 -->
|
||||
<q-select
|
||||
v-model="padding"
|
||||
:options="paddings"
|
||||
label="填充方式"
|
||||
dense
|
||||
filled
|
||||
class="col-select"
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 密钥输入区域 -->
|
||||
<div class="col-grow key-input">
|
||||
<div class="key-wrapper">
|
||||
<q-input
|
||||
v-model="key"
|
||||
filled
|
||||
label="密钥"
|
||||
class="key-input"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
<q-btn-dropdown flat dense :label="keyCodec" class="codec-dropdown">
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="codec in keyCodecs"
|
||||
:key="codec.value"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="keyCodec = codec.value"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ codec.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<!-- IV输入区域 -->
|
||||
<div v-if="showIV" class="col-grow key-input">
|
||||
<div class="key-wrapper">
|
||||
<q-input
|
||||
v-model="iv"
|
||||
filled
|
||||
label="IV"
|
||||
class="key-input"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
<q-btn-dropdown flat dense :label="ivCodec" class="codec-dropdown">
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="codec in keyCodecs"
|
||||
:key="codec.value"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="ivCodec = codec.value"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ codec.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "../VariableInput.vue";
|
||||
import { formatJsonVariables } from "js/composer/formatString";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SymmetricCryptoEditor",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Object],
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operation: "encrypt",
|
||||
text: "",
|
||||
algorithm: "AES",
|
||||
keyLength: 128,
|
||||
mode: "CBC",
|
||||
padding: "Pkcs7",
|
||||
key: "",
|
||||
keyCodec: "Utf8",
|
||||
iv: "",
|
||||
ivCodec: "Utf8",
|
||||
format: "Base64",
|
||||
keyCodecs: [
|
||||
{ label: "UTF-8", value: "Utf8" },
|
||||
{ label: "Base64", value: "Base64" },
|
||||
{ label: "Hex", value: "Hex" },
|
||||
],
|
||||
algorithms: [
|
||||
{ label: "AES", value: "AES" },
|
||||
{ label: "SM4", value: "SM4" },
|
||||
],
|
||||
outputFormats: [
|
||||
{ label: "Base64", value: "Base64" },
|
||||
{ label: "Hex", value: "Hex" },
|
||||
],
|
||||
inputFormats: [
|
||||
{ label: "Base64", value: "Base64" },
|
||||
{ label: "Hex", value: "Hex" },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
modes() {
|
||||
// SM4 只支持 ECB/CBC
|
||||
if (this.algorithm === "SM4") {
|
||||
return [
|
||||
{ label: "ECB", value: "ECB" },
|
||||
{ label: "CBC", value: "CBC" },
|
||||
];
|
||||
}
|
||||
// AES/DES/3DES 支持更多模式
|
||||
return [
|
||||
{ label: "ECB", value: "ECB" },
|
||||
{ label: "CBC", value: "CBC" },
|
||||
{ label: "CFB", value: "CFB" },
|
||||
{ label: "OFB", value: "OFB" },
|
||||
{ label: "CTR", value: "CTR" },
|
||||
{ label: "GCM", value: "GCM" },
|
||||
];
|
||||
},
|
||||
paddings() {
|
||||
// SM4 支持的填充方式
|
||||
if (this.algorithm === "SM4") {
|
||||
return [
|
||||
{ label: "PKCS#7", value: "pkcs#7" },
|
||||
{ label: "None", value: "none" },
|
||||
];
|
||||
}
|
||||
// AES/DES/3DES 支持的填充方式
|
||||
return [
|
||||
{ label: "PKCS7", value: "Pkcs7" },
|
||||
{ label: "Zero Padding", value: "ZeroPadding" },
|
||||
{ label: "No Padding", value: "NoPadding" },
|
||||
{ label: "ISO-10126", value: "Iso10126" },
|
||||
{ label: "ANSI X.923", value: "AnsiX923" },
|
||||
{ label: "ISO-97971", value: "Iso97971" },
|
||||
];
|
||||
},
|
||||
showIV() {
|
||||
return this.mode !== "ECB";
|
||||
},
|
||||
showKeyLength() {
|
||||
return this.algorithm === "AES";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateConfig() {
|
||||
const code = `quickcomposer.textProcessing.symmetricCrypto(${formatJsonVariables(
|
||||
{
|
||||
text: this.text,
|
||||
algorithm: this.algorithm,
|
||||
mode: this.mode,
|
||||
padding: this.padding,
|
||||
key: {
|
||||
value: this.key,
|
||||
codec: this.keyCodec,
|
||||
},
|
||||
keyLength: this.keyLength,
|
||||
operation: this.operation,
|
||||
format: this.format,
|
||||
iv:
|
||||
this.mode !== "ECB"
|
||||
? {
|
||||
value: this.iv,
|
||||
codec: this.ivCodec,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
["text"]
|
||||
)})`;
|
||||
|
||||
this.$emit("update:model-value", code);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
operation() {
|
||||
// 切换操作时重置格式为默认值
|
||||
this.format = "Base64";
|
||||
this.updateConfig();
|
||||
},
|
||||
text() {
|
||||
this.updateConfig();
|
||||
},
|
||||
algorithm() {
|
||||
// 切换算法时重置模式和填充
|
||||
if (this.algorithm === "SM4") {
|
||||
this.mode = "ECB";
|
||||
this.padding = "pkcs#7";
|
||||
} else {
|
||||
this.mode = "CBC";
|
||||
this.padding = "Pkcs7";
|
||||
}
|
||||
this.updateConfig();
|
||||
},
|
||||
mode() {
|
||||
this.updateConfig();
|
||||
},
|
||||
padding() {
|
||||
this.updateConfig();
|
||||
},
|
||||
format() {
|
||||
this.updateConfig();
|
||||
},
|
||||
keyLength() {
|
||||
this.updateConfig();
|
||||
},
|
||||
key() {
|
||||
this.updateConfig();
|
||||
},
|
||||
iv() {
|
||||
this.updateConfig();
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.crypto-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.col-select {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.col-grow {
|
||||
flex: 1 1 0;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
/* 确保第一行的输入框和格式选择器的比例固定 */
|
||||
.row:first-of-type .col-8 {
|
||||
flex: 4;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.row:first-of-type .col-4 {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* 确保选择器行在空间不够时换行美观 */
|
||||
@media (max-width: 600px) {
|
||||
.col-select {
|
||||
flex: 1 1 calc(50% - 8px);
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.q-btn-toggle {
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.body--dark .q-btn-toggle {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.key-input {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.key-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 编码选择下拉按钮样式 */
|
||||
.codec-dropdown {
|
||||
min-width: 45px;
|
||||
max-width: 45px;
|
||||
font-size: 10px;
|
||||
padding: 2px 4px;
|
||||
height: 20px;
|
||||
line-height: 16px;
|
||||
border-radius: 4px;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
/* 下拉菜单项样式 */
|
||||
.codec-dropdown :deep(.q-btn-dropdown__arrow) {
|
||||
font-size: 10px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.codec-dropdown :deep(.q-list) {
|
||||
min-width: 60px;
|
||||
background: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.codec-dropdown :deep(.q-item) {
|
||||
min-height: 24px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.codec-dropdown :deep(.q-item__label) {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.body--dark .codec-dropdown {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.body--dark .codec-dropdown :deep(.q-list) {
|
||||
background: #1d1d1d;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 确保下拉按钮内容垂直居中 */
|
||||
.codec-dropdown :deep(.q-btn__content) {
|
||||
min-height: unset;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 调整下拉按钮的内容间距 */
|
||||
.codec-dropdown :deep(.q-btn__wrapper) {
|
||||
padding: 0 4px;
|
||||
min-height: unset;
|
||||
}
|
||||
</style>
|
@ -1,85 +0,0 @@
|
||||
export const encodeCommands = {
|
||||
label: "编码解码",
|
||||
icon: "code",
|
||||
defaultOpened: false,
|
||||
commands: [
|
||||
{
|
||||
value: "(text=>Buffer.from(text).toString('base64'))",
|
||||
label: "Base64编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "(text=>Buffer.from(text,'base64').toString())",
|
||||
label: "Base64解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的Base64文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock_open",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "(text=>Buffer.from(text).toString('hex'))",
|
||||
label: "十六进制编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "(text=>Buffer.from(text,'hex').toString())",
|
||||
label: "十六进制解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的十六进制文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock_open",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "encodeURIComponent",
|
||||
label: "URL编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "link",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "decodeURIComponent",
|
||||
label: "URL解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的URL编码文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "link_off",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -2,7 +2,7 @@ import { fileCommands } from "./fileCommands";
|
||||
import { networkCommands } from "./networkCommands";
|
||||
import { systemCommands } from "./systemCommands";
|
||||
import { notifyCommands } from "./notifyCommands";
|
||||
import { encodeCommands } from "./encodeCommands";
|
||||
import { textProcessingCommands } from "./textProcessingCommands";
|
||||
import { otherCommands } from "./otherCommands";
|
||||
import { keyCommands } from "./keyCommands";
|
||||
|
||||
@ -11,7 +11,7 @@ export const commandCategories = [
|
||||
networkCommands,
|
||||
systemCommands,
|
||||
notifyCommands,
|
||||
encodeCommands,
|
||||
textProcessingCommands,
|
||||
otherCommands,
|
||||
keyCommands,
|
||||
];
|
||||
|
249
src/js/composer/commands/textProcessingCommands.js
Normal file
249
src/js/composer/commands/textProcessingCommands.js
Normal file
@ -0,0 +1,249 @@
|
||||
export const textProcessingCommands = {
|
||||
label: "文本处理",
|
||||
icon: "code",
|
||||
defaultOpened: false,
|
||||
commands: [
|
||||
{
|
||||
value: "quickcomposer.textProcessing.base64Encode",
|
||||
label: "Base64编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.base64Decode",
|
||||
label: "Base64解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的Base64文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock_open",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.hexEncode",
|
||||
label: "十六进制编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.hexDecode",
|
||||
label: "十六进制解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的十六进制文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "lock_open",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.urlEncode",
|
||||
label: "URL编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "link",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.urlDecode",
|
||||
label: "URL解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的URL编码文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "link_off",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.htmlEncode",
|
||||
label: "HTML编码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要编码的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "code",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.htmlDecode",
|
||||
label: "HTML解码",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要解码的HTML文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "code_off",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.reverseString",
|
||||
label: "字符串反转",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要反转的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "swap_horiz",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.replaceString",
|
||||
label: "字符串替换",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "原始文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "text_fields",
|
||||
},
|
||||
{
|
||||
key: "oldStr",
|
||||
label: "要替换的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "find_replace",
|
||||
},
|
||||
{
|
||||
key: "newStr",
|
||||
label: "替换为",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "text_fields",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.substring",
|
||||
label: "字符串截取",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "原始文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "text_fields",
|
||||
},
|
||||
{
|
||||
key: "start",
|
||||
label: "起始位置",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
defaultValue: "0",
|
||||
icon: "first_page",
|
||||
},
|
||||
{
|
||||
key: "end",
|
||||
label: "结束位置",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
defaultValue: "",
|
||||
icon: "last_page",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.regexExtract",
|
||||
label: "正则提取",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "原始文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "text_fields",
|
||||
},
|
||||
{
|
||||
key: "regex",
|
||||
label: "正则表达式",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "regex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.symmetricCrypto",
|
||||
label: "对称加解密",
|
||||
component: "SymmetricCryptoEditor",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.asymmetricCrypto",
|
||||
label: "非对称加解密",
|
||||
component: "AsymmetricCryptoEditor",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.md5Hash",
|
||||
label: "MD5哈希",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要哈希的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "enhanced_encryption",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.sha256Hash",
|
||||
label: "SHA256哈希",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要哈希的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "enhanced_encryption",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.textProcessing.sm3Hash",
|
||||
label: "SM3哈希",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "要哈希的文本",
|
||||
type: "input",
|
||||
defaultValue: "",
|
||||
icon: "enhanced_encryption",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user