// @ts-nocheck import CryptoES from '../deps/node_modules/crypto-es/lib/index.js' import JSEncrypt from '../deps/node_modules/jsencrypt/lib/index.js' function ensureSupportedCodec(codec: string): void { const value = codec.toLowerCase() if (value == 'utf8' || value == 'hex' || value == 'base64' || value == 'base64url' || value == 'latin1' || value == 'utf16') { return } throw new Error('不支持的编码类型') } function ensureDigestKind(kind: string): string { const value = kind.toLowerCase() if (value == 'md5' || value == 'sha1' || value == 'sha256' || value == 'sha512') { return value } throw new Error('不支持的摘要算法') } function ensureMacKind(kind: string): string { const value = kind.toLowerCase() if (value == 'sha1' || value == 'sha256' || value == 'sha512') { return value } throw new Error('不支持的HMAC算法') } function ensureAesMode(mode: string): any { const value = mode.toUpperCase() if (value == 'CBC') { return CryptoES.mode.CBC } if (value == 'CFB') { return CryptoES.mode.CFB } if (value == 'CTR') { return CryptoES.mode.CTR } if (value == 'CTRGLADMAN') { return CryptoES.mode.CTRGladman } if (value == 'ECB') { return CryptoES.mode.ECB } if (value == 'OFB') { return CryptoES.mode.OFB } throw new Error('不支持的AES模式') } function ensureAesPadding(padding: string): any { const value = padding.toUpperCase() if (value == 'PKCS7') { return CryptoES.pad.Pkcs7 } if (value == 'ANSI_X923') { return CryptoES.pad.AnsiX923 } if (value == 'ISO_10126') { return CryptoES.pad.Iso10126 } if (value == 'ISO_97971') { return CryptoES.pad.Iso97971 } if (value == 'NONE') { return CryptoES.pad.NoPadding } if (value == 'ZERO') { return CryptoES.pad.ZeroPadding } throw new Error('不支持的AES填充') } function ensureTripleDesMode(mode: string): any { const value = mode.toUpperCase() if (value == 'CBC') { return CryptoES.mode.CBC } if (value == 'ECB') { return CryptoES.mode.ECB } throw new Error('不支持的3DES模式') } function ensureDesMode(mode: string): any { const value = mode.toUpperCase() if (value == 'CBC') { return CryptoES.mode.CBC } if (value == 'CFB') { return CryptoES.mode.CFB } if (value == 'CTR') { return CryptoES.mode.CTR } if (value == 'CTRGLADMAN') { return CryptoES.mode.CTRGladman } if (value == 'ECB') { return CryptoES.mode.ECB } if (value == 'OFB') { return CryptoES.mode.OFB } throw new Error('不支持的DES模式') } function inferKeyLength(sourceLength: number, hint: number | null): number { if (hint != null) { if (hint == 16 || hint == 24 || hint == 32) { return hint } if (hint == 128) { return 16 } if (hint == 192) { return 24 } if (hint == 256) { return 32 } throw new Error('AES密钥长度无效') } if (sourceLength <= 16) { return 16 } if (sourceLength <= 24) { return 24 } return 32 } function clampBytes(source: Uint8Array, targetLength: number): Uint8Array { const output = new Uint8Array(targetLength) const actualLength = source.length < targetLength ? source.length : targetLength let index = 0 while (index < actualLength) { output[index] = source[index] index += 1 } return output } function textToUtf8Bytes(input: string): Uint8Array { const wordArray = CryptoES.enc.Utf8.parse(input) return wordArrayToBytes(wordArray) } function utf8BytesToText(input: Uint8Array): string { return bytesToWordArray(input).toString(CryptoES.enc.Utf8) } function bytesToWordArray(input: Uint8Array): any { const words: number[] = [] let index = 0 while (index < input.length) { let word = 0 let offset = 0 while (offset < 4 && index + offset < input.length) { word |= input[index + offset] << (24 - offset * 8) offset += 1 } words.push(word) index += 4 } return CryptoES.lib.WordArray.create(words, input.length) } function wordArrayToBytes(wordArray: any): Uint8Array { const sigBytes = wordArray.sigBytes const output = new Uint8Array(sigBytes) const words = wordArray.words let index = 0 while (index < sigBytes) { const wordIndex = Math.floor(index / 4) const byteOffset = index % 4 output[index] = (words[wordIndex] >>> (24 - byteOffset * 8)) & 0xff index += 1 } return output } function fitKeyText(secretText: string, keyLength: number | null): any { const rawBytes = textToUtf8Bytes(secretText) const actualLength = inferKeyLength(rawBytes.length, keyLength) return bytesToWordArray(clampBytes(rawBytes, actualLength)) } function fitFixedTextKey(secretText: string, targetLength: number): any { return bytesToWordArray(clampBytes(textToUtf8Bytes(secretText), targetLength)) } function fitKeyBytes(secretBytes: Uint8Array, keyLength: number | null): any { const actualLength = inferKeyLength(secretBytes.length, keyLength) return bytesToWordArray(clampBytes(secretBytes, actualLength)) } function fitIvText(ivText: string | null, size: number): any { const source = ivText == null ? new Uint8Array(size) : clampBytes(textToUtf8Bytes(ivText), size) return bytesToWordArray(source) } function fitIvBytes(ivBytes: Uint8Array | null, size: number): any { const source = ivBytes == null ? new Uint8Array(size) : clampBytes(ivBytes, size) return bytesToWordArray(source) } function wrapCipherBytes(cipherBytes: Uint8Array): any { return CryptoES.lib.CipherParams.create({ ciphertext: bytesToWordArray(cipherBytes) }) } function toBase64Url(base64Text: string): string { return base64Text.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '') } function fromBase64Url(base64UrlText: string): string { let output = base64UrlText.replace(/-/g, '+').replace(/_/g, '/') while (output.length % 4 != 0) { output += '=' } return output } function base64ToHex(base64Text: string): string { return CryptoES.enc.Base64.parse(base64Text).toString(CryptoES.enc.Hex) } function hexToBase64(hexText: string): string { return CryptoES.enc.Hex.parse(hexText).toString(CryptoES.enc.Base64) } export function encodeTextPortable(codec: string, plainText: string): string { ensureSupportedCodec(codec) const value = codec.toLowerCase() if (value == 'utf8') { return plainText } const word = CryptoES.enc.Utf8.parse(plainText) if (value == 'hex') { return CryptoES.enc.Hex.stringify(word) } if (value == 'base64') { return CryptoES.enc.Base64.stringify(word) } if (value == 'base64url') { return toBase64Url(CryptoES.enc.Base64.stringify(word)) } if (value == 'latin1') { return CryptoES.enc.Latin1.stringify(word) } return CryptoES.enc.Utf16.stringify(word) } export function decodeTextPortable(codec: string, encodedText: string): string { ensureSupportedCodec(codec) const value = codec.toLowerCase() if (value == 'utf8') { return encodedText } if (value == 'hex') { return CryptoES.enc.Hex.parse(encodedText).toString(CryptoES.enc.Utf8) } if (value == 'base64') { return CryptoES.enc.Base64.parse(encodedText).toString(CryptoES.enc.Utf8) } if (value == 'base64url') { return CryptoES.enc.Base64.parse(fromBase64Url(encodedText)).toString(CryptoES.enc.Utf8) } if (value == 'latin1') { return CryptoES.enc.Latin1.parse(encodedText).toString(CryptoES.enc.Utf8) } return CryptoES.enc.Utf16.parse(encodedText).toString(CryptoES.enc.Utf8) } export function digestTextPortable(kind: string, plainText: string): string { const value = ensureDigestKind(kind) if (value == 'md5') { return CryptoES.MD5(plainText).toString() } if (value == 'sha1') { return CryptoES.SHA1(plainText).toString() } if (value == 'sha256') { return CryptoES.SHA256(plainText).toString() } return CryptoES.SHA512(plainText).toString() } export function signTextPortable(kind: string, secretText: string, plainText: string): string { const value = ensureMacKind(kind) if (value == 'sha1') { return CryptoES.HmacSHA1(plainText, secretText).toString() } if (value == 'sha256') { return CryptoES.HmacSHA256(plainText, secretText).toString() } return CryptoES.HmacSHA512(plainText, secretText).toString() } export function aesEncryptTextPortable(secretText: string, plainText: string, mode: string, padding: string, ivText: string | null, keyLength: number | null): string { const encrypted = CryptoES.AES.encrypt(plainText, fitKeyText(secretText, keyLength), { iv: fitIvText(ivText, 16), mode: ensureAesMode(mode), padding: ensureAesPadding(padding) }) return encrypted.toString() } export function aesDecryptTextPortable(secretText: string, cipherText: string, mode: string, padding: string, ivText: string | null, keyLength: number | null): string { const decrypted = CryptoES.AES.decrypt(cipherText, fitKeyText(secretText, keyLength), { iv: fitIvText(ivText, 16), mode: ensureAesMode(mode), padding: ensureAesPadding(padding) }) return decrypted.toString(CryptoES.enc.Utf8) } export function aesEncryptBytesPortable(secretBytes: Uint8Array, plainBytes: Uint8Array, mode: string, padding: string, ivBytes: Uint8Array | null, keyLength: number | null): Uint8Array { const encrypted = CryptoES.AES.encrypt(bytesToWordArray(plainBytes), fitKeyBytes(secretBytes, keyLength), { iv: fitIvBytes(ivBytes, 16), mode: ensureAesMode(mode), padding: ensureAesPadding(padding) }) return wordArrayToBytes(encrypted.ciphertext) } export function aesDecryptBytesPortable(secretBytes: Uint8Array, cipherBytes: Uint8Array, mode: string, padding: string, ivBytes: Uint8Array | null, keyLength: number | null): Uint8Array { const decrypted = CryptoES.AES.decrypt(wrapCipherBytes(cipherBytes), fitKeyBytes(secretBytes, keyLength), { iv: fitIvBytes(ivBytes, 16), mode: ensureAesMode(mode), padding: ensureAesPadding(padding) }) return wordArrayToBytes(decrypted) } export function tripleDesEncryptTextPortable(secretText: string, plainText: string, mode: string, ivText: string | null): string { const encrypted = CryptoES.TripleDES.encrypt(plainText, fitKeyText(secretText, 24), { iv: fitIvText(ivText, 8), mode: ensureTripleDesMode(mode), padding: CryptoES.pad.Pkcs7 }) return encrypted.toString() } export function tripleDesDecryptTextPortable(secretText: string, cipherText: string, mode: string, ivText: string | null): string { const decrypted = CryptoES.TripleDES.decrypt(cipherText, fitKeyText(secretText, 24), { iv: fitIvText(ivText, 8), mode: ensureTripleDesMode(mode), padding: CryptoES.pad.Pkcs7 }) return decrypted.toString(CryptoES.enc.Utf8) } export function desEncryptTextPortable(secretText: string, plainText: string, mode: string, padding: string, ivText: string | null): string { const encrypted = CryptoES.DES.encrypt(plainText, fitFixedTextKey(secretText, 8), { iv: fitIvText(ivText, 8), mode: ensureDesMode(mode), padding: ensureAesPadding(padding) }) return encrypted.toString() } export function desDecryptTextPortable(secretText: string, cipherText: string, mode: string, padding: string, ivText: string | null): string { const decrypted = CryptoES.DES.decrypt(cipherText, fitFixedTextKey(secretText, 8), { iv: fitIvText(ivText, 8), mode: ensureDesMode(mode), padding: ensureAesPadding(padding) }) return decrypted.toString(CryptoES.enc.Utf8) } export function rc4EncryptTextPortable(secretText: string, plainText: string): string { const encrypted = CryptoES.RC4.encrypt(plainText, CryptoES.enc.Utf8.parse(secretText)) return encrypted.ciphertext.toString(CryptoES.enc.Hex) } export function rc4DecryptTextPortable(secretText: string, cipherHexText: string): string { const decrypted = CryptoES.RC4.decrypt(wrapCipherBytes(wordArrayToBytes(CryptoES.enc.Hex.parse(cipherHexText))), CryptoES.enc.Utf8.parse(secretText)) return decrypted.toString(CryptoES.enc.Utf8) } export function createRsaPairPortable(keySize: number): { publicKey: string, privateKey: string } { const engine = new JSEncrypt({ default_key_size: keySize.toString() }) engine.getKey() return { publicKey: engine.getPublicKey(), privateKey: engine.getPrivateKey() } } export function rsaEncryptTextPortable(publicKey: string, plainText: string, outputKind: string | null): string { const engine = new JSEncrypt() engine.setPublicKey(publicKey) const encrypted = engine.encrypt(plainText) if (encrypted == null || encrypted == false) { throw new Error('RSA加密失败') } return outputKind == 'hex' ? base64ToHex(encrypted as string) : encrypted as string } export function rsaDecryptTextPortable(privateKey: string, cipherText: string, outputKind: string | null): string { const engine = new JSEncrypt() engine.setPrivateKey(privateKey) const source = outputKind == 'hex' ? hexToBase64(cipherText) : cipherText const decrypted = engine.decrypt(source) if (decrypted == null || decrypted == false) { throw new Error('RSA解密失败') } return decrypted as string } export function bytesToHexPortable(input: Uint8Array): string { return bytesToWordArray(input).toString(CryptoES.enc.Hex) } export function utf8ToBytesPortable(input: string): Uint8Array { return textToUtf8Bytes(input) } export function bytesToUtf8Portable(input: Uint8Array): string { return utf8BytesToText(input) }