From 5e2994fe6d66854bce1968f6e966e2b3036f537a Mon Sep 17 00:00:00 2001 From: fofolee Date: Mon, 30 Dec 2024 18:37:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AF=E8=A7=86=E5=8C=96=E7=BC=96=E6=8E=92?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E6=9C=AC=E5=A4=84=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A0=86=E6=88=90=E5=92=8C=E9=9D=9E=E5=AF=B9?= =?UTF-8?q?=E7=A7=B0=E5=8A=A0=E8=A7=A3=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/lib/RSAEncryption.js | 150 ++++++ plugin/lib/ShangmiEncryption.js | 149 ++++++ plugin/lib/SymmetricEncryption.js | 280 ++++++++++++ plugin/lib/quickcomposer.js | 5 + plugin/lib/quickcomposer/textProcessing.js | 383 ++++++++++++++++ plugin/package-lock.json | 57 ++- plugin/package.json | 3 + plugin/preload.js | 3 + .../editor/composer/CommandComposer.vue | 17 +- .../editor/composer/ComposerCard.vue | 6 +- .../crypto/AsymmetricCryptoEditor.vue | 398 ++++++++++++++++ .../composer/crypto/SymmetricCryptoEditor.vue | 432 ++++++++++++++++++ src/js/composer/commands/encodeCommands.js | 85 ---- src/js/composer/commands/index.js | 4 +- .../commands/textProcessingCommands.js | 249 ++++++++++ 15 files changed, 2127 insertions(+), 94 deletions(-) create mode 100644 plugin/lib/RSAEncryption.js create mode 100644 plugin/lib/ShangmiEncryption.js create mode 100644 plugin/lib/SymmetricEncryption.js create mode 100644 plugin/lib/quickcomposer.js create mode 100644 plugin/lib/quickcomposer/textProcessing.js create mode 100644 src/components/editor/composer/crypto/AsymmetricCryptoEditor.vue create mode 100644 src/components/editor/composer/crypto/SymmetricCryptoEditor.vue delete mode 100644 src/js/composer/commands/encodeCommands.js create mode 100644 src/js/composer/commands/textProcessingCommands.js diff --git a/plugin/lib/RSAEncryption.js b/plugin/lib/RSAEncryption.js new file mode 100644 index 0000000..31ff949 --- /dev/null +++ b/plugin/lib/RSAEncryption.js @@ -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); + }, +}; diff --git a/plugin/lib/ShangmiEncryption.js b/plugin/lib/ShangmiEncryption.js new file mode 100644 index 0000000..42f4a9f --- /dev/null +++ b/plugin/lib/ShangmiEncryption.js @@ -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); + }, +}; diff --git a/plugin/lib/SymmetricEncryption.js b/plugin/lib/SymmetricEncryption.js new file mode 100644 index 0000000..b0c6b35 --- /dev/null +++ b/plugin/lib/SymmetricEncryption.js @@ -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)), +}; diff --git a/plugin/lib/quickcomposer.js b/plugin/lib/quickcomposer.js new file mode 100644 index 0000000..ceac8d6 --- /dev/null +++ b/plugin/lib/quickcomposer.js @@ -0,0 +1,5 @@ +const quickcomposer = { + textProcessing: require("./quickcomposer/textProcessing"), +}; + +module.exports = quickcomposer; diff --git a/plugin/lib/quickcomposer/textProcessing.js b/plugin/lib/quickcomposer/textProcessing.js new file mode 100644 index 0000000..34b0c54 --- /dev/null +++ b/plugin/lib/quickcomposer/textProcessing.js @@ -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, "'"); + }, + // 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; diff --git a/plugin/package-lock.json b/plugin/package-lock.json index ed45473..2e6c6eb 100644 --- a/plugin/package-lock.json +++ b/plugin/package-lock.json @@ -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", diff --git a/plugin/package.json b/plugin/package.json index 3a60acf..b051281 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -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" } } diff --git a/plugin/preload.js b/plugin/preload.js index fc91973..5d822d4 100644 --- a/plugin/preload.js +++ b/plugin/preload.js @@ -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]; diff --git a/src/components/editor/composer/CommandComposer.vue b/src/components/editor/composer/CommandComposer.vue index e585a55..4f4096d 100644 --- a/src/components/editor/composer/CommandComposer.vue +++ b/src/components/editor/composer/CommandComposer.vue @@ -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; diff --git a/src/components/editor/composer/ComposerCard.vue b/src/components/editor/composer/ComposerCard.vue index 8def119..859fea8 100644 --- a/src/components/editor/composer/ComposerCard.vue +++ b/src/components/editor/composer/ComposerCard.vue @@ -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) { diff --git a/src/components/editor/composer/crypto/AsymmetricCryptoEditor.vue b/src/components/editor/composer/crypto/AsymmetricCryptoEditor.vue new file mode 100644 index 0000000..154601e --- /dev/null +++ b/src/components/editor/composer/crypto/AsymmetricCryptoEditor.vue @@ -0,0 +1,398 @@ + + + + + diff --git a/src/components/editor/composer/crypto/SymmetricCryptoEditor.vue b/src/components/editor/composer/crypto/SymmetricCryptoEditor.vue new file mode 100644 index 0000000..3134a60 --- /dev/null +++ b/src/components/editor/composer/crypto/SymmetricCryptoEditor.vue @@ -0,0 +1,432 @@ + + + + + diff --git a/src/js/composer/commands/encodeCommands.js b/src/js/composer/commands/encodeCommands.js deleted file mode 100644 index a12be83..0000000 --- a/src/js/composer/commands/encodeCommands.js +++ /dev/null @@ -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", - }, - ], - }, - ], -}; diff --git a/src/js/composer/commands/index.js b/src/js/composer/commands/index.js index ea22846..a7c29f0 100644 --- a/src/js/composer/commands/index.js +++ b/src/js/composer/commands/index.js @@ -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, ]; diff --git a/src/js/composer/commands/textProcessingCommands.js b/src/js/composer/commands/textProcessingCommands.js new file mode 100644 index 0000000..4838c85 --- /dev/null +++ b/src/js/composer/commands/textProcessingCommands.js @@ -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", + }, + ], + }, + ], +};