Files
xiaozhi-user-uniappx/uni_modules/laoqianjunzi-crypto/utssdk/shared/portable_crypto.ts
T

426 lines
13 KiB
TypeScript

// @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)
}