添加导入加密插件"laoqianjunzi-crypto"

This commit is contained in:
2026-06-07 10:59:23 +08:00
parent 4ffe829f75
commit 704e9650f9
22 changed files with 5949 additions and 0 deletions
@@ -0,0 +1,2 @@
## 1.0.02026-05-30
初次提交
@@ -0,0 +1,104 @@
{
"id": "laoqianjunzi-crypto",
"displayName": "多端加密工具箱",
"version": "1.0.0",
"description": "为 uni-app x 提供 Android、iOS、Harmony、Web、微信小程序统一加密能力,覆盖摘要、编码、HMAC、AES、DES、RC4 与 RSA。",
"keywords": [
"crypto",
"hash",
"aes",
"rsa",
"uts"
],
"repository": "",
"engines": {
"HBuilderX": "^5.07",
"uni-app": "",
"uni-app-x": "^4.75"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "所有加解密与编码运算均在本地执行,不内置远程上传逻辑",
"permissions": "无"
},
"darkmode": "√",
"i18n": "√",
"widescreen": "√",
"npmurl": "https://gitee.com/laoqianjunzi/laoqianjunzi-crypto"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"client": {
"uni-app": {
"vue": {
"vue2": "-",
"vue3": "-"
},
"web": {
"safari": "-",
"chrome": "-"
},
"app": {
"vue": "-",
"nvue": "-",
"android": "-",
"ios": "-",
"harmony": "-"
},
"mp": {
"weixin": "-",
"alipay": "-",
"toutiao": "-",
"baidu": "-",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "-",
"lark": "-"
},
"quickapp": {
"huawei": "-",
"union": "-"
}
},
"uni-app-x": {
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"android": {
"extVersion": "",
"minVersion": "21"
},
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√"
}
}
},
"cloud": {
"tcb": "√",
"aliyun": "√",
"alipay": "√"
}
}
}
}
@@ -0,0 +1,518 @@
<template>
<!-- #ifdef APP -->
<scroll-view class="crypto-scroll">
<!-- #endif -->
<view class="crypto-page">
<view class="hero-panel">
<text class="hero-kicker">LAOQIANJUNZI-CRYPTO</text>
<text class="hero-title">统一加密能力演示</text>
<text class="hero-desc">这一页直接调用插件导出的新接口,集中演示编码、摘要、HMAC、AES、3DES、RC4、RSA 与字节数组加解密。</text>
</view>
<view class="card-block runtime-card">
<text class="card-title">运行时概览</text>
<view class="status-row">
<text class="status-label">平台</text>
<text class="status-value">{{ runtimeInfo.platformName }}</text>
</view>
<view class="status-row">
<text class="status-label">引擎</text>
<text class="status-value">{{ runtimeInfo.engineName }}</text>
</view>
<view class="status-row">
<text class="status-label">高级模式</text>
<text class="status-value">{{ runtimeInfo.supportsAdvancedModes ? '已启用' : '受限' }}</text>
</view>
<view class="status-row">
<text class="status-label">原生桥接</text>
<text class="status-value">{{ runtimeInfo.usesNativeBridge ? '是' : '否' }}</text>
</view>
</view>
<view class="card-block form-card">
<text class="card-title">输入参数</text>
<view class="field-group">
<text class="field-label">明文</text>
<input class="field-input" v-model="plainText" />
</view>
<view class="field-group">
<text class="field-label">主密钥</text>
<input class="field-input" v-model="secretText" />
</view>
<view class="field-group">
<text class="field-label">向量</text>
<input class="field-input" v-model="ivText" />
</view>
<view class="field-group">
<text class="field-label">HMAC 密钥</text>
<input class="field-input" v-model="macSecretText" />
</view>
<view class="field-group">
<text class="field-label">3DES 密钥</text>
<input class="field-input" v-model="legacySecretText" />
</view>
<view class="field-group">
<text class="field-label">RSA 位数</text>
<input class="field-input" type="number" v-model="rsaBitsText" />
</view>
</view>
<view class="action-grid">
<button class="action-button action-strong" @click="runAllDemos">运行全部示例</button>
<button class="action-button" @click="runCodecDemo">编码</button>
<button class="action-button" @click="runDigestDemo">摘要/HMAC</button>
<button class="action-button" @click="runAesDemo">AES 文本</button>
<button class="action-button" @click="runByteDemo">AES 字节</button>
<button class="action-button" @click="runLegacyDemo">3DES/RC4</button>
<button class="action-button" @click="runRsaDemo">RSA</button>
<button class="action-button" @click="runCompatDemo">兼容 API</button>
<button class="action-button action-warn" @click="clearResults">清空结果</button>
</view>
<view class="result-grid">
<view class="card-block result-card">
<text class="card-title">编码</text>
<text class="result-line">Base64: {{ codecBase64 }}</text>
<text class="result-line">Hex: {{ codecHex }}</text>
<text class="result-line">Base64url: {{ codecBase64url }}</text>
<text class="result-line">反解: {{ codecBackText }}</text>
</view>
<view class="card-block result-card">
<text class="card-title">摘要与签名</text>
<text class="result-line">MD5: {{ md5Text }}</text>
<text class="result-line">SHA256: {{ sha256Text }}</text>
<text class="result-line">SHA512: {{ sha512Text }}</text>
<text class="result-line">HMAC-SHA256: {{ hmacText }}</text>
</view>
<view class="card-block result-card">
<text class="card-title">AES 文本</text>
<text class="result-line">密文: {{ aesCipherText }}</text>
<text class="result-line">解密: {{ aesPlainText }}</text>
</view>
<view class="card-block result-card">
<text class="card-title">AES 字节</text>
<text class="result-line">字节密文: {{ aesByteCipherHex }}</text>
<text class="result-line">字节解密: {{ aesBytePlainText }}</text>
</view>
<view class="card-block result-card">
<text class="card-title">3DES 与 RC4</text>
<text class="result-line">3DES 密文: {{ tripleCipherText }}</text>
<text class="result-line">3DES 解密: {{ triplePlainText }}</text>
<text class="result-line">RC4 密文: {{ rc4CipherHex }}</text>
<text class="result-line">RC4 解密: {{ rc4PlainText }}</text>
</view>
<view class="card-block result-card">
<text class="card-title">RSA</text>
<text class="result-line">公钥长度: {{ rsaPublicBrief }}</text>
<text class="result-line">私钥长度: {{ rsaPrivateBrief }}</text>
<text class="result-line">RSA 密文: {{ rsaCipherText }}</text>
<text class="result-line">RSA 解密: {{ rsaPlainText }}</text>
</view>
</view>
<view class="card-block log-card">
<text class="card-title">执行日志</text>
<view class="log-list">
<text class="log-line" v-for="(item,index) in logLines" :key="index">{{ item }}</text>
</view>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang="uts">
import { aesDecrypt, aesDecryptBytes, aesDecryptText, aesEncrypt, aesEncryptBytes, aesEncryptText, base64Decode, base64Encode, createRsaPair, defaultByteCipherOptions, defaultTextCipherOptions, defaultTripleDesOptions, decodeText, describeCryptoRuntime, digestText, encodeText, generateRSAKeyPair, md5, rc4DecryptText, rc4EncryptText, rsaDecrypt, rsaDecryptText, rsaEncrypt, rsaEncryptText, signText, tripleDesDecryptText, tripleDesEncryptText, useCrypto } from '@/uni_modules/laoqianjunzi-crypto'
import type { CryptoRuntimeSnapshot } from '@/uni_modules/laoqianjunzi-crypto'
const runtimeInfo = ref(describeCryptoRuntime() as CryptoRuntimeSnapshot)
const plainText = ref('Hello laoqianjunzi-crypto')
const secretText = ref('1234567890abcdef')
const ivText = ref('fedcba0987654321')
const macSecretText = ref('signing-key')
const legacySecretText = ref('1234567890abcdef12345678')
const rsaBitsText = ref('2048')
const codecBase64 = ref('')
const codecHex = ref('')
const codecBase64url = ref('')
const codecBackText = ref('')
const md5Text = ref('')
const sha256Text = ref('')
const sha512Text = ref('')
const hmacText = ref('')
const aesCipherText = ref('')
const aesPlainText = ref('')
const aesByteCipherHex = ref('')
const aesBytePlainText = ref('')
const tripleCipherText = ref('')
const triplePlainText = ref('')
const rc4CipherHex = ref('')
const rc4PlainText = ref('')
const rsaCipherText = ref('')
const rsaPlainText = ref('')
const rsaPublicBrief = ref('')
const rsaPrivateBrief = ref('')
const logLines = ref(['等待执行示例'] as string[])
function appendLog(message : string) : void {
const stamp = new Date().toISOString()
logLines.value.unshift(stamp + ' ' + message)
if (logLines.value.length > 20) {
logLines.value.splice(20)
}
}
function bytesToHex(input : Uint8Array) : string {
let output = ''
let index = 0
while (index < input.length) {
let part = input[index].toString(16)
if (part.length < 2) {
part = '0' + part
}
output += part
index += 1
}
return output
}
function textToBytes(input : string) : Uint8Array {
const output = new Uint8Array(input.length)
let index = 0
while (index < input.length) {
const code = input.charCodeAt(index)
output[index] = code == null ? 0 : code & 0xff
index += 1
}
return output
}
function bytesToText(input : Uint8Array) : string {
let output = ''
let index = 0
while (index < input.length) {
output += String.fromCharCode(input[index])
index += 1
}
return output
}
function currentRsaBits() : number {
const parsed = parseInt(rsaBitsText.value)
if (parsed == 1024 || parsed == 2048 || parsed == 3072 || parsed == 4096) {
return parsed
}
return 2048
}
function runCodecDemo() : void {
codecBase64.value = encodeText('base64', plainText.value)
codecHex.value = encodeText('hex', plainText.value)
codecBase64url.value = encodeText('base64url', plainText.value)
codecBackText.value = decodeText('base64', codecBase64.value)
appendLog('编码能力已完成')
}
function runDigestDemo() : void {
md5Text.value = digestText('md5', plainText.value)
sha256Text.value = digestText('sha256', plainText.value)
sha512Text.value = digestText('sha512', plainText.value)
hmacText.value = signText('sha256', macSecretText.value, plainText.value)
appendLog('摘要与 HMAC 已完成')
}
function runAesDemo() : void {
const options = defaultTextCipherOptions()
options.mode = 'CBC'
options.padding = 'PKCS7'
options.ivText = ivText.value
options.keyLength = 16
aesCipherText.value = aesEncryptText(secretText.value, plainText.value, options)
aesPlainText.value = aesDecryptText(secretText.value, aesCipherText.value, options)
appendLog('AES 文本加解密已完成')
}
function runByteDemo() : void {
const options = defaultByteCipherOptions()
options.mode = 'CTR'
options.padding = 'PKCS7'
options.keyLength = 16
const secretBytes = textToBytes(secretText.value)
const plainBytes = textToBytes(plainText.value)
const cipherBytes = aesEncryptBytes(secretBytes, plainBytes, options)
const restoreBytes = aesDecryptBytes(secretBytes, cipherBytes, options)
aesByteCipherHex.value = bytesToHex(cipherBytes)
aesBytePlainText.value = bytesToText(restoreBytes)
appendLog('AES 字节加解密已完成')
}
function runLegacyDemo() : void {
const options = defaultTripleDesOptions()
options.mode = 'CBC'
options.ivText = '12345678'
tripleCipherText.value = tripleDesEncryptText(legacySecretText.value, plainText.value, options)
triplePlainText.value = tripleDesDecryptText(legacySecretText.value, tripleCipherText.value, options)
rc4CipherHex.value = rc4EncryptText(macSecretText.value, plainText.value)
rc4PlainText.value = rc4DecryptText(macSecretText.value, rc4CipherHex.value)
appendLog('3DES 与 RC4 已完成')
}
function runRsaDemo() : void {
const pair = createRsaPair(currentRsaBits())
rsaPublicBrief.value = pair.publicKey.length + ' chars'
rsaPrivateBrief.value = pair.privateKey.length + ' chars'
rsaCipherText.value = rsaEncryptText(pair.publicKey, plainText.value, 'base64')
rsaPlainText.value = rsaDecryptText(pair.privateKey, rsaCipherText.value, 'base64')
appendLog('RSA 生成与加解密已完成')
}
function runCompatDemo() : void {
const xBase64 = base64Encode(plainText.value)
const xPlain = base64Decode(xBase64)
const xMd5 = md5(plainText.value)
const xCipher = aesEncrypt(secretText.value, plainText.value, 'CBC', ivText.value, 16)
const xRestore = aesDecrypt(secretText.value, xCipher, 'CBC', ivText.value, 16)
const xPair = generateRSAKeyPair(currentRsaBits())
const xRsaText = rsaDecrypt(xPair.privateKey, rsaEncrypt(xPair.publicKey, plainText.value))
const crypto = useCrypto()
const limeIv = crypto.enc.Utf8.parse(ivText.value)
const limeCipher = crypto.AES.encrypt(plainText.value, secretText.value, {
iv: limeIv,
mode: crypto.mode.CBC,
padding: crypto.pad.Pkcs7
} as UTSJSONObject)
const limePlain = crypto.AES.decrypt(limeCipher.toString(), secretText.value, {
iv: limeIv,
mode: crypto.mode.CBC,
padding: crypto.pad.Pkcs7
} as UTSJSONObject).toString(crypto.enc.Utf8)
const limeRsa = crypto.RSA
const limePair = limeRsa.generateKeyPair()
limeRsa.setPublicKey(limePair.publicKey)
const limeCipherText = limeRsa.encrypt(plainText.value)
limeRsa.setPrivateKey(limePair.privateKey)
const limeRsaText = limeRsa.decrypt(limeCipherText)
appendLog('兼容 API 已完成:x-base64=' + xPlain.length + ' x-md5=' + xMd5.substring(0, 8) + ' x-aes=' + xRestore.length + ' x-rsa=' + xRsaText.length + ' lime-aes=' + limePlain.length + ' lime-rsa=' + limeRsaText.length)
}
function runAllDemos() : void {
try {
runCodecDemo()
runDigestDemo()
runAesDemo()
runByteDemo()
runLegacyDemo()
runRsaDemo()
runCompatDemo()
appendLog('全部能力演示执行结束')
} catch (error) {
appendLog('执行失败:' + (error as Error).message)
}
}
function clearResults() : void {
codecBase64.value = ''
codecHex.value = ''
codecBase64url.value = ''
codecBackText.value = ''
md5Text.value = ''
sha256Text.value = ''
sha512Text.value = ''
hmacText.value = ''
aesCipherText.value = ''
aesPlainText.value = ''
aesByteCipherHex.value = ''
aesBytePlainText.value = ''
tripleCipherText.value = ''
triplePlainText.value = ''
rc4CipherHex.value = ''
rc4PlainText.value = ''
rsaCipherText.value = ''
rsaPlainText.value = ''
rsaPublicBrief.value = ''
rsaPrivateBrief.value = ''
logLines.value = ['结果已清空'] as string[]
}
onMounted(() => {
appendLog('示例页已就绪,点击按钮即可执行')
})
</script>
<style>
.crypto-scroll {
flex: 1;
}
.crypto-page {
display: flex;
flex-direction: column;
padding: 48rpx;
background-color: #efe8dc;
}
.hero-panel {
display: flex;
flex-direction: column;
padding: 40rpx;
border-radius: 48rpx;
background-color: #11231c;
margin-bottom: 36rpx;
}
.hero-kicker {
color: #f2c169;
font-size: 26rpx;
letter-spacing: 4rpx;
margin-bottom: 12rpx;
}
.hero-title {
color: #fff8ea;
font-size: 56rpx;
font-weight: 700;
margin-bottom: 12rpx;
}
.hero-desc {
color: #c9d6cc;
font-size: 28rpx;
line-height: 40rpx;
}
.card-block {
display: flex;
flex-direction: column;
padding: 36rpx;
border-radius: 40rpx;
background-color: #fffaf1;
margin-bottom: 28rpx;
}
.card-title {
color: #24352e;
font-size: 36rpx;
font-weight: 700;
margin-bottom: 18rpx;
}
.runtime-card {
background-color: #f7f1e3;
}
.status-row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom-width: 2rpx;
border-bottom-style: solid;
border-bottom-color: #e7dbc8;
}
.status-label {
color: #6b6d66;
font-size: 26rpx;
}
.status-value {
color: #1c2b25;
font-size: 28rpx;
font-weight: 600;
}
.form-card {
background-color: #fff7ee;
}
.field-group {
display: flex;
flex-direction: column;
margin-bottom: 16rpx;
}
.field-label {
color: #6f6258;
font-size: 26rpx;
}
.field-input {
height: 84rpx;
padding: 0 24rpx;
border-radius: 24rpx;
background-color: #f1e7da;
color: #1d2823;
font-size: 28rpx;
}
.action-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 28rpx;
}
.action-button {
min-width: 208rpx;
border-radius: 999rpx;
background-color: #d8e5db;
color: #20312a;
font-size: 26rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
}
.action-strong {
background-color: #17392d;
color: #fff8ef;
}
.action-warn {
background-color: #ead4b9;
color: #5d3722;
}
.result-grid {
display: flex;
flex-direction: column;
}
.result-card {
background-color: #fffdf8;
}
.result-line {
color: #25322c;
font-size: 26rpx;
line-height: 38rpx;
}
.log-card {
background-color: #15261f;
}
.log-list {
display: flex;
flex-direction: column;
}
.log-line {
color: #d6e7dc;
font-size: 24rpx;
line-height: 36rpx;
margin-bottom: 16rpx;
}
</style>
+483
View File
@@ -0,0 +1,483 @@
# laoqianjunzi-crypto
`laoqianjunzi-crypto` 是一个面向 `uni-app x` 的多端加密工具插件,统一封装了文本编解码、摘要、HMAC、AES、DES、3DES、RC4、RSA 等常见能力。
插件当前覆盖以下平台:
- `App-Android`
- `App-iOS`
- `App-Harmony`
- `Web`
- `微信小程序`
适合以下场景:
- 业务请求签名、摘要校验、口令派生前的基础处理
- 本地敏感文本或字节数据加解密
- 公私钥生成、RSA 短文本加解密
- 需要在多端复用同一套加密接口的 `uni-app x` 项目
## 功能概览
- 支持 `utf8``hex``base64``base64url``latin1``utf16` 编解码
- 支持 `md5``sha1``sha256``sha512` 摘要
- 支持 `HMAC-SHA1``HMAC-SHA256``HMAC-SHA512`
- 支持 AES 文本加解密与 AES 字节数组加解密
- 支持 DES、3DES、RC4 文本加解密
- 支持 RSA 密钥对生成与短文本加解密
- 支持读取当前运行时的底层引擎信息
- 所有计算均在本地执行,不包含网络上传逻辑
## 版本要求
根据插件当前 `package.json` 配置,建议使用以下环境:
- `HBuilderX` `5.07` 或更高版本
- `uni-app x` `4.75` 或更高版本
## 平台支持矩阵
| 平台 | 加密能力 | RSA | 运行时引擎 |
| --- | --- | --- | --- |
| App-Android | 支持 | 支持 | `JCA` 原生桥接 |
| App-iOS | 支持 | 支持 | `CommonCrypto` 原生桥接 |
| App-Harmony | 支持 | 支持 | `@ohos/crypto-js` + 原生 RSA |
| Web | 支持 | 支持 | `crypto-es` + `jsencrypt` |
| 微信小程序 | 支持 | 支持 | `crypto-es` + `jsencrypt` |
说明:
- `describeCryptoRuntime()` 可返回当前平台名、底层引擎名、是否支持高级分组模式、是否经过原生桥接
- Web 与微信小程序端的实现基于纯脚本运行时,App 端优先走原生桥接
- 文本加密、文本解密、摘要、签名、RSA 等接口均采用统一函数签名,便于业务跨端复用
## 安装与导入
将插件放入项目 `uni_modules` 目录后,直接从插件根目录导入:
```uts
import {
aesDecryptBytes,
aesDecryptText,
aesEncryptBytes,
aesEncryptText,
createRsaPair,
decodeText,
defaultByteCipherOptions,
defaultTextCipherOptions,
describeCryptoRuntime,
digestText,
encodeText,
rsaDecryptText,
rsaEncryptText,
signText
} from "@/uni_modules/laoqianjunzi-crypto"
import type {
CryptoByteCipherOptions,
CryptoRsaPair,
CryptoRuntimeSnapshot,
CryptoTextCipherOptions
} from "@/uni_modules/laoqianjunzi-crypto"
```
请注意:
- 只从插件根目录导入,不要直接导入插件内部 `utssdk` 文件
- 业务代码建议统一使用插件根导出,便于后续维护和跨端类型识别
## 快速开始
下面示例演示最常用的四类能力:运行时信息、文本编解码、摘要/HMAC、AES 文本加解密、RSA 密钥对与短文本加解密。
```uts
import {
aesDecryptText,
aesEncryptText,
createRsaPair,
decodeText,
defaultTextCipherOptions,
describeCryptoRuntime,
digestText,
encodeText,
rsaDecryptText,
rsaEncryptText,
signText
} from "@/uni_modules/laoqianjunzi-crypto"
import type {
CryptoRsaPair,
CryptoRuntimeSnapshot,
CryptoTextCipherOptions
} from "@/uni_modules/laoqianjunzi-crypto"
function runCryptoDemo(): void {
const runtime: CryptoRuntimeSnapshot = describeCryptoRuntime()
console.log("runtime", runtime.platformName, runtime.engineName)
const plainText = "Hello uni-app x"
const secretText = "1234567890abcdef"
const ivText = "fedcba0987654321"
const base64Text = encodeText("base64", plainText)
const restoreText = decodeText("base64", base64Text)
console.log("base64", base64Text)
console.log("restore", restoreText)
const sha256Text = digestText("sha256", plainText)
const hmacText = signText("sha256", "sign-key", plainText)
console.log("sha256", sha256Text)
console.log("hmac", hmacText)
const aesOptions: CryptoTextCipherOptions = defaultTextCipherOptions()
aesOptions.mode = "CBC"
aesOptions.padding = "PKCS7"
aesOptions.ivText = ivText
aesOptions.keyLength = 16
const aesCipherText = aesEncryptText(secretText, plainText, aesOptions)
const aesPlainText = aesDecryptText(secretText, aesCipherText, aesOptions)
console.log("aes cipher", aesCipherText)
console.log("aes plain", aesPlainText)
const rsaPair: CryptoRsaPair = createRsaPair(2048)
const rsaCipherText = rsaEncryptText(rsaPair.publicKey, plainText, "base64")
const rsaPlainText = rsaDecryptText(rsaPair.privateKey, rsaCipherText, "base64")
console.log("rsa plain", rsaPlainText)
}
```
## AES 字节数组示例
如果你的业务处理的是二进制数据,而不是普通字符串,建议使用字节接口:
```uts
import {
aesDecryptBytes,
aesEncryptBytes,
defaultByteCipherOptions
} from "@/uni_modules/laoqianjunzi-crypto"
import type { CryptoByteCipherOptions } from "@/uni_modules/laoqianjunzi-crypto"
function textToBytes(input: string): Uint8Array {
const output = new Uint8Array(input.length)
let index = 0
while (index < input.length) {
const code = input.charCodeAt(index)
output[index] = code == null ? 0 : code & 0xff
index += 1
}
return output
}
function bytesToText(input: Uint8Array): string {
let output = ""
let index = 0
while (index < input.length) {
output += String.fromCharCode(input[index])
index += 1
}
return output
}
function runByteCipherDemo(): void {
const options: CryptoByteCipherOptions = defaultByteCipherOptions()
options.mode = "CTR"
options.padding = "PKCS7"
options.keyLength = 16
const secretBytes = textToBytes("1234567890abcdef")
const plainBytes = textToBytes("hello-bytes")
const cipherBytes = aesEncryptBytes(secretBytes, plainBytes, options)
const restoreBytes = aesDecryptBytes(secretBytes, cipherBytes, options)
console.log("byte plain", bytesToText(restoreBytes))
console.log("cipher length", cipherBytes.length)
}
```
## 对外类型
### CryptoDigestKind
摘要算法枚举:
- `md5`
- `sha1`
- `sha256`
- `sha512`
### CryptoMacKind
HMAC 算法枚举:
- `sha1`
- `sha256`
- `sha512`
### CryptoCodecKind
文本编解码枚举:
- `utf8`
- `hex`
- `base64`
- `base64url`
- `latin1`
- `utf16`
### CryptoBlockMode
分组模式枚举:
- `ECB`
- `CBC`
- `CFB`
- `CTR`
- `CTRGladman`
- `OFB`
### CryptoPaddingKind
填充模式枚举:
- `PKCS7`
- `ANSI_X923`
- `ISO_10126`
- `ISO_97971`
- `NONE`
- `ZERO`
### RsaOutputKind
RSA 密文输出格式:
- `base64`
- `hex`
### CryptoTextCipherOptions
AES 文本加解密参数:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `mode` | `CryptoBlockMode` | 是 | 分组模式 |
| `padding` | `CryptoPaddingKind` | 是 | 填充模式 |
| `ivText` | `string \\| null` | 是 | 初始向量文本,`ECB` 模式可传 `null` |
| `keyLength` | `number \\| null` | 是 | AES 密钥长度,建议传 `16``24``32` |
### CryptoByteCipherOptions
AES 字节加解密参数:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `mode` | `CryptoBlockMode` | 是 | 分组模式 |
| `padding` | `CryptoPaddingKind` | 是 | 填充模式 |
| `ivBytes` | `Uint8Array \\| null` | 是 | 初始向量字节数组,`ECB` 模式可传 `null` |
| `keyLength` | `number \\| null` | 是 | AES 密钥长度,建议传 `16``24``32` |
### CryptoTripleDesOptions
3DES 参数:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `mode` | `"ECB" \\| "CBC"` | 是 | 3DES 当前支持的分组模式 |
| `ivText` | `string \\| null` | 是 | 初始向量文本,`ECB` 模式可传 `null` |
### CryptoDesOptions
DES 参数:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `mode` | `CryptoBlockMode` | 是 | 分组模式 |
| `padding` | `CryptoPaddingKind` | 是 | 填充模式 |
| `ivText` | `string \\| null` | 是 | 初始向量文本,`ECB` 模式可传 `null` |
### CryptoRsaPair
RSA 密钥对对象:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `publicKey` | `string` | 是 | 公钥 PEM 文本 |
| `privateKey` | `string` | 是 | 私钥 PEM 文本 |
### CryptoRuntimeSnapshot
运行时描述对象:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `platformName` | `string` | 是 | 平台名称 |
| `engineName` | `string` | 是 | 当前底层实现名称 |
| `supportsAdvancedModes` | `boolean` | 是 | 是否支持高级分组模式 |
| `usesNativeBridge` | `boolean` | 是 | 是否经过原生桥接 |
## 默认参数函数
### defaultTextCipherOptions()
返回 AES 文本加解密默认参数:
| 字段 | 默认值 |
| --- | --- |
| `mode` | `CBC` |
| `padding` | `PKCS7` |
| `ivText` | `0123456789abcdef` |
| `keyLength` | `16` |
### defaultByteCipherOptions()
返回 AES 字节加解密默认参数:
| 字段 | 默认值 |
| --- | --- |
| `mode` | `CBC` |
| `padding` | `PKCS7` |
| `ivBytes` | `0123456789abcdef` 的 ASCII 字节 |
| `keyLength` | `16` |
### defaultTripleDesOptions()
返回 3DES 默认参数:
| 字段 | 默认值 |
| --- | --- |
| `mode` | `CBC` |
| `ivText` | `12345678` |
### defaultDesOptions()
返回 DES 默认参数:
| 字段 | 默认值 |
| --- | --- |
| `mode` | `CBC` |
| `padding` | `PKCS7` |
| `ivText` | `12345678` |
## 运行时信息函数
### describeCryptoRuntime()
返回当前平台的运行时信息:
```uts
const runtime = describeCryptoRuntime()
console.log(runtime.platformName)
console.log(runtime.engineName)
console.log(runtime.supportsAdvancedModes)
console.log(runtime.usesNativeBridge)
```
## API 清单
### 编解码
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `encodeText(codec, plainText)` | 将普通文本编码为指定格式文本 | `string` |
| `decodeText(codec, encodedText)` | 将指定格式文本还原为普通文本 | `string` |
### 摘要与签名
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `digestText(kind, plainText)` | 计算摘要,结果为十六进制字符串 | `string` |
| `signText(kind, secretText, plainText)` | 计算 HMAC,结果为十六进制字符串 | `string` |
### AES 文本加解密
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `aesEncryptText(secretText, plainText, options)` | 使用文本密钥加密文本 | `string` |
| `aesDecryptText(secretText, cipherText, options)` | 使用文本密钥解密文本 | `string` |
### AES 字节加解密
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `aesEncryptBytes(secretBytes, plainBytes, options)` | 使用字节密钥加密字节数组 | `Uint8Array` |
| `aesDecryptBytes(secretBytes, cipherBytes, options)` | 使用字节密钥解密字节数组 | `Uint8Array` |
### 3DES
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `tripleDesEncryptText(secretText, plainText, options)` | 3DES 文本加密 | `string` |
| `tripleDesDecryptText(secretText, cipherText, options)` | 3DES 文本解密 | `string` |
### DES
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `desEncryptText(secretText, plainText, options)` | DES 文本加密 | `string` |
| `desDecryptText(secretText, cipherText, options)` | DES 文本解密 | `string` |
### RC4
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `rc4EncryptText(secretText, plainText)` | RC4 加密,返回十六进制密文 | `string` |
| `rc4DecryptText(secretText, cipherHexText)` | RC4 解密十六进制密文 | `string` |
### RSA
| 函数 | 说明 | 返回值 |
| --- | --- | --- |
| `createRsaPair(keySize)` | 生成 RSA 密钥对 | `CryptoRsaPair` |
| `rsaEncryptText(publicKey, plainText, outputKind)` | 使用公钥加密短文本 | `string` |
| `rsaDecryptText(privateKey, cipherText, outputKind)` | 使用私钥解密短文本 | `string` |
建议:
- `keySize` 推荐使用 `1024``2048``3072``4096`
- 一般业务默认建议使用 `2048`
## 使用建议
- 编解码、摘要、HMAC 建议直接使用字符串接口,返回值稳定、便于日志记录和接口传输
- 处理图片片段、协议报文、蓝牙包、文件块等二进制内容时,优先使用 `aesEncryptBytes``aesDecryptBytes`
- AES 跨端调用时,`keyLength` 建议固定传 `16``24``32` 之一,避免不同运行时对推断长度的理解差异
- 使用 `CBC``CFB``CTR``OFB` 等模式时,建议显式传入业务自定义 `ivText``ivBytes`
- `RSA` 更适合短文本、密钥交换或签名相关前置处理,不适合大体积正文直接加解密
- `RC4``DES``3DES` 更适用于兼容特定协议场景,新业务优先建议选择 `AES``RSA`
## 异常处理建议
参数不合法、密文格式不正确、密钥与向量不匹配时,接口可能抛出错误。业务代码建议统一使用 `try/catch` 包裹:
```uts
import {
aesEncryptText,
defaultTextCipherOptions
} from "@/uni_modules/laoqianjunzi-crypto"
try {
const options = defaultTextCipherOptions()
const cipherText = aesEncryptText("1234567890abcdef", "hello", options)
console.log(cipherText)
} catch (error) {
console.error("crypto error", error)
}
```
## 注意事项
- 本插件面向 `uni-app x`,不面向传统 `uni-app` 页面运行时
- 文本接口默认适合 UTF-8 文本场景;如果你的原始数据本身不是普通文本,请优先使用字节接口
- `ECB` 模式不依赖 IV,其余分组模式建议始终显式设置 IV
- RSA 密文输出格式由 `outputKind` 控制,发送方与接收方应保持一致
- 通过 `default*Options()` 获取参数对象后,可按当前业务需求覆盖默认值
## 示例页面
插件内已提供示例页面,可用于快速验证能力是否正常:
- `uni_modules/laoqianjunzi-crypto/pages/index`
如果项目已在 `pages.json` 中注册该页面,可直接运行到对应页面进行体验。
@@ -0,0 +1,744 @@
package uts.sdk.modules.laoqianjunziCrypto
import android.util.Base64
import java.io.ByteArrayOutputStream
import java.nio.charset.StandardCharsets
import java.security.GeneralSecurityException
import java.security.KeyFactory
import java.security.KeyPairGenerator
import java.security.MessageDigest
import java.security.SecureRandom
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher
import javax.crypto.Mac
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
data class NativeRsaBundle(val publicKey: String, val privateKey: String)
object NativeCipherCore {
private const val AES_BLOCK_SIZE = 16
private const val DES_BLOCK_SIZE = 8
private val secureRandom = SecureRandom()
fun digestHex(kind: String, plainText: String): String {
return try {
val digest = MessageDigest.getInstance(resolveDigestAlgorithm(kind))
toHex(digest.digest(plainText.toByteArray(StandardCharsets.UTF_8)))
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalStateException("摘要失败")
}
}
fun hmacHex(kind: String, secretText: String, plainText: String): String {
return try {
val mac = Mac.getInstance(resolveHmacAlgorithm(kind))
mac.init(SecretKeySpec(secretText.toByteArray(StandardCharsets.UTF_8), mac.algorithm))
toHex(mac.doFinal(plainText.toByteArray(StandardCharsets.UTF_8)))
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalStateException("HMAC失败")
}
}
fun encodeBase64(plainText: String): String {
return Base64.encodeToString(plainText.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP)
}
fun decodeBase64(encodedText: String): String {
return try {
String(Base64.decode(encodedText, Base64.DEFAULT), StandardCharsets.UTF_8)
} catch (e: Exception) {
throw IllegalArgumentException("Base64无效")
}
}
fun encodeCodec(codec: String, plainText: String): String {
val bytes = plainText.toByteArray(StandardCharsets.UTF_8)
return when (codec.lowercase()) {
"utf8" -> plainText
"hex" -> toHex(bytes)
"base64" -> Base64.encodeToString(bytes, Base64.NO_WRAP)
"base64url" -> Base64.encodeToString(bytes, Base64.NO_WRAP)
.replace('+', '-')
.replace('/', '_')
.trimEnd('=')
"latin1" -> latin1FromBytes(bytes)
"utf16" -> utf16CodecFromBytes(bytes)
else -> throw IllegalArgumentException("不支持的编码类型")
}
}
fun decodeCodec(codec: String, encodedText: String): String {
return when (codec.lowercase()) {
"utf8" -> encodedText
"hex" -> String(fromHex(encodedText), StandardCharsets.UTF_8)
"base64" -> decodeBase64(encodedText)
"base64url" -> {
var normalized = encodedText.replace('-', '+').replace('_', '/')
while (normalized.length % 4 != 0) {
normalized += '='
}
decodeBase64(normalized)
}
"latin1" -> String(bytesFromLatin1(encodedText), StandardCharsets.UTF_8)
"utf16" -> String(bytesFromUtf16Codec(encodedText), StandardCharsets.UTF_8)
else -> throw IllegalArgumentException("不支持的编码类型")
}
}
fun aesEncryptText(
secretText: String,
plainText: String,
mode: String,
padding: String,
ivText: String?,
keyLength: Int?
): String {
val cipherBytes = aesEncryptBytes(
secretText.toByteArray(StandardCharsets.UTF_8),
plainText.toByteArray(StandardCharsets.UTF_8),
mode,
padding,
ivText?.toByteArray(StandardCharsets.UTF_8),
keyLength
)
return Base64.encodeToString(cipherBytes, Base64.NO_WRAP)
}
fun aesDecryptText(
secretText: String,
cipherText: String,
mode: String,
padding: String,
ivText: String?,
keyLength: Int?
): String {
val cipherBytes = try {
Base64.decode(cipherText, Base64.DEFAULT)
} catch (e: Exception) {
throw IllegalArgumentException("密文Base64无效")
}
val plainBytes = aesDecryptBytes(
secretText.toByteArray(StandardCharsets.UTF_8),
cipherBytes,
mode,
padding,
ivText?.toByteArray(StandardCharsets.UTF_8),
keyLength
)
return String(plainBytes, StandardCharsets.UTF_8)
}
fun aesEncryptBytes(
secretBytes: ByteArray,
plainBytes: ByteArray,
mode: String,
padding: String,
ivBytes: ByteArray?,
keyLength: Int?
): ByteArray {
return cryptAes(true, secretBytes, plainBytes, mode, padding, ivBytes, keyLength)
}
fun aesDecryptBytes(
secretBytes: ByteArray,
cipherBytes: ByteArray,
mode: String,
padding: String,
ivBytes: ByteArray?,
keyLength: Int?
): ByteArray {
return cryptAes(false, secretBytes, cipherBytes, mode, padding, ivBytes, keyLength)
}
fun tripleDesEncryptText(secretText: String, plainText: String, mode: String, ivText: String?): String {
val encrypted = cryptTripleDes(
true,
secretText.toByteArray(StandardCharsets.UTF_8),
plainText.toByteArray(StandardCharsets.UTF_8),
mode,
ivText?.toByteArray(StandardCharsets.UTF_8)
)
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
}
fun tripleDesDecryptText(secretText: String, cipherText: String, mode: String, ivText: String?): String {
val cipherBytes = try {
Base64.decode(cipherText, Base64.DEFAULT)
} catch (e: Exception) {
throw IllegalArgumentException("密文Base64无效")
}
val decrypted = cryptTripleDes(
false,
secretText.toByteArray(StandardCharsets.UTF_8),
cipherBytes,
mode,
ivText?.toByteArray(StandardCharsets.UTF_8)
)
return String(decrypted, StandardCharsets.UTF_8)
}
fun desEncryptText(secretText: String, plainText: String, mode: String, padding: String, ivText: String?): String {
val encrypted = cryptDes(
true,
secretText.toByteArray(StandardCharsets.UTF_8),
plainText.toByteArray(StandardCharsets.UTF_8),
mode,
padding,
ivText?.toByteArray(StandardCharsets.UTF_8)
)
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
}
fun desDecryptText(secretText: String, cipherText: String, mode: String, padding: String, ivText: String?): String {
val cipherBytes = try {
Base64.decode(cipherText, Base64.DEFAULT)
} catch (e: Exception) {
throw IllegalArgumentException("密文Base64无效")
}
val decrypted = cryptDes(
false,
secretText.toByteArray(StandardCharsets.UTF_8),
cipherBytes,
mode,
padding,
ivText?.toByteArray(StandardCharsets.UTF_8)
)
return String(decrypted, StandardCharsets.UTF_8)
}
fun rc4EncryptToHex(secretText: String, plainText: String): String {
return toHex(rc4(secretText.toByteArray(StandardCharsets.UTF_8), plainText.toByteArray(StandardCharsets.UTF_8)))
}
fun rc4DecryptFromHex(secretText: String, cipherHexText: String): String {
val plainBytes = rc4(secretText.toByteArray(StandardCharsets.UTF_8), fromHex(cipherHexText))
return String(plainBytes, StandardCharsets.UTF_8)
}
fun createRsaBundle(keySize: Int): NativeRsaBundle {
return try {
val generator = KeyPairGenerator.getInstance("RSA")
generator.initialize(keySize)
val pair = generator.generateKeyPair()
val publicPem = toPem("PUBLIC KEY", pair.public.encoded)
val privatePem = toPem("PRIVATE KEY", pair.private.encoded)
NativeRsaBundle(publicPem, privatePem)
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalStateException("RSA生成失败")
}
}
fun rsaEncrypt(publicKey: String, plainText: String, outputKind: String?): String {
return try {
val rsaKey = parsePublicKey(publicKey)
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, rsaKey)
val encrypted = rsaProcess(cipher, plainText.toByteArray(StandardCharsets.UTF_8), rsaEncryptChunkSize(rsaKey), "RSA加密失败")
if (isHexOutput(outputKind)) toHex(encrypted) else Base64.encodeToString(encrypted, Base64.NO_WRAP)
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalStateException("RSA加密失败")
}
}
fun rsaDecrypt(privateKey: String, cipherText: String, outputKind: String?): String {
return try {
val rsaKey = parsePrivateKey(privateKey)
val cipherBytes = if (isHexOutput(outputKind)) {
fromHex(cipherText)
} else {
try {
Base64.decode(cipherText, Base64.DEFAULT)
} catch (e: Exception) {
throw IllegalArgumentException("RSA密文无效")
}
}
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, rsaKey)
val decrypted = rsaProcess(cipher, cipherBytes, rsaDecryptChunkSize(rsaKey), "RSA解密失败")
String(decrypted, StandardCharsets.UTF_8)
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalStateException("RSA解密失败")
}
}
private fun cryptAes(
encrypt: Boolean,
secretBytes: ByteArray,
inputBytes: ByteArray,
mode: String,
padding: String,
ivBytes: ByteArray?,
keyLength: Int?
): ByteArray {
try {
val normalizedMode = resolveAesMode(mode)
val normalizedPadding = resolvePadding(padding)
val key = SecretKeySpec(normalizeAesKey(secretBytes, keyLength), "AES")
val data = if (encrypt) padBytes(inputBytes, AES_BLOCK_SIZE, normalizedPadding) else inputBytes
if (!encrypt && data.isEmpty()) {
return ByteArray(0)
}
val cipher = Cipher.getInstance("AES/$normalizedMode/NoPadding")
initCipher(cipher, encrypt, key, normalizedMode, normalizeIv(ivBytes, AES_BLOCK_SIZE, normalizedMode))
val output = cipher.doFinal(data)
return if (encrypt) output else unpadBytes(output, AES_BLOCK_SIZE, normalizedPadding)
} catch (e: IllegalArgumentException) {
throw e
} catch (e: GeneralSecurityException) {
throw IllegalStateException(if (encrypt) "AES加密失败" else "AES解密失败")
}
}
private fun cryptTripleDes(
encrypt: Boolean,
secretBytes: ByteArray,
inputBytes: ByteArray,
mode: String,
ivBytes: ByteArray?
): ByteArray {
try {
val normalizedMode = resolveTripleDesMode(mode)
val key = SecretKeySpec(normalizeTripleDesKey(secretBytes), "DESede")
val data = if (encrypt) padBytes(inputBytes, DES_BLOCK_SIZE, "PKCS7") else inputBytes
val cipher = Cipher.getInstance("DESede/$normalizedMode/NoPadding")
initCipher(cipher, encrypt, key, normalizedMode, normalizeIv(ivBytes, DES_BLOCK_SIZE, normalizedMode))
val output = cipher.doFinal(data)
return if (encrypt) output else unpadBytes(output, DES_BLOCK_SIZE, "PKCS7")
} catch (e: IllegalArgumentException) {
throw e
} catch (e: GeneralSecurityException) {
throw IllegalStateException(if (encrypt) "3DES加密失败" else "3DES解密失败")
}
}
private fun cryptDes(
encrypt: Boolean,
secretBytes: ByteArray,
inputBytes: ByteArray,
mode: String,
padding: String,
ivBytes: ByteArray?
): ByteArray {
try {
val normalizedMode = resolveDesMode(mode)
val normalizedPadding = resolvePadding(padding)
val key = SecretKeySpec(normalizeDesKey(secretBytes), "DES")
val data = if (encrypt) padBytes(inputBytes, DES_BLOCK_SIZE, normalizedPadding) else inputBytes
if (!encrypt && data.isEmpty()) {
return ByteArray(0)
}
val cipher = Cipher.getInstance("DES/$normalizedMode/NoPadding")
initCipher(cipher, encrypt, key, normalizedMode, normalizeIv(ivBytes, DES_BLOCK_SIZE, normalizedMode))
val output = cipher.doFinal(data)
return if (encrypt) output else unpadBytes(output, DES_BLOCK_SIZE, normalizedPadding)
} catch (e: IllegalArgumentException) {
throw e
} catch (e: GeneralSecurityException) {
throw IllegalStateException(if (encrypt) "DES加密失败" else "DES解密失败")
}
}
private fun initCipher(cipher: Cipher, encrypt: Boolean, key: SecretKeySpec, mode: String, ivBytes: ByteArray?) {
if (mode == "ECB") {
cipher.init(if (encrypt) Cipher.ENCRYPT_MODE else Cipher.DECRYPT_MODE, key)
} else {
val actualIv = ivBytes ?: throw IllegalArgumentException("IV不能为空")
cipher.init(if (encrypt) Cipher.ENCRYPT_MODE else Cipher.DECRYPT_MODE, key, IvParameterSpec(actualIv))
}
}
private fun resolveDigestAlgorithm(kind: String): String {
return when (kind.lowercase()) {
"md5" -> "MD5"
"sha1" -> "SHA-1"
"sha256" -> "SHA-256"
"sha512" -> "SHA-512"
else -> throw IllegalArgumentException("不支持的摘要算法")
}
}
private fun resolveHmacAlgorithm(kind: String): String {
return when (kind.lowercase()) {
"sha1" -> "HmacSHA1"
"sha256" -> "HmacSHA256"
"sha512" -> "HmacSHA512"
else -> throw IllegalArgumentException("不支持的HMAC算法")
}
}
private fun resolveAesMode(mode: String): String {
return when (mode.uppercase()) {
"ECB", "CBC", "CFB", "CTR", "OFB" -> mode.uppercase()
// CTRGladman 在 Android 端按标准 CTR 变换兼容映射处理。
"CTRGLADMAN" -> "CTR"
else -> throw IllegalArgumentException("不支持的AES模式")
}
}
private fun resolveTripleDesMode(mode: String): String {
return when (mode.uppercase()) {
"ECB", "CBC" -> mode.uppercase()
else -> throw IllegalArgumentException("不支持的3DES模式")
}
}
private fun resolveDesMode(mode: String): String {
return when (mode.uppercase()) {
"ECB", "CBC", "CFB", "CTR", "OFB" -> mode.uppercase()
"CTRGLADMAN" -> "CTR"
else -> throw IllegalArgumentException("不支持的DES模式")
}
}
private fun resolvePadding(padding: String): String {
return when (padding.uppercase()) {
"PKCS7", "ANSI_X923", "ISO_10126", "ISO_97971", "NONE", "ZERO" -> padding.uppercase()
else -> throw IllegalArgumentException("不支持的填充")
}
}
private fun normalizeAesKey(secretBytes: ByteArray, keyLength: Int?): ByteArray {
val keySize = when (keyLength) {
null -> inferAesKeySize(secretBytes.size)
128, 192, 256 -> keyLength / 8
else -> throw IllegalArgumentException("AES密钥长度无效")
}
return fitBytes(secretBytes, keySize)
}
private fun inferAesKeySize(size: Int): Int {
return when {
size <= 16 -> 16
size <= 24 -> 24
size <= 32 -> 32
else -> throw IllegalArgumentException("AES密钥长度无效")
}
}
private fun normalizeTripleDesKey(secretBytes: ByteArray): ByteArray {
val source = fitBytes(secretBytes, 24)
if (secretBytes.size <= 16) {
System.arraycopy(source, 0, source, 16, 8)
}
return source
}
private fun normalizeDesKey(secretBytes: ByteArray): ByteArray {
if (secretBytes.isEmpty()) {
throw IllegalArgumentException("密钥不能为空")
}
return fitBytes(secretBytes, 8)
}
private fun normalizeIv(ivBytes: ByteArray?, expectedSize: Int, mode: String): ByteArray? {
if (mode == "ECB") return null
val bytes = ivBytes ?: return null
return fitBytes(bytes, expectedSize)
}
private fun fitBytes(source: ByteArray, targetSize: Int): ByteArray {
val output = ByteArray(targetSize)
val copySize = minOf(source.size, targetSize)
System.arraycopy(source, 0, output, 0, copySize)
return output
}
private fun padBytes(input: ByteArray, blockSize: Int, padding: String): ByteArray {
if (padding == "NONE") {
if (input.size % blockSize != 0) {
throw IllegalArgumentException("数据长度不是块大小整数倍")
}
return input
}
val padSize = blockSize - (input.size % blockSize).let { if (it == 0) blockSize else it }
val actualPadSize = if (padSize == 0) blockSize else padSize
val output = ByteArray(input.size + actualPadSize)
System.arraycopy(input, 0, output, 0, input.size)
when (padding) {
"PKCS7" -> fillPad(output, input.size, actualPadSize, actualPadSize.toByte())
"ANSI_X923" -> output[output.lastIndex] = actualPadSize.toByte()
"ISO_10126" -> {
val randomBytes = ByteArray(actualPadSize - 1)
if (randomBytes.isNotEmpty()) {
secureRandom.nextBytes(randomBytes)
System.arraycopy(randomBytes, 0, output, input.size, randomBytes.size)
}
output[output.lastIndex] = actualPadSize.toByte()
}
"ISO_97971" -> {
output[input.size] = 0x80.toByte()
}
"ZERO" -> {
}
else -> throw IllegalArgumentException("不支持的填充")
}
return output
}
private fun fillPad(output: ByteArray, start: Int, count: Int, value: Byte) {
for (i in start until start + count) {
output[i] = value
}
}
private fun unpadBytes(input: ByteArray, blockSize: Int, padding: String): ByteArray {
if (padding == "NONE") {
return input
}
if (input.isEmpty() || input.size % blockSize != 0) {
throw IllegalArgumentException("密文长度无效")
}
val dataLength = when (padding) {
"PKCS7" -> {
val padSize = input.last().toInt() and 0xFF
validatePadSize(padSize, blockSize)
for (i in input.size - padSize until input.size) {
if ((input[i].toInt() and 0xFF) != padSize) {
throw IllegalArgumentException("填充无效")
}
}
input.size - padSize
}
"ANSI_X923" -> {
val padSize = input.last().toInt() and 0xFF
validatePadSize(padSize, blockSize)
for (i in input.size - padSize until input.lastIndex) {
if (input[i].toInt() != 0) {
throw IllegalArgumentException("填充无效")
}
}
input.size - padSize
}
"ISO_10126" -> {
val padSize = input.last().toInt() and 0xFF
validatePadSize(padSize, blockSize)
input.size - padSize
}
"ISO_97971" -> {
var index = input.lastIndex
while (index >= 0 && input[index].toInt() == 0) {
index -= 1
}
if (index < 0 || input[index] != 0x80.toByte()) {
throw IllegalArgumentException("填充无效")
}
index
}
"ZERO" -> {
var index = input.size
while (index > 0 && input[index - 1].toInt() == 0) {
index -= 1
}
index
}
else -> throw IllegalArgumentException("不支持的填充")
}
return input.copyOf(dataLength)
}
private fun validatePadSize(padSize: Int, blockSize: Int) {
if (padSize <= 0 || padSize > blockSize) {
throw IllegalArgumentException("填充无效")
}
}
private fun rc4(secretBytes: ByteArray, inputBytes: ByteArray): ByteArray {
if (secretBytes.isEmpty()) {
throw IllegalArgumentException("RC4密钥不能为空")
}
val s = IntArray(256) { it }
var j = 0
for (i in 0 until 256) {
j = (j + s[i] + (secretBytes[i % secretBytes.size].toInt() and 0xFF)) and 0xFF
val tmp = s[i]
s[i] = s[j]
s[j] = tmp
}
val output = ByteArray(inputBytes.size)
var i = 0
j = 0
for (index in inputBytes.indices) {
i = (i + 1) and 0xFF
j = (j + s[i]) and 0xFF
val tmp = s[i]
s[i] = s[j]
s[j] = tmp
val keyByte = s[(s[i] + s[j]) and 0xFF]
output[index] = (inputBytes[index].toInt() xor keyByte).toByte()
}
return output
}
private fun parsePublicKey(publicKey: String): RSAPublicKey {
try {
val keyBytes = decodePem(publicKey, "PUBLIC KEY")
val keySpec = X509EncodedKeySpec(keyBytes)
return KeyFactory.getInstance("RSA").generatePublic(keySpec) as RSAPublicKey
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalArgumentException("公钥无效")
}
}
private fun parsePrivateKey(privateKey: String): RSAPrivateKey {
try {
val keyBytes = decodePem(privateKey, "PRIVATE KEY")
val keySpec = PKCS8EncodedKeySpec(keyBytes)
return KeyFactory.getInstance("RSA").generatePrivate(keySpec) as RSAPrivateKey
} catch (e: IllegalArgumentException) {
throw e
} catch (e: Exception) {
throw IllegalArgumentException("私钥无效")
}
}
private fun decodePem(pemText: String, label: String): ByteArray {
val header = "-----BEGIN $label-----"
val footer = "-----END $label-----"
val trimmed = pemText.trim()
if (!trimmed.startsWith(header) || !trimmed.endsWith(footer)) {
throw IllegalArgumentException("PEM格式无效")
}
val content = trimmed
.removePrefix(header)
.removeSuffix(footer)
.replace("\\s".toRegex(), "")
return try {
Base64.decode(content, Base64.DEFAULT)
} catch (e: Exception) {
throw IllegalArgumentException("PEM内容无效")
}
}
private fun toPem(label: String, keyBytes: ByteArray): String {
val base64 = Base64.encodeToString(keyBytes, Base64.NO_WRAP)
val builder = StringBuilder()
builder.append("-----BEGIN ").append(label).append("-----\n")
var index = 0
while (index < base64.length) {
val end = minOf(index + 64, base64.length)
builder.append(base64, index, end).append('\n')
index = end
}
builder.append("-----END ").append(label).append("-----")
return builder.toString()
}
private fun rsaEncryptChunkSize(key: RSAPublicKey): Int {
return (key.modulus.bitLength() + 7) / 8 - 11
}
private fun rsaDecryptChunkSize(key: RSAPrivateKey): Int {
return (key.modulus.bitLength() + 7) / 8
}
private fun rsaProcess(cipher: Cipher, input: ByteArray, chunkSize: Int, failureMessage: String): ByteArray {
if (chunkSize <= 0) {
throw IllegalArgumentException("RSA密钥无效")
}
val output = ByteArrayOutputStream()
var offset = 0
try {
while (offset < input.size) {
val size = minOf(chunkSize, input.size - offset)
output.write(cipher.doFinal(input, offset, size))
offset += size
}
return output.toByteArray()
} catch (e: Exception) {
throw IllegalStateException(failureMessage)
} finally {
output.close()
}
}
private fun isHexOutput(outputKind: String?): Boolean {
return outputKind?.lowercase() == "hex"
}
private fun latin1FromBytes(bytes: ByteArray): String {
val chars = CharArray(bytes.size)
for (i in bytes.indices) {
chars[i] = (bytes[i].toInt() and 0xFF).toChar()
}
return String(chars)
}
private fun bytesFromLatin1(text: String): ByteArray {
val output = ByteArray(text.length)
for (i in text.indices) {
output[i] = (text[i].code and 0xFF).toByte()
}
return output
}
private fun utf16CodecFromBytes(bytes: ByteArray): String {
if (bytes.isEmpty()) {
return ""
}
val chars = CharArray((bytes.size + 1) / 2)
var byteIndex = 0
var charIndex = 0
while (byteIndex < bytes.size) {
val high = bytes[byteIndex].toInt() and 0xFF
val low = if (byteIndex + 1 < bytes.size) bytes[byteIndex + 1].toInt() and 0xFF else 0
chars[charIndex] = ((high shl 8) or low).toChar()
byteIndex += 2
charIndex += 1
}
return String(chars)
}
private fun bytesFromUtf16Codec(text: String): ByteArray {
val output = ByteArray(text.length * 2)
var index = 0
for (charValue in text) {
val code = charValue.code
output[index] = ((code ushr 8) and 0xFF).toByte()
output[index + 1] = (code and 0xFF).toByte()
index += 2
}
return output
}
private fun toHex(bytes: ByteArray): String {
val chars = CharArray(bytes.size * 2)
val digits = "0123456789abcdef"
for (i in bytes.indices) {
val value = bytes[i].toInt() and 0xFF
chars[i * 2] = digits[value ushr 4]
chars[i * 2 + 1] = digits[value and 0x0F]
}
return String(chars)
}
private fun fromHex(hexText: String): ByteArray {
val normalized = hexText.trim()
if (normalized.length % 2 != 0) {
throw IllegalArgumentException("Hex无效")
}
val output = ByteArray(normalized.length / 2)
for (i in output.indices) {
val start = i * 2
val value = normalized.substring(start, start + 2).toIntOrNull(16)
?: throw IllegalArgumentException("Hex无效")
output[i] = value.toByte()
}
return output
}
}
@@ -0,0 +1,3 @@
{
"minSdkVersion": "21"
}
@@ -0,0 +1,241 @@
import NativeCipherCore from 'uts.sdk.modules.laoqianjunziCrypto.NativeCipherCore'
import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts'
import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts'
import type { CompatBridge, CompatCrypto } from '../shared/compat.uts'
import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts'
function toNativeBytes(source : Uint8Array) : ByteArray {
const output = new ByteArray(source.length.toInt())
let index = 0
while (index < source.length) {
output[index.toInt()] = source[index.toInt()].toByte()
index += 1
}
return output
}
function fromNativeBytes(source : ByteArray) : Uint8Array {
const output = new Uint8Array(source.size)
let index = 0
while (index < source.size) {
output[index.toInt()] = source[index.toInt()] & 0xff
index += 1
}
return output
}
function byteToHex(value : number) : string {
let part = value.toString(16)
if (part.length < 2) {
part = '0' + part
}
return part
}
export function defaultTextCipherOptions() : CryptoTextCipherOptions {
return createDefaultTextCipherOptions()
}
export function defaultByteCipherOptions() : CryptoByteCipherOptions {
return createDefaultByteCipherOptions()
}
export function defaultTripleDesOptions() : CryptoTripleDesOptions {
return createDefaultTripleDesOptions()
}
export function defaultDesOptions() : CryptoDesOptions {
return createDefaultDesOptions()
}
export function describeCryptoRuntime() : CryptoRuntimeSnapshot {
return buildRuntimeSnapshot('Android', 'JCA', true, true)
}
export function encodeText(codec : CryptoCodecKind, plainText : string) : string {
return NativeCipherCore.encodeCodec(codec, plainText)
}
export function decodeText(codec : CryptoCodecKind, encodedText : string) : string {
return NativeCipherCore.decodeCodec(codec, encodedText)
}
export function digestText(kind : CryptoDigestKind, plainText : string) : string {
return NativeCipherCore.digestHex(kind, plainText)
}
export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string {
return NativeCipherCore.hmacHex(kind, secretText, plainText)
}
export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
const actualKeyLength = actual.keyLength == null ? null : actual.keyLength.toInt()
return NativeCipherCore.aesEncryptText(secretText, plainText, actual.mode, actual.padding, actual.ivText, actualKeyLength)
}
export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
const actualKeyLength = actual.keyLength == null ? null : actual.keyLength.toInt()
return NativeCipherCore.aesDecryptText(secretText, cipherText, actual.mode, actual.padding, actual.ivText, actualKeyLength)
}
export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
const actualKeyLength = actual.keyLength == null ? null : actual.keyLength.toInt()
return fromNativeBytes(NativeCipherCore.aesEncryptBytes(toNativeBytes(secretBytes), toNativeBytes(plainBytes), actual.mode, actual.padding, actual.ivBytes == null ? null : toNativeBytes(actual.ivBytes), actualKeyLength))
}
export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
const actualKeyLength = actual.keyLength == null ? null : actual.keyLength.toInt()
return fromNativeBytes(NativeCipherCore.aesDecryptBytes(toNativeBytes(secretBytes), toNativeBytes(cipherBytes), actual.mode, actual.padding, actual.ivBytes == null ? null : toNativeBytes(actual.ivBytes), actualKeyLength))
}
export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return NativeCipherCore.tripleDesEncryptText(secretText, plainText, actual.mode, actual.ivText)
}
export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return NativeCipherCore.tripleDesDecryptText(secretText, cipherText, actual.mode, actual.ivText)
}
export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return NativeCipherCore.desEncryptText(secretText, plainText, actual.mode, actual.padding, actual.ivText)
}
export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return NativeCipherCore.desDecryptText(secretText, cipherText, actual.mode, actual.padding, actual.ivText)
}
export function rc4EncryptText(secretText : string, plainText : string) : string {
return NativeCipherCore.rc4EncryptToHex(secretText, plainText)
}
export function rc4DecryptText(secretText : string, cipherHexText : string) : string {
return NativeCipherCore.rc4DecryptFromHex(secretText, cipherHexText)
}
export function createRsaPair(keySize : number) : CryptoRsaPair {
const bundle = NativeCipherCore.createRsaBundle(keySize.toInt())
return {
publicKey: bundle.publicKey,
privateKey: bundle.privateKey
} as CryptoRsaPair
}
export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string {
return NativeCipherCore.rsaEncrypt(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string {
return NativeCipherCore.rsaDecrypt(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
function compatBridge() : CompatBridge {
return createCompatBridge(
(codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText),
(codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText),
(kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText),
(kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText),
(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options),
(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options),
(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options),
(secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options),
(keySize : number) : CryptoRsaPair => createRsaPair(keySize),
(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind),
(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind),
(secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText),
(secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText)
)
}
export function base64Encode(input : string) : string {
return base64EncodeCompat(compatBridge(), input)
}
export function base64Decode(input : string) : string {
return base64DecodeCompat(compatBridge(), input)
}
export function md5(input : string) : string {
return md5Compat(compatBridge(), input)
}
export function sha1(input : string) : string {
return sha1Compat(compatBridge(), input)
}
export function sha256(input : string) : string {
return sha256Compat(compatBridge(), input)
}
export function sha512(input : string) : string {
return sha512Compat(compatBridge(), input)
}
export function hmacSha1(key : string, data : string) : string {
return hmacSha1Compat(compatBridge(), key, data)
}
export function hmacSha256(key : string, data : string) : string {
return hmacSha256Compat(compatBridge(), key, data)
}
export function hmacSha512(key : string, data : string) : string {
return hmacSha512Compat(compatBridge(), key, data)
}
export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desEncryptCompat(compatBridge(), key, data, mode, iv)
}
export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desDecryptCompat(compatBridge(), key, data, mode, iv)
}
export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair {
return generateRsaKeyPairCompat(compatBridge(), keySize)
}
export function rsaEncrypt(publicKey : string, data : string) : string {
return rsaEncryptCompat(compatBridge(), publicKey, data)
}
export function rsaDecrypt(privateKey : string, data : string) : string {
return rsaDecryptCompat(compatBridge(), privateKey, data)
}
export function rc4Encrypt(key : string, data : string) : string {
return rc4EncryptCompat(compatBridge(), key, data)
}
export function rc4Decrypt(key : string, data : string) : string {
return rc4DecryptCompat(compatBridge(), key, data)
}
export function useCrypto() : CompatCrypto {
return createCompatCrypto(compatBridge())
}
@@ -0,0 +1,5 @@
{
"dependencies": {
"@ohos/crypto-js": "2.0.4"
}
}
@@ -0,0 +1,428 @@
import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts'
import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts'
import { NativeHarmonyRsaCore } from './native_rsa.ets'
import { CryptoJS } from '@ohos/crypto-js'
import type { CompatBridge, CompatCrypto } from '../shared/compat.uts'
import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts'
type WordValue = CryptoJS.lib.WordArray
function bytesToWordArray(input : Uint8Array) : WordValue {
const words = [] as 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 CryptoJS.lib.WordArray.create(words, input.length)
}
function wordArrayToBytes(input : WordValue) : Uint8Array {
const output = new Uint8Array(input.sigBytes)
let index = 0
while (index < input.sigBytes) {
const wordIndex = Math.floor(index / 4)
const offset = index % 4
output[index] = (input.words[wordIndex] >>> (24 - offset * 8)) & 0xff
index += 1
}
return output
}
function resolveAesMode(mode : string) : CryptoJS.BlockCipherMode {
if (mode == 'CBC') {
return CryptoJS.mode.CBC
}
if (mode == 'CFB') {
return CryptoJS.mode.CFB
}
if (mode == 'CTR') {
return CryptoJS.mode.CTR
}
if (mode == 'CTRGladman') {
return CryptoJS.mode.CTRGladman
}
if (mode == 'ECB') {
return CryptoJS.mode.ECB
}
return CryptoJS.mode.OFB
}
function resolvePadding(padding : string) : CryptoJS.Padding {
if (padding == 'PKCS7') {
return CryptoJS.pad.Pkcs7
}
if (padding == 'ANSI_X923') {
return CryptoJS.pad.AnsiX923
}
if (padding == 'ISO_10126') {
return CryptoJS.pad.Iso10126
}
if (padding == 'ISO_97971') {
return CryptoJS.pad.Iso97971
}
if (padding == 'NONE') {
return CryptoJS.pad.NoPadding
}
return CryptoJS.pad.ZeroPadding
}
function fitIvText(ivText : string | null, size : number) : WordValue {
const bytes = new Uint8Array(size)
if (ivText != null) {
const parsed : WordValue = CryptoJS.enc.Utf8.parse(ivText)
const source = wordArrayToBytes(parsed)
let index = 0
while (index < size && index < source.length) {
bytes[index] = source[index]
index += 1
}
}
return bytesToWordArray(bytes)
}
function fitIvBytes(ivBytes : Uint8Array | null, size : number) : WordValue {
const bytes = new Uint8Array(size)
if (ivBytes != null) {
let index = 0
while (index < size && index < ivBytes.length) {
bytes[index] = ivBytes[index]
index += 1
}
}
return bytesToWordArray(bytes)
}
function fitKeyWordFromText(secretText : string, keyLength : number | null) : WordValue {
let target = 16
if (keyLength == 24 || keyLength == 32) {
target = keyLength
}
const sourceWord : WordValue = CryptoJS.enc.Utf8.parse(secretText)
const source = wordArrayToBytes(sourceWord)
const output = new Uint8Array(target)
let index = 0
while (index < target && index < source.length) {
output[index] = source[index]
index += 1
}
return bytesToWordArray(output)
}
function fitKeyWordFromBytes(secretBytes : Uint8Array, keyLength : number | null) : WordValue {
let target = 16
if (keyLength == 24 || keyLength == 32) {
target = keyLength
}
const output = new Uint8Array(target)
let index = 0
while (index < target && index < secretBytes.length) {
output[index] = secretBytes[index]
index += 1
}
return bytesToWordArray(output)
}
export function defaultTextCipherOptions() : CryptoTextCipherOptions {
return createDefaultTextCipherOptions()
}
export function defaultByteCipherOptions() : CryptoByteCipherOptions {
return createDefaultByteCipherOptions()
}
export function defaultTripleDesOptions() : CryptoTripleDesOptions {
return createDefaultTripleDesOptions()
}
export function defaultDesOptions() : CryptoDesOptions {
return createDefaultDesOptions()
}
export function describeCryptoRuntime() : CryptoRuntimeSnapshot {
return buildRuntimeSnapshot('Harmony', '@ohos/crypto-js', true, true)
}
export function encodeText(codec : CryptoCodecKind, plainText : string) : string {
if (codec == 'utf8') {
return plainText
}
const word : WordValue = CryptoJS.enc.Utf8.parse(plainText)
if (codec == 'hex') {
return CryptoJS.enc.Hex.stringify(word)
}
if (codec == 'base64') {
return CryptoJS.enc.Base64.stringify(word)
}
if (codec == 'base64url') {
return CryptoJS.enc.Base64url.stringify(word)
}
if (codec == 'latin1') {
return CryptoJS.enc.Latin1.stringify(word)
}
return CryptoJS.enc.Utf16.stringify(word)
}
export function decodeText(codec : CryptoCodecKind, encodedText : string) : string {
if (codec == 'utf8') {
return encodedText
}
if (codec == 'hex') {
return CryptoJS.enc.Hex.parse(encodedText).toString(CryptoJS.enc.Utf8)
}
if (codec == 'base64') {
return CryptoJS.enc.Base64.parse(encodedText).toString(CryptoJS.enc.Utf8)
}
if (codec == 'base64url') {
return CryptoJS.enc.Base64url.parse(encodedText).toString(CryptoJS.enc.Utf8)
}
if (codec == 'latin1') {
return CryptoJS.enc.Latin1.parse(encodedText).toString(CryptoJS.enc.Utf8)
}
return CryptoJS.enc.Utf16.parse(encodedText).toString(CryptoJS.enc.Utf8)
}
export function digestText(kind : CryptoDigestKind, plainText : string) : string {
if (kind == 'md5') {
return CryptoJS.MD5(plainText).toString()
}
if (kind == 'sha1') {
return CryptoJS.SHA1(plainText).toString()
}
if (kind == 'sha256') {
return CryptoJS.SHA256(plainText).toString()
}
return CryptoJS.SHA512(plainText).toString()
}
export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string {
if (kind == 'sha1') {
return CryptoJS.HmacSHA1(plainText, secretText).toString()
}
if (kind == 'sha256') {
return CryptoJS.HmacSHA256(plainText, secretText).toString()
}
return CryptoJS.HmacSHA512(plainText, secretText).toString()
}
export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.AES.encrypt(plainText, fitKeyWordFromText(secretText, actual.keyLength), {
iv: fitIvText(actual.ivText, 16),
mode: resolveAesMode(actual.mode),
padding: resolvePadding(actual.padding)
})
return encrypted.toString()
}
export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
const decrypted : WordValue = CryptoJS.AES.decrypt(cipherText, fitKeyWordFromText(secretText, actual.keyLength), {
iv: fitIvText(actual.ivText, 16),
mode: resolveAesMode(actual.mode),
padding: resolvePadding(actual.padding)
})
return decrypted.toString(CryptoJS.enc.Utf8)
}
export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.AES.encrypt(bytesToWordArray(plainBytes), fitKeyWordFromBytes(secretBytes, actual.keyLength), {
iv: fitIvBytes(actual.ivBytes, 16),
mode: resolveAesMode(actual.mode),
padding: resolvePadding(actual.padding)
})
return wordArrayToBytes(encrypted.ciphertext)
}
export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
const payload : UTSJSONObject = {
ciphertext: bytesToWordArray(cipherBytes)
}
const decrypted : WordValue = CryptoJS.AES.decrypt(CryptoJS.lib.CipherParams.create(payload), fitKeyWordFromBytes(secretBytes, actual.keyLength), {
iv: fitIvBytes(actual.ivBytes, 16),
mode: resolveAesMode(actual.mode),
padding: resolvePadding(actual.padding)
})
return wordArrayToBytes(decrypted)
}
export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.TripleDES.encrypt(plainText, fitKeyWordFromText(secretText, 24), {
iv: fitIvText(actual.ivText, 8),
mode: actual.mode == 'ECB' ? CryptoJS.mode.ECB : CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
return encrypted.toString()
}
export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
const decrypted : WordValue = CryptoJS.TripleDES.decrypt(cipherText, fitKeyWordFromText(secretText, 24), {
iv: fitIvText(actual.ivText, 8),
mode: actual.mode == 'ECB' ? CryptoJS.mode.ECB : CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
return decrypted.toString(CryptoJS.enc.Utf8)
}
export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.DES.encrypt(plainText, fitKeyWordFromText(secretText, 8), {
iv: fitIvText(actual.ivText, 8),
mode: resolveAesMode(actual.mode),
padding: resolvePadding(actual.padding)
})
return encrypted.toString()
}
export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
const decrypted : WordValue = CryptoJS.DES.decrypt(cipherText, fitKeyWordFromText(secretText, 8), {
iv: fitIvText(actual.ivText, 8),
mode: resolveAesMode(actual.mode),
padding: resolvePadding(actual.padding)
})
return decrypted.toString(CryptoJS.enc.Utf8)
}
export function rc4EncryptText(secretText : string, plainText : string) : string {
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.RC4.encrypt(plainText, CryptoJS.enc.Utf8.parse(secretText))
return encrypted.ciphertext.toString(CryptoJS.enc.Hex)
}
export function rc4DecryptText(secretText : string, cipherHexText : string) : string {
const payload : UTSJSONObject = {
ciphertext: CryptoJS.enc.Hex.parse(cipherHexText)
}
const decrypted : WordValue = CryptoJS.RC4.decrypt(CryptoJS.lib.CipherParams.create(payload), CryptoJS.enc.Utf8.parse(secretText))
return decrypted.toString(CryptoJS.enc.Utf8)
}
export function createRsaPair(keySize : number) : CryptoRsaPair {
const bundle = NativeHarmonyRsaCore.createRsaBundle(keySize)
return {
publicKey: bundle.publicKey,
privateKey: bundle.privateKey
} as CryptoRsaPair
}
export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string {
return NativeHarmonyRsaCore.rsaEncrypt(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string {
return NativeHarmonyRsaCore.rsaDecrypt(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
function compatBridge() : CompatBridge {
return createCompatBridge(
(codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText),
(codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText),
(kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText),
(kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText),
(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options),
(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options),
(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options),
(secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options),
(keySize : number) : CryptoRsaPair => createRsaPair(keySize),
(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind),
(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind),
(secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText),
(secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText)
)
}
export function base64Encode(input : string) : string {
return base64EncodeCompat(compatBridge(), input)
}
export function base64Decode(input : string) : string {
return base64DecodeCompat(compatBridge(), input)
}
export function md5(input : string) : string {
return md5Compat(compatBridge(), input)
}
export function sha1(input : string) : string {
return sha1Compat(compatBridge(), input)
}
export function sha256(input : string) : string {
return sha256Compat(compatBridge(), input)
}
export function sha512(input : string) : string {
return sha512Compat(compatBridge(), input)
}
export function hmacSha1(key : string, data : string) : string {
return hmacSha1Compat(compatBridge(), key, data)
}
export function hmacSha256(key : string, data : string) : string {
return hmacSha256Compat(compatBridge(), key, data)
}
export function hmacSha512(key : string, data : string) : string {
return hmacSha512Compat(compatBridge(), key, data)
}
export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desEncryptCompat(compatBridge(), key, data, mode, iv)
}
export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desDecryptCompat(compatBridge(), key, data, mode, iv)
}
export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair {
return generateRsaKeyPairCompat(compatBridge(), keySize)
}
export function rsaEncrypt(publicKey : string, data : string) : string {
return rsaEncryptCompat(compatBridge(), publicKey, data)
}
export function rsaDecrypt(privateKey : string, data : string) : string {
return rsaDecryptCompat(compatBridge(), privateKey, data)
}
export function rc4Encrypt(key : string, data : string) : string {
return rc4EncryptCompat(compatBridge(), key, data)
}
export function rc4Decrypt(key : string, data : string) : string {
return rc4DecryptCompat(compatBridge(), key, data)
}
export function useCrypto() : CompatCrypto {
return createCompatCrypto(compatBridge())
}
@@ -0,0 +1,176 @@
import cryptoFramework from '@ohos.security.cryptoFramework'
import util from '@ohos.util'
const UTF8_ENCODER = new util.TextEncoder('utf-8')
const UTF8_DECODER = util.TextDecoder.create('utf-8')
const BASE64_HELPER = new util.Base64Helper()
const PUBLIC_PEM_FORMATS: string[] = ['X509', 'x509', 'SPKI', 'spki', 'PKCS1', 'pkcs1']
const PRIVATE_PEM_FORMATS: string[] = ['PKCS8', 'pkcs8', 'PKCS1', 'pkcs1']
const RSA_GENERATOR_ALGS: string[] = ['RSA']
const RSA_CIPHER_ALGS: string[] = [
'RSA/ECB/PKCS1Padding',
'RSA/None/PKCS1Padding',
'RSA/None/OAEPWithSHA256AndMGF1Padding',
'RSA'
]
export class NativeRsaBundle {
public publicKey: string
public privateKey: string
constructor(publicKey: string = '', privateKey: string = '') {
this.publicKey = publicKey
this.privateKey = privateKey
}
}
export class NativeHarmonyRsaCore {
static createRsaBundle(keySize: number): NativeRsaBundle {
const generator = NativeHarmonyRsaCore.createKeyGenerator(keySize)
const keyPair = generator.generateKeyPairSync()
const publicKey = NativeHarmonyRsaCore.exportPublicPem(keyPair.pubKey)
const privateKey = NativeHarmonyRsaCore.exportPrivatePem(keyPair.priKey)
return new NativeRsaBundle(publicKey, privateKey)
}
static rsaEncrypt(publicKey: string, plainText: string, outputKind: string | null): string {
const keyPair = NativeHarmonyRsaCore.importPemKeyPair(publicKey, null)
const cipher = NativeHarmonyRsaCore.createCipher()
cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null)
const output = cipher.doFinalSync({ data: NativeHarmonyRsaCore.encodeUtf8(plainText) })
return NativeHarmonyRsaCore.encodeCipherOutput(output.data, outputKind)
}
static rsaDecrypt(privateKey: string, cipherText: string, outputKind: string | null): string {
const keyPair = NativeHarmonyRsaCore.importPemKeyPair(null, privateKey)
const cipher = NativeHarmonyRsaCore.createCipher()
cipher.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null)
const sourceBytes = NativeHarmonyRsaCore.decodeCipherInput(cipherText)
const output = cipher.doFinalSync({ data: sourceBytes })
return NativeHarmonyRsaCore.decodePlainOutput(output.data, outputKind)
}
private static createKeyGenerator(keySize: number): cryptoFramework.AsyKeyGenerator {
const candidates: string[] = [
'RSA' + keySize.toString(),
'RSA|' + keySize.toString(),
'RSA/' + keySize.toString(),
'RSA'
]
let lastError: Error | null = null
for (const algName of candidates) {
try {
return cryptoFramework.createAsyKeyGenerator(algName)
} catch (error) {
lastError = error as Error
}
}
throw lastError ?? new Error('create RSA key generator failed')
}
private static importPemKeyPair(publicKey: string | null, privateKey: string | null): cryptoFramework.KeyPair {
let lastError: Error | null = null
for (const algName of RSA_GENERATOR_ALGS) {
try {
const generator = cryptoFramework.createAsyKeyGenerator(algName)
return generator.convertPemKeySync(publicKey, privateKey)
} catch (error) {
lastError = error as Error
}
}
throw lastError ?? new Error('import RSA PEM failed')
}
private static createCipher(): cryptoFramework.Cipher {
let lastError: Error | null = null
for (const algName of RSA_CIPHER_ALGS) {
try {
return cryptoFramework.createCipher(algName)
} catch (error) {
lastError = error as Error
}
}
throw lastError ?? new Error('create RSA cipher failed')
}
private static exportPublicPem(publicKey: cryptoFramework.PubKey): string {
let lastError: Error | null = null
for (const formatName of PUBLIC_PEM_FORMATS) {
try {
return publicKey.getEncodedPem(formatName)
} catch (error) {
lastError = error as Error
}
}
throw lastError ?? new Error('export public PEM failed')
}
private static exportPrivatePem(privateKey: cryptoFramework.PriKey): string {
let lastError: Error | null = null
for (const formatName of PRIVATE_PEM_FORMATS) {
try {
return privateKey.getEncodedPem(formatName)
} catch (error) {
lastError = error as Error
}
}
throw lastError ?? new Error('export private PEM failed')
}
private static encodeUtf8(input: string): Uint8Array {
return UTF8_ENCODER.encodeInto(input)
}
private static decodeUtf8(input: Uint8Array): string {
return UTF8_DECODER.decode(input)
}
private static encodeCipherOutput(input: Uint8Array, outputKind: string | null): string {
if (outputKind === 'hex') {
return NativeHarmonyRsaCore.bytesToHex(input)
}
return BASE64_HELPER.encodeToStringSync(input)
}
private static decodePlainOutput(input: Uint8Array, outputKind: string | null): string {
if (outputKind === 'hex') {
return NativeHarmonyRsaCore.bytesToHex(input)
}
return NativeHarmonyRsaCore.decodeUtf8(input)
}
private static decodeCipherInput(cipherText: string): Uint8Array {
const normalized = cipherText.trim()
if (NativeHarmonyRsaCore.looksLikeHex(normalized)) {
return NativeHarmonyRsaCore.hexToBytes(normalized)
}
return BASE64_HELPER.decodeSync(normalized)
}
private static looksLikeHex(input: string): boolean {
return input.length > 0 && input.length % 2 == 0 && /^[0-9a-fA-F]+$/.test(input)
}
private static bytesToHex(input: Uint8Array): string {
let output = ''
for (let i = 0; i < input.length; i++) {
const item = input[i]
if (item < 16) {
output += '0'
}
output += item.toString(16)
}
return output
}
private static hexToBytes(input: string): Uint8Array {
const length = input.length / 2
const output = new Uint8Array(length)
for (let i = 0; i < length; i++) {
const offset = i * 2
output[i] = Number.parseInt(input.substring(offset, offset + 2), 16)
}
return output
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,251 @@
import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts'
import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts'
import type { CompatBridge, CompatCrypto } from '../shared/compat.uts'
import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts'
function toNumberArray(source : Uint8Array) : number[] {
const output = [] as number[]
let index = 0
while (index < source.length) {
output.push(source[index])
index += 1
}
return output
}
function fromNumberArray(source : number[]) : Uint8Array {
const output = new Uint8Array(source.length)
let index = 0
while (index < source.length) {
output[index] = source[index] & 0xff
index += 1
}
return output
}
function unwrapString(value : string | null, message : string) : string {
if (value == null) {
throw new Error(message)
}
return value
}
function unwrapNumberArray(value : number[] | null, message : string) : number[] {
if (value == null) {
throw new Error(message)
}
return value
}
function unwrapBundle(value : NativeRsaBundle | null, message : string) : NativeRsaBundle {
if (value == null) {
throw new Error(message)
}
return value
}
export function defaultTextCipherOptions() : CryptoTextCipherOptions {
return createDefaultTextCipherOptions()
}
export function defaultByteCipherOptions() : CryptoByteCipherOptions {
return createDefaultByteCipherOptions()
}
export function defaultTripleDesOptions() : CryptoTripleDesOptions {
return createDefaultTripleDesOptions()
}
export function defaultDesOptions() : CryptoDesOptions {
return createDefaultDesOptions()
}
export function describeCryptoRuntime() : CryptoRuntimeSnapshot {
return buildRuntimeSnapshot('iOS', 'CommonCrypto', true, true)
}
export function encodeText(codec : CryptoCodecKind, plainText : string) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.encodeCodec(codec, plainText), '?'), '编码失败')
}
export function decodeText(codec : CryptoCodecKind, encodedText : string) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.decodeCodec(codec, encodedText), '?'), '解码失败')
}
export function digestText(kind : CryptoDigestKind, plainText : string) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.digestHex(kind, plainText), '?'), '摘要失败')
}
export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.hmacHex(kind, secretText, plainText), '?'), 'HMAC失败')
}
export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
return unwrapString(UTSiOS.try(NativeCipherCore.aesEncryptText(secretText, plainText, actual.mode, actual.padding, actual.ivText, actual.keyLength), '?'), 'AES加密失败')
}
export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
return unwrapString(UTSiOS.try(NativeCipherCore.aesDecryptText(secretText, cipherText, actual.mode, actual.padding, actual.ivText, actual.keyLength), '?'), 'AES解密失败')
}
export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
const response = unwrapNumberArray(UTSiOS.try(NativeCipherCore.aesEncryptBytes(toNumberArray(secretBytes), toNumberArray(plainBytes), actual.mode, actual.padding, actual.ivBytes == null ? null : toNumberArray(actual.ivBytes), actual.keyLength), '?'), 'AES字节加密失败')
return fromNumberArray(response)
}
export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
const response = unwrapNumberArray(UTSiOS.try(NativeCipherCore.aesDecryptBytes(toNumberArray(secretBytes), toNumberArray(cipherBytes), actual.mode, actual.padding, actual.ivBytes == null ? null : toNumberArray(actual.ivBytes), actual.keyLength), '?'), 'AES字节解密失败')
return fromNumberArray(response)
}
export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return unwrapString(UTSiOS.try(NativeCipherCore.tripleDesEncryptText(secretText, plainText, actual.mode, actual.ivText), '?'), '3DES加密失败')
}
export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return unwrapString(UTSiOS.try(NativeCipherCore.tripleDesDecryptText(secretText, cipherText, actual.mode, actual.ivText), '?'), '3DES解密失败')
}
export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return unwrapString(UTSiOS.try(NativeCipherCore.desEncryptText(secretText, plainText, actual.mode, actual.padding, actual.ivText), '?'), 'DES加密失败')
}
export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return unwrapString(UTSiOS.try(NativeCipherCore.desDecryptText(secretText, cipherText, actual.mode, actual.padding, actual.ivText), '?'), 'DES解密失败')
}
export function rc4EncryptText(secretText : string, plainText : string) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.rc4EncryptToHex(secretText, plainText), '?'), 'RC4加密失败')
}
export function rc4DecryptText(secretText : string, cipherHexText : string) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.rc4DecryptFromHex(secretText, cipherHexText), '?'), 'RC4解密失败')
}
export function createRsaPair(keySize : number) : CryptoRsaPair {
const bundle = unwrapBundle(UTSiOS.try(NativeCipherCore.createRsaBundle(keySize), '?'), 'RSA密钥生成失败')
return {
publicKey: bundle.publicKey,
privateKey: bundle.privateKey
} as CryptoRsaPair
}
export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.rsaEncrypt(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64'), '?'), 'RSA加密失败')
}
export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string {
return unwrapString(UTSiOS.try(NativeCipherCore.rsaDecrypt(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64'), '?'), 'RSA解密失败')
}
function compatBridge() : CompatBridge {
return createCompatBridge(
(codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText),
(codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText),
(kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText),
(kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText),
(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options),
(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options),
(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options),
(secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options),
(keySize : number) : CryptoRsaPair => createRsaPair(keySize),
(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind),
(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind),
(secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText),
(secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText)
)
}
export function base64Encode(input : string) : string {
return base64EncodeCompat(compatBridge(), input)
}
export function base64Decode(input : string) : string {
return base64DecodeCompat(compatBridge(), input)
}
export function md5(input : string) : string {
return md5Compat(compatBridge(), input)
}
export function sha1(input : string) : string {
return sha1Compat(compatBridge(), input)
}
export function sha256(input : string) : string {
return sha256Compat(compatBridge(), input)
}
export function sha512(input : string) : string {
return sha512Compat(compatBridge(), input)
}
export function hmacSha1(key : string, data : string) : string {
return hmacSha1Compat(compatBridge(), key, data)
}
export function hmacSha256(key : string, data : string) : string {
return hmacSha256Compat(compatBridge(), key, data)
}
export function hmacSha512(key : string, data : string) : string {
return hmacSha512Compat(compatBridge(), key, data)
}
export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desEncryptCompat(compatBridge(), key, data, mode, iv)
}
export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desDecryptCompat(compatBridge(), key, data, mode, iv)
}
export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair {
return generateRsaKeyPairCompat(compatBridge(), keySize)
}
export function rsaEncrypt(publicKey : string, data : string) : string {
return rsaEncryptCompat(compatBridge(), publicKey, data)
}
export function rsaDecrypt(privateKey : string, data : string) : string {
return rsaDecryptCompat(compatBridge(), privateKey, data)
}
export function rc4Encrypt(key : string, data : string) : string {
return rc4EncryptCompat(compatBridge(), key, data)
}
export function rc4Decrypt(key : string, data : string) : string {
return rc4DecryptCompat(compatBridge(), key, data)
}
export function useCrypto() : CompatCrypto {
return createCompatCrypto(compatBridge())
}
@@ -0,0 +1,28 @@
{
"name": "laoqianjunzi-crypto-runtime-deps",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "laoqianjunzi-crypto-runtime-deps",
"version": "1.0.0",
"dependencies": {
"crypto-es": "^2.1.0",
"jsencrypt": "^3.5.4"
}
},
"node_modules/crypto-es": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/crypto-es/-/crypto-es-2.1.0.tgz",
"integrity": "sha512-C5Dbuv4QTPGuloy5c5Vv/FZHtmK+lobLAypFfuRaBbwCsk3qbCWWESCH3MUcBsrgXloRNMrzwUAiPg4U6+IaKA==",
"license": "MIT"
},
"node_modules/jsencrypt": {
"version": "3.5.4",
"resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.5.4.tgz",
"integrity": "sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA==",
"license": "MIT"
}
}
}
@@ -0,0 +1,9 @@
{
"name": "laoqianjunzi-crypto-runtime-deps",
"private": true,
"version": "1.0.0",
"dependencies": {
"crypto-es": "^2.1.0",
"jsencrypt": "^3.5.4"
}
}
@@ -0,0 +1,22 @@
export * from './interface.uts'
export * from './unierror.uts'
// #ifdef APP-ANDROID
export * from './app-android/index.uts'
// #endif
// #ifdef APP-IOS
export * from './app-ios/index.uts'
// #endif
// #ifdef APP-HARMONY
export * from './app-harmony/index.uts'
// #endif
// #ifdef WEB
export * from './web/index.uts'
// #endif
// #ifdef MP-WEIXIN
export * from './mp-weixin/index.uts'
// #endif
@@ -0,0 +1,58 @@
export type CryptoDigestKind = "md5" | "sha1" | "sha256" | "sha512"
export type CryptoMacKind = "sha1" | "sha256" | "sha512"
export type CryptoCodecKind = "utf8" | "hex" | "base64" | "base64url" | "latin1" | "utf16"
export type CryptoBlockMode = "ECB" | "CBC" | "CFB" | "CTR" | "CTRGladman" | "OFB"
export type CryptoPaddingKind = "PKCS7" | "ANSI_X923" | "ISO_10126" | "ISO_97971" | "NONE" | "ZERO"
export type RsaOutputKind = "base64" | "hex"
export type CryptoTextCipherOptions = {
mode : CryptoBlockMode,
padding : CryptoPaddingKind,
ivText : string | null,
keyLength : number | null
}
export type CryptoByteCipherOptions = {
mode : CryptoBlockMode,
padding : CryptoPaddingKind,
ivBytes : Uint8Array | null,
keyLength : number | null
}
export type CryptoTripleDesOptions = {
mode : "ECB" | "CBC",
ivText : string | null
}
export type CryptoDesOptions = {
mode : CryptoBlockMode,
padding : CryptoPaddingKind,
ivText : string | null
}
export type CryptoRsaPair = {
publicKey : string,
privateKey : string
}
export type RSARANDOMKEY = CryptoRsaPair
export type RSAKeyPair = CryptoRsaPair
export type CryptoRuntimeSnapshot = {
platformName : string,
engineName : string,
supportsAdvancedModes : boolean,
usesNativeBridge : boolean
}
export type CryptoBridgeErrorCode = 7401 | 7402 | 7403 | 7404 | 7405 | 7406 | 7407 | 7408 | 7409 | 7410
export interface CryptoBridgeFailure extends IUniError {
errCode : CryptoBridgeErrorCode
}
@@ -0,0 +1,209 @@
import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts'
import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts'
import { aesDecryptBytesPortable, aesDecryptTextPortable, aesEncryptBytesPortable, aesEncryptTextPortable, createRsaPairPortable, decodeTextPortable, desDecryptTextPortable, desEncryptTextPortable, digestTextPortable, encodeTextPortable, rc4DecryptTextPortable, rc4EncryptTextPortable, rsaDecryptTextPortable, rsaEncryptTextPortable, signTextPortable, tripleDesDecryptTextPortable, tripleDesEncryptTextPortable } from '../shared/portable_crypto'
import type { CompatBridge, CompatCrypto } from '../shared/compat.uts'
import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts'
export function defaultTextCipherOptions() : CryptoTextCipherOptions {
return createDefaultTextCipherOptions()
}
export function defaultByteCipherOptions() : CryptoByteCipherOptions {
return createDefaultByteCipherOptions()
}
export function defaultTripleDesOptions() : CryptoTripleDesOptions {
return createDefaultTripleDesOptions()
}
export function defaultDesOptions() : CryptoDesOptions {
return createDefaultDesOptions()
}
export function describeCryptoRuntime() : CryptoRuntimeSnapshot {
return buildRuntimeSnapshot('mp-weixin', 'crypto-es', true, false)
}
export function encodeText(codec : CryptoCodecKind, plainText : string) : string {
return encodeTextPortable(codec, plainText)
}
export function decodeText(codec : CryptoCodecKind, encodedText : string) : string {
return decodeTextPortable(codec, encodedText)
}
export function digestText(kind : CryptoDigestKind, plainText : string) : string {
return digestTextPortable(kind, plainText)
}
export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string {
return signTextPortable(kind, secretText, plainText)
}
export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
return aesEncryptTextPortable(secretText, plainText, actual.mode, actual.padding, actual.ivText, actual.keyLength)
}
export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
return aesDecryptTextPortable(secretText, cipherText, actual.mode, actual.padding, actual.ivText, actual.keyLength)
}
export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
return aesEncryptBytesPortable(secretBytes, plainBytes, actual.mode, actual.padding, actual.ivBytes, actual.keyLength)
}
export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
return aesDecryptBytesPortable(secretBytes, cipherBytes, actual.mode, actual.padding, actual.ivBytes, actual.keyLength)
}
export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return tripleDesEncryptTextPortable(secretText, plainText, actual.mode, actual.ivText)
}
export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return tripleDesDecryptTextPortable(secretText, cipherText, actual.mode, actual.ivText)
}
export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return desEncryptTextPortable(secretText, plainText, actual.mode, actual.padding, actual.ivText)
}
export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return desDecryptTextPortable(secretText, cipherText, actual.mode, actual.padding, actual.ivText)
}
export function rc4EncryptText(secretText : string, plainText : string) : string {
return rc4EncryptTextPortable(secretText, plainText)
}
export function rc4DecryptText(secretText : string, cipherHexText : string) : string {
return rc4DecryptTextPortable(secretText, cipherHexText)
}
export function createRsaPair(keySize : number) : CryptoRsaPair {
const bundle = createRsaPairPortable(keySize)
return {
publicKey: bundle.publicKey,
privateKey: bundle.privateKey
} as CryptoRsaPair
}
export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string {
return rsaEncryptTextPortable(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string {
return rsaDecryptTextPortable(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
function compatBridge() : CompatBridge {
return createCompatBridge(
(codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText),
(codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText),
(kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText),
(kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText),
(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options),
(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options),
(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options),
(secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options),
(keySize : number) : CryptoRsaPair => createRsaPair(keySize),
(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind),
(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind),
(secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText),
(secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText)
)
}
export function base64Encode(input : string) : string {
return base64EncodeCompat(compatBridge(), input)
}
export function base64Decode(input : string) : string {
return base64DecodeCompat(compatBridge(), input)
}
export function md5(input : string) : string {
return md5Compat(compatBridge(), input)
}
export function sha1(input : string) : string {
return sha1Compat(compatBridge(), input)
}
export function sha256(input : string) : string {
return sha256Compat(compatBridge(), input)
}
export function sha512(input : string) : string {
return sha512Compat(compatBridge(), input)
}
export function hmacSha1(key : string, data : string) : string {
return hmacSha1Compat(compatBridge(), key, data)
}
export function hmacSha256(key : string, data : string) : string {
return hmacSha256Compat(compatBridge(), key, data)
}
export function hmacSha512(key : string, data : string) : string {
return hmacSha512Compat(compatBridge(), key, data)
}
export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desEncryptCompat(compatBridge(), key, data, mode, iv)
}
export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desDecryptCompat(compatBridge(), key, data, mode, iv)
}
export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair {
return generateRsaKeyPairCompat(compatBridge(), keySize)
}
export function rsaEncrypt(publicKey : string, data : string) : string {
return rsaEncryptCompat(compatBridge(), publicKey, data)
}
export function rsaDecrypt(privateKey : string, data : string) : string {
return rsaDecryptCompat(compatBridge(), privateKey, data)
}
export function rc4Encrypt(key : string, data : string) : string {
return rc4EncryptCompat(compatBridge(), key, data)
}
export function rc4Decrypt(key : string, data : string) : string {
return rc4DecryptCompat(compatBridge(), key, data)
}
export function useCrypto() : CompatCrypto {
return createCompatCrypto(compatBridge())
}
@@ -0,0 +1,771 @@
import type { CryptoBlockMode, CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoPaddingKind, CryptoRsaPair, CryptoTextCipherOptions, RsaOutputKind } from '../interface.uts'
const LIME_MODE_CBC = 1
const LIME_MODE_CFB = 2
const LIME_MODE_CTR = 3
const LIME_MODE_CTR_GLADMAN = 4
const LIME_MODE_ECB = 5
const LIME_MODE_OFB = 6
const LIME_PAD_PKCS7 = 1
const LIME_PAD_ANSI_X923 = 2
const LIME_PAD_ISO_10126 = 3
const LIME_PAD_ISO_97971 = 4
const LIME_PAD_NONE = 5
const LIME_PAD_ZERO = 6
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
export type CompatBridge = {
encodeText : (codec : CryptoCodecKind, plainText : string) => string,
decodeText : (codec : CryptoCodecKind, encodedText : string) => string,
digestText : (kind : CryptoDigestKind, plainText : string) => string,
signText : (kind : CryptoMacKind, secretText : string, plainText : string) => string,
aesEncryptText : (secretText : string, plainText : string, options : CryptoTextCipherOptions | null) => string,
aesDecryptText : (secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) => string,
aesEncryptBytes : (secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) => Uint8Array,
aesDecryptBytes : (secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) => Uint8Array,
desEncryptText : (secretText : string, plainText : string, options : CryptoDesOptions | null) => string,
desDecryptText : (secretText : string, cipherText : string, options : CryptoDesOptions | null) => string,
createRsaPair : (keySize : number) => CryptoRsaPair,
rsaEncryptText : (publicKey : string, plainText : string, outputKind : RsaOutputKind | null) => string,
rsaDecryptText : (privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) => string,
rc4EncryptText : (secretText : string, plainText : string) => string,
rc4DecryptText : (secretText : string, cipherHexText : string) => string
}
export type CompatEncSet = {
Utf8 : CompatEncoder,
Hex : CompatEncoder,
Base64 : CompatEncoder,
Latin1 : CompatEncoder,
Utf16 : CompatEncoder,
Base64url : CompatEncoder
}
export type CompatModeSet = {
CBC : number,
CFB : number,
CTR : number,
CTRGladman : number,
ECB : number,
OFB : number
}
export type CompatPadSet = {
Pkcs7 : number,
AnsiX923 : number,
Iso10126 : number,
Iso97971 : number,
NoPadding : number,
ZeroPadding : number
}
export function createCompatBridge(
encodeText : (codec : CryptoCodecKind, plainText : string) => string,
decodeText : (codec : CryptoCodecKind, encodedText : string) => string,
digestText : (kind : CryptoDigestKind, plainText : string) => string,
signText : (kind : CryptoMacKind, secretText : string, plainText : string) => string,
aesEncryptText : (secretText : string, plainText : string, options : CryptoTextCipherOptions | null) => string,
aesDecryptText : (secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) => string,
aesEncryptBytes : (secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) => Uint8Array,
aesDecryptBytes : (secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) => Uint8Array,
desEncryptText : (secretText : string, plainText : string, options : CryptoDesOptions | null) => string,
desDecryptText : (secretText : string, cipherText : string, options : CryptoDesOptions | null) => string,
createRsaPair : (keySize : number) => CryptoRsaPair,
rsaEncryptText : (publicKey : string, plainText : string, outputKind : RsaOutputKind | null) => string,
rsaDecryptText : (privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) => string,
rc4EncryptText : (secretText : string, plainText : string) => string,
rc4DecryptText : (secretText : string, cipherHexText : string) => string
) : CompatBridge {
return {
encodeText,
decodeText,
digestText,
signText,
aesEncryptText,
aesDecryptText,
aesEncryptBytes,
aesDecryptBytes,
desEncryptText,
desDecryptText,
createRsaPair,
rsaEncryptText,
rsaDecryptText,
rc4EncryptText,
rc4DecryptText
} as CompatBridge
}
function cloneBytes(input : Uint8Array) : Uint8Array {
const output = new Uint8Array(input.length)
let index = 0
while (index < input.length) {
output[index] = input[index]
index += 1
}
return output
}
function bytesToWords(input : Uint8Array) : number[] {
const output = [] as 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
}
output.push(word)
index += 4
}
return output
}
function byteToHex(value : number) : string {
let part = value.toString(16)
if (part.length < 2) {
part = '0' + part
}
return part
}
function bytesToHex(input : Uint8Array) : string {
let output = ''
let index = 0
while (index < input.length) {
output += byteToHex(input[index] & 0xff)
index += 1
}
return output
}
function hexValueOfChar(ch : string) : number {
const lower = ch.toLowerCase()
const code = lower.charCodeAt(0)
const actualCode = code == null ? 0 : code
if (lower >= '0' && lower <= '9') {
return actualCode - 48
}
return actualCode - 87
}
function hexToBytes(hexText : string) : Uint8Array {
const actualLength = hexText.length % 2 == 0 ? hexText.length : hexText.length - 1
const output = new Uint8Array(actualLength / 2)
let index = 0
while (index < actualLength) {
const high = hexValueOfChar(hexText.substring(index, index + 1))
const low = hexValueOfChar(hexText.substring(index + 1, index + 2))
const targetIndex = index / 2
output[targetIndex] = ((high << 4) | low) & 0xff
index += 2
}
return output
}
function base64IndexOf(ch : string) : number {
return BASE64_ALPHABET.indexOf(ch)
}
function toBase64UrlText(input : string) : string {
let output = ''
let index = 0
while (index < input.length) {
const ch = input.substring(index, index + 1)
if (ch == '+') {
output += '-'
} else if (ch == '/') {
output += '_'
} else if (ch != '=') {
output += ch
}
index += 1
}
return output
}
function fromBase64UrlText(input : string) : string {
let output = ''
let index = 0
while (index < input.length) {
const ch = input.substring(index, index + 1)
if (ch == '-') {
output += '+'
} else if (ch == '_') {
output += '/'
} else {
output += ch
}
index += 1
}
while (output.length % 4 != 0) {
output += '='
}
return output
}
function bytesToBase64(input : Uint8Array, urlSafe : boolean) : string {
let output = ''
let index = 0
while (index < input.length) {
const first = input[index] & 0xff
const hasSecond = index + 1 < input.length
const hasThird = index + 2 < input.length
const second = hasSecond ? input[index + 1] & 0xff : 0
const third = hasThird ? input[index + 2] & 0xff : 0
const block = (first << 16) | (second << 8) | third
output += BASE64_ALPHABET.substring((block >>> 18) & 0x3f, ((block >>> 18) & 0x3f) + 1)
output += BASE64_ALPHABET.substring((block >>> 12) & 0x3f, ((block >>> 12) & 0x3f) + 1)
if (hasSecond) {
output += BASE64_ALPHABET.substring((block >>> 6) & 0x3f, ((block >>> 6) & 0x3f) + 1)
} else {
output += '='
}
if (hasThird) {
output += BASE64_ALPHABET.substring(block & 0x3f, (block & 0x3f) + 1)
} else {
output += '='
}
index += 3
}
if (urlSafe) {
return toBase64UrlText(output)
}
return output
}
function normalizeBase64Text(input : string) : string {
return fromBase64UrlText(input)
}
function base64ToBytes(input : string, urlSafe : boolean) : Uint8Array {
const source = urlSafe ? normalizeBase64Text(input) : input
const output = [] as number[]
let index = 0
while (index < source.length) {
const char1 = source.substring(index, index + 1)
const char2 = source.substring(index + 1, index + 2)
const char3 = source.substring(index + 2, index + 3)
const char4 = source.substring(index + 3, index + 4)
const value1 = base64IndexOf(char1)
const value2 = base64IndexOf(char2)
const value3 = char3 == '=' ? -1 : base64IndexOf(char3)
const value4 = char4 == '=' ? -1 : base64IndexOf(char4)
const block = (value1 << 18) | (value2 << 12) | ((value3 < 0 ? 0 : value3) << 6) | (value4 < 0 ? 0 : value4)
output.push((block >>> 16) & 0xff)
if (value3 >= 0) {
output.push((block >>> 8) & 0xff)
}
if (value4 >= 0) {
output.push(block & 0xff)
}
index += 4
}
return new Uint8Array(output)
}
function latin1ToBytes(input : string) : Uint8Array {
const output = new Uint8Array(input.length)
let index = 0
while (index < input.length) {
const code = input.charCodeAt(index)
output[index] = (code == null ? 0 : code) & 0xff
index += 1
}
return output
}
function bytesToLatin1(input : Uint8Array) : string {
let output = ''
let index = 0
while (index < input.length) {
output += String.fromCharCode(input[index] & 0xff)
index += 1
}
return output
}
function utf16ToBytes(input : string) : Uint8Array {
const output = new Uint8Array(input.length * 2)
let index = 0
while (index < input.length) {
const code = input.charCodeAt(index)
const actualCode = code == null ? 0 : code
output[index * 2] = (actualCode >>> 8) & 0xff
output[index * 2 + 1] = actualCode & 0xff
index += 1
}
return output
}
function bytesToUtf16(input : Uint8Array) : string {
let output = ''
let index = 0
while (index + 1 < input.length) {
const code = ((input[index] & 0xff) << 8) | (input[index + 1] & 0xff)
output += String.fromCharCode(code)
index += 2
}
return output
}
function textToUtf8Bytes(input : string, bridge : CompatBridge) : Uint8Array {
return hexToBytes(bridge.encodeText('hex', input))
}
function utf8BytesToText(input : Uint8Array, bridge : CompatBridge) : string {
return bridge.decodeText('hex', bytesToHex(input))
}
function parseCodecBytes(kind : CryptoCodecKind, input : string, bridge : CompatBridge) : Uint8Array {
if (kind == 'utf8') {
return textToUtf8Bytes(input, bridge)
}
if (kind == 'hex') {
return hexToBytes(input)
}
if (kind == 'base64') {
return base64ToBytes(input, false)
}
if (kind == 'base64url') {
return base64ToBytes(input, true)
}
if (kind == 'latin1') {
return latin1ToBytes(input)
}
return utf16ToBytes(input)
}
function stringifyCodecBytes(kind : CryptoCodecKind, input : Uint8Array, bridge : CompatBridge) : string {
if (kind == 'utf8') {
return utf8BytesToText(input, bridge)
}
if (kind == 'hex') {
return bytesToHex(input)
}
if (kind == 'base64') {
return bytesToBase64(input, false)
}
if (kind == 'base64url') {
return bytesToBase64(input, true)
}
if (kind == 'latin1') {
return bytesToLatin1(input)
}
return bytesToUtf16(input)
}
function normalizeKeyLength(keySize : number | null) : number | null {
if (keySize == null) {
return null
}
if (keySize == 128) {
return 16
}
if (keySize == 192) {
return 24
}
if (keySize == 256) {
return 32
}
if (keySize == 16 || keySize == 24 || keySize == 32) {
return keySize
}
return 16
}
function toLimeMode(mode : number | null) : CryptoBlockMode {
if (mode == LIME_MODE_CFB) {
return 'CFB'
}
if (mode == LIME_MODE_CTR) {
return 'CTR'
}
if (mode == LIME_MODE_CTR_GLADMAN) {
return 'CTRGladman'
}
if (mode == LIME_MODE_ECB) {
return 'ECB'
}
if (mode == LIME_MODE_OFB) {
return 'OFB'
}
return 'CBC'
}
function toLimePadding(padding : number | null) : CryptoPaddingKind {
if (padding == LIME_PAD_ANSI_X923) {
return 'ANSI_X923'
}
if (padding == LIME_PAD_ISO_10126) {
return 'ISO_10126'
}
if (padding == LIME_PAD_ISO_97971) {
return 'ISO_97971'
}
if (padding == LIME_PAD_NONE) {
return 'NONE'
}
if (padding == LIME_PAD_ZERO) {
return 'ZERO'
}
return 'PKCS7'
}
function readConfigNumber(cfg : UTSJSONObject | null, key : string) : number | null {
if (cfg == null) {
return null
}
return cfg.getNumber(key)
}
function readConfigWordArray(cfg : UTSJSONObject | null, key : string) : CompatWordArray | null {
if (cfg == null) {
return null
}
const raw = cfg.get(key) as object | null
if (raw != null && raw instanceof CompatWordArray) {
return raw as CompatWordArray
}
return null
}
function buildLimeTextOptions(cfg : UTSJSONObject | null, bridge : CompatBridge) : CryptoTextCipherOptions {
const iv = readConfigWordArray(cfg, 'iv')
return {
mode: toLimeMode(readConfigNumber(cfg, 'mode')),
padding: toLimePadding(readConfigNumber(cfg, 'padding')),
ivText: iv == null ? null : utf8BytesToText(iv.toUint8Array(), bridge),
keyLength: null
} as CryptoTextCipherOptions
}
function buildLimeDesOptions(cfg : UTSJSONObject | null, bridge : CompatBridge) : CryptoDesOptions {
const iv = readConfigWordArray(cfg, 'iv')
return {
mode: toLimeMode(readConfigNumber(cfg, 'mode')),
padding: toLimePadding(readConfigNumber(cfg, 'padding')),
ivText: iv == null ? null : utf8BytesToText(iv.toUint8Array(), bridge)
} as CryptoDesOptions
}
function buildXTextOptions(mode : string, iv : string | null, keySize : number | null) : CryptoTextCipherOptions {
return {
mode: mode == 'CBC' ? 'CBC' : 'ECB',
padding: 'PKCS7',
ivText: iv,
keyLength: normalizeKeyLength(keySize)
} as CryptoTextCipherOptions
}
function buildXByteOptions(mode : string, iv : Uint8Array | null, keySize : number | null) : CryptoByteCipherOptions {
return {
mode: mode == 'CBC' ? 'CBC' : 'ECB',
padding: 'PKCS7',
ivBytes: iv,
keyLength: normalizeKeyLength(keySize)
} as CryptoByteCipherOptions
}
function buildXDesOptions(mode : string, iv : string | null) : CryptoDesOptions {
return {
mode: mode == 'CBC' ? 'CBC' : 'ECB',
padding: 'PKCS7',
ivText: iv
} as CryptoDesOptions
}
function keyToText(key : string | CompatWordArray, bridge : CompatBridge) : string {
if (typeof key == 'string') {
return key
}
if (key instanceof CompatWordArray) {
return utf8BytesToText(key.toUint8Array(), bridge)
}
return ''
}
export class CompatWordArray {
words : number[]
sigBytes : number
private rawBytes : Uint8Array
constructor(rawBytes : Uint8Array) {
this.rawBytes = cloneBytes(rawBytes)
this.words = bytesToWords(this.rawBytes)
this.sigBytes = this.rawBytes.length
}
toString(encoder : CompatEncoder | null = null) : string {
if (encoder == null) {
return bytesToHex(this.rawBytes)
}
return encoder.stringify(this)
}
toUint8Array() : Uint8Array {
return cloneBytes(this.rawBytes)
}
}
export class CompatCipherResult {
ciphertext : CompatWordArray
private serialized : string
constructor(serialized : string, cipherBytes : Uint8Array) {
this.serialized = serialized
this.ciphertext = new CompatWordArray(cipherBytes)
}
toString(encoder : CompatEncoder | null = null) : string {
if (encoder == null) {
return this.serialized
}
return this.ciphertext.toString(encoder)
}
}
export class CompatEncoder {
private kind : CryptoCodecKind
private bridge : CompatBridge
constructor(kind : CryptoCodecKind, bridge : CompatBridge) {
this.kind = kind
this.bridge = bridge
}
parse(input : string) : CompatWordArray {
return new CompatWordArray(parseCodecBytes(this.kind, input, this.bridge))
}
stringify(wordArray : CompatWordArray) : string {
return stringifyCodecBytes(this.kind, wordArray.toUint8Array(), this.bridge)
}
}
export class CompatRsaAdapter {
private bridge : CompatBridge
private publicKey : string = ''
private privateKey : string = ''
constructor(bridge : CompatBridge) {
this.bridge = bridge
}
generateKeyPair() : CryptoRsaPair {
const pair = this.bridge.createRsaPair(2048)
this.publicKey = pair.publicKey
this.privateKey = pair.privateKey
return pair
}
setPublicKey(publicKey : string) : void {
this.publicKey = publicKey
}
setPrivateKey(privateKey : string) : void {
this.privateKey = privateKey
}
encrypt(data : string) : string {
return this.bridge.rsaEncryptText(this.publicKey, data, 'base64')
}
decrypt(encryptedData : string) : string {
return this.bridge.rsaDecryptText(this.privateKey, encryptedData, 'base64')
}
getPublicKey() : string {
return this.publicKey
}
getPrivateKey() : string {
return this.privateKey
}
}
export class CompatSymmetricAdapter {
private bridge : CompatBridge
private algorithm : string
constructor(bridge : CompatBridge, algorithm : string) {
this.bridge = bridge
this.algorithm = algorithm
}
encrypt(message : string, key : string | CompatWordArray, cfg : UTSJSONObject | null = null) : CompatCipherResult {
const keyText = keyToText(key, this.bridge)
if (this.algorithm == 'DES') {
const cipherText = this.bridge.desEncryptText(keyText, message, buildLimeDesOptions(cfg, this.bridge))
return new CompatCipherResult(cipherText, parseCodecBytes('base64', cipherText, this.bridge))
}
const cipherText = this.bridge.aesEncryptText(keyText, message, buildLimeTextOptions(cfg, this.bridge))
return new CompatCipherResult(cipherText, parseCodecBytes('base64', cipherText, this.bridge))
}
decrypt(ciphertext : string, password : string | CompatWordArray, cfg : UTSJSONObject | null = null) : CompatWordArray {
const keyText = keyToText(password, this.bridge)
if (this.algorithm == 'DES') {
return new CompatWordArray(textToUtf8Bytes(this.bridge.desDecryptText(keyText, ciphertext, buildLimeDesOptions(cfg, this.bridge)), this.bridge))
}
return new CompatWordArray(textToUtf8Bytes(this.bridge.aesDecryptText(keyText, ciphertext, buildLimeTextOptions(cfg, this.bridge)), this.bridge))
}
}
export class CompatCrypto {
enc : CompatEncSet
mode : CompatModeSet
pad : CompatPadSet
AES : CompatSymmetricAdapter
DES : CompatSymmetricAdapter
RSA : CompatRsaAdapter
private bridge : CompatBridge
constructor(bridge : CompatBridge) {
this.bridge = bridge
this.enc = {
Utf8: new CompatEncoder('utf8', bridge),
Hex: new CompatEncoder('hex', bridge),
Base64: new CompatEncoder('base64', bridge),
Latin1: new CompatEncoder('latin1', bridge),
Utf16: new CompatEncoder('utf16', bridge),
Base64url: new CompatEncoder('base64url', bridge)
} as CompatEncSet
this.mode = {
CBC: LIME_MODE_CBC,
CFB: LIME_MODE_CFB,
CTR: LIME_MODE_CTR,
CTRGladman: LIME_MODE_CTR_GLADMAN,
ECB: LIME_MODE_ECB,
OFB: LIME_MODE_OFB
} as CompatModeSet
this.pad = {
Pkcs7: LIME_PAD_PKCS7,
AnsiX923: LIME_PAD_ANSI_X923,
Iso10126: LIME_PAD_ISO_10126,
Iso97971: LIME_PAD_ISO_97971,
NoPadding: LIME_PAD_NONE,
ZeroPadding: LIME_PAD_ZERO
} as CompatPadSet
this.AES = new CompatSymmetricAdapter(bridge, 'AES')
this.DES = new CompatSymmetricAdapter(bridge, 'DES')
this.RSA = new CompatRsaAdapter(bridge)
}
MD5(message : string) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.digestText('md5', message)))
}
SHA1(message : string) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.digestText('sha1', message)))
}
SHA256(message : string) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.digestText('sha256', message)))
}
SHA512(message : string) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.digestText('sha512', message)))
}
HmacSHA256(message : string, secretKey : string | CompatWordArray) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.signText('sha256', typeof secretKey == 'string' ? secretKey : keyToText(secretKey, this.bridge), message)))
}
HmacSHA1(message : string, secretKey : string | CompatWordArray) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.signText('sha1', typeof secretKey == 'string' ? secretKey : keyToText(secretKey, this.bridge), message)))
}
HmacSHA512(message : string, secretKey : string | CompatWordArray) : CompatWordArray {
return new CompatWordArray(hexToBytes(this.bridge.signText('sha512', typeof secretKey == 'string' ? secretKey : keyToText(secretKey, this.bridge), message)))
}
onError(_callback : (err : IUniError) => void) : void {
}
}
export function createCompatCrypto(bridge : CompatBridge) : CompatCrypto {
return new CompatCrypto(bridge)
}
export function base64EncodeCompat(bridge : CompatBridge, input : string) : string {
return bridge.encodeText('base64', input)
}
export function base64DecodeCompat(bridge : CompatBridge, input : string) : string {
return bridge.decodeText('base64', input)
}
export function md5Compat(bridge : CompatBridge, input : string) : string {
return bridge.digestText('md5', input)
}
export function sha1Compat(bridge : CompatBridge, input : string) : string {
return bridge.digestText('sha1', input)
}
export function sha256Compat(bridge : CompatBridge, input : string) : string {
return bridge.digestText('sha256', input)
}
export function sha512Compat(bridge : CompatBridge, input : string) : string {
return bridge.digestText('sha512', input)
}
export function hmacSha1Compat(bridge : CompatBridge, key : string, data : string) : string {
return bridge.signText('sha1', key, data)
}
export function hmacSha256Compat(bridge : CompatBridge, key : string, data : string) : string {
return bridge.signText('sha256', key, data)
}
export function hmacSha512Compat(bridge : CompatBridge, key : string, data : string) : string {
return bridge.signText('sha512', key, data)
}
export function aesEncryptCompat(bridge : CompatBridge, key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return bridge.aesEncryptText(key, data, buildXTextOptions(mode, iv, keySize))
}
export function aesDecryptCompat(bridge : CompatBridge, key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return bridge.aesDecryptText(key, data, buildXTextOptions(mode, iv, keySize))
}
export function aesEncryptBytesCompat(bridge : CompatBridge, key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return bridge.aesEncryptBytes(key, data, buildXByteOptions(mode, iv, keySize))
}
export function aesDecryptBytesCompat(bridge : CompatBridge, key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return bridge.aesDecryptBytes(key, data, buildXByteOptions(mode, iv, keySize))
}
export function desEncryptCompat(bridge : CompatBridge, key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return bridge.desEncryptText(key, data, buildXDesOptions(mode, iv))
}
export function desDecryptCompat(bridge : CompatBridge, key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return bridge.desDecryptText(key, data, buildXDesOptions(mode, iv))
}
export function generateRsaKeyPairCompat(bridge : CompatBridge, keySize : number = 2048) : CryptoRsaPair {
return bridge.createRsaPair(keySize)
}
export function rsaEncryptCompat(bridge : CompatBridge, publicKey : string, data : string) : string {
return bridge.rsaEncryptText(publicKey, data, 'base64')
}
export function rsaDecryptCompat(bridge : CompatBridge, privateKey : string, data : string) : string {
return bridge.rsaDecryptText(privateKey, data, 'base64')
}
export function rc4EncryptCompat(bridge : CompatBridge, key : string, data : string) : string {
return bridge.rc4EncryptText(key, data)
}
export function rc4DecryptCompat(bridge : CompatBridge, key : string, data : string) : string {
return bridge.rc4DecryptText(key, data)
}
@@ -0,0 +1,106 @@
import type { CryptoBlockMode, CryptoByteCipherOptions, CryptoDesOptions, CryptoPaddingKind, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from "../interface.uts"
export function createDefaultTextCipherOptions() : CryptoTextCipherOptions {
return {
mode: "CBC",
padding: "PKCS7",
ivText: "0123456789abcdef",
keyLength: 16
} as CryptoTextCipherOptions
}
export function createDefaultByteCipherOptions() : CryptoByteCipherOptions {
return {
mode: "CBC",
padding: "PKCS7",
ivBytes: new Uint8Array([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102]),
keyLength: 16
} as CryptoByteCipherOptions
}
export function createDefaultTripleDesOptions() : CryptoTripleDesOptions {
return {
mode: "CBC",
ivText: "12345678"
} as CryptoTripleDesOptions
}
export function createDefaultDesOptions() : CryptoDesOptions {
return {
mode: "CBC",
padding: "PKCS7",
ivText: "12345678"
} as CryptoDesOptions
}
export function normalizeTextCipherOptions(options : CryptoTextCipherOptions | null) : CryptoTextCipherOptions {
if (options == null) {
return createDefaultTextCipherOptions()
}
return {
mode: options.mode,
padding: options.padding,
ivText: options.ivText,
keyLength: options.keyLength
} as CryptoTextCipherOptions
}
export function normalizeByteCipherOptions(options : CryptoByteCipherOptions | null) : CryptoByteCipherOptions {
if (options == null) {
return createDefaultByteCipherOptions()
}
return {
mode: options.mode,
padding: options.padding,
ivBytes: options.ivBytes,
keyLength: options.keyLength
} as CryptoByteCipherOptions
}
export function normalizeTripleDesOptions(options : CryptoTripleDesOptions | null) : CryptoTripleDesOptions {
if (options == null) {
return createDefaultTripleDesOptions()
}
return {
mode: options.mode,
ivText: options.ivText
} as CryptoTripleDesOptions
}
export function normalizeDesOptions(options : CryptoDesOptions | null) : CryptoDesOptions {
if (options == null) {
return createDefaultDesOptions()
}
return {
mode: options.mode,
padding: options.padding,
ivText: options.ivText
} as CryptoDesOptions
}
export function buildRuntimeSnapshot(platformName : string, engineName : string, supportsAdvancedModes : boolean, usesNativeBridge : boolean) : CryptoRuntimeSnapshot {
return {
platformName: platformName,
engineName: engineName,
supportsAdvancedModes: supportsAdvancedModes,
usesNativeBridge: usesNativeBridge
} as CryptoRuntimeSnapshot
}
export function resolveHexOutput(outputKind : RsaOutputKind | null) : boolean {
return outputKind == "hex"
}
export function createAsciiBlock(text : string, size : number) : Uint8Array {
const output = new Uint8Array(size)
let index = 0
while (index < size) {
if (index < text.length) {
output[index] = text.charCodeAt(index) & 0xff
} else {
output[index] = 0
}
index += 1
}
return output
}
@@ -0,0 +1,425 @@
// @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)
}
@@ -0,0 +1,27 @@
import { CryptoBridgeErrorCode, CryptoBridgeFailure } from "./interface.uts"
export const CryptoErrorSubject = "laoqianjunzi-crypto"
export const CryptoErrorMessages : Map<CryptoBridgeErrorCode, string> = new Map([
[7401, "crypto bridge: text input is empty"],
[7402, "crypto bridge: unsupported codec kind"],
[7403, "crypto bridge: unsupported digest kind"],
[7404, "crypto bridge: unsupported hmac kind"],
[7405, "crypto bridge: unsupported block mode"],
[7406, "crypto bridge: unsupported padding kind"],
[7407, "crypto bridge: invalid cipher payload"],
[7408, "crypto bridge: invalid key or iv"],
[7409, "crypto bridge: rsa operation failed"],
[7410, "crypto bridge: current platform is missing required capability"]
])
export class CryptoBridgeFailureImpl extends UniError implements CryptoBridgeFailure {
override errCode : CryptoBridgeErrorCode
constructor(errCode : CryptoBridgeErrorCode) {
super()
this.errSubject = CryptoErrorSubject
this.errCode = errCode
this.errMsg = CryptoErrorMessages[errCode] ?? ""
}
}
@@ -0,0 +1,209 @@
import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts'
import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts'
import { aesDecryptBytesPortable, aesDecryptTextPortable, aesEncryptBytesPortable, aesEncryptTextPortable, createRsaPairPortable, decodeTextPortable, desDecryptTextPortable, desEncryptTextPortable, digestTextPortable, encodeTextPortable, rc4DecryptTextPortable, rc4EncryptTextPortable, rsaDecryptTextPortable, rsaEncryptTextPortable, signTextPortable, tripleDesDecryptTextPortable, tripleDesEncryptTextPortable } from '../shared/portable_crypto'
import type { CompatBridge, CompatCrypto } from '../shared/compat.uts'
import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts'
export function defaultTextCipherOptions() : CryptoTextCipherOptions {
return createDefaultTextCipherOptions()
}
export function defaultByteCipherOptions() : CryptoByteCipherOptions {
return createDefaultByteCipherOptions()
}
export function defaultTripleDesOptions() : CryptoTripleDesOptions {
return createDefaultTripleDesOptions()
}
export function defaultDesOptions() : CryptoDesOptions {
return createDefaultDesOptions()
}
export function describeCryptoRuntime() : CryptoRuntimeSnapshot {
return buildRuntimeSnapshot('Web', 'crypto-es', true, false)
}
export function encodeText(codec : CryptoCodecKind, plainText : string) : string {
return encodeTextPortable(codec, plainText)
}
export function decodeText(codec : CryptoCodecKind, encodedText : string) : string {
return decodeTextPortable(codec, encodedText)
}
export function digestText(kind : CryptoDigestKind, plainText : string) : string {
return digestTextPortable(kind, plainText)
}
export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string {
return signTextPortable(kind, secretText, plainText)
}
export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
return aesEncryptTextPortable(secretText, plainText, actual.mode, actual.padding, actual.ivText, actual.keyLength)
}
export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string {
const actual = normalizeTextCipherOptions(options)
return aesDecryptTextPortable(secretText, cipherText, actual.mode, actual.padding, actual.ivText, actual.keyLength)
}
export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
return aesEncryptBytesPortable(secretBytes, plainBytes, actual.mode, actual.padding, actual.ivBytes, actual.keyLength)
}
export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
const actual = normalizeByteCipherOptions(options)
return aesDecryptBytesPortable(secretBytes, cipherBytes, actual.mode, actual.padding, actual.ivBytes, actual.keyLength)
}
export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return tripleDesEncryptTextPortable(secretText, plainText, actual.mode, actual.ivText)
}
export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string {
const actual = normalizeTripleDesOptions(options)
return tripleDesDecryptTextPortable(secretText, cipherText, actual.mode, actual.ivText)
}
export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return desEncryptTextPortable(secretText, plainText, actual.mode, actual.padding, actual.ivText)
}
export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string {
const actual = normalizeDesOptions(options)
return desDecryptTextPortable(secretText, cipherText, actual.mode, actual.padding, actual.ivText)
}
export function rc4EncryptText(secretText : string, plainText : string) : string {
return rc4EncryptTextPortable(secretText, plainText)
}
export function rc4DecryptText(secretText : string, cipherHexText : string) : string {
return rc4DecryptTextPortable(secretText, cipherHexText)
}
export function createRsaPair(keySize : number) : CryptoRsaPair {
const bundle = createRsaPairPortable(keySize)
return {
publicKey: bundle.publicKey,
privateKey: bundle.privateKey
} as CryptoRsaPair
}
export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string {
return rsaEncryptTextPortable(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string {
return rsaDecryptTextPortable(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
}
function compatBridge() : CompatBridge {
return createCompatBridge(
(codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText),
(codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText),
(kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText),
(kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText),
(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options),
(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options),
(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options),
(secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options),
(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options),
(keySize : number) : CryptoRsaPair => createRsaPair(keySize),
(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind),
(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind),
(secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText),
(secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText)
)
}
export function base64Encode(input : string) : string {
return base64EncodeCompat(compatBridge(), input)
}
export function base64Decode(input : string) : string {
return base64DecodeCompat(compatBridge(), input)
}
export function md5(input : string) : string {
return md5Compat(compatBridge(), input)
}
export function sha1(input : string) : string {
return sha1Compat(compatBridge(), input)
}
export function sha256(input : string) : string {
return sha256Compat(compatBridge(), input)
}
export function sha512(input : string) : string {
return sha512Compat(compatBridge(), input)
}
export function hmacSha1(key : string, data : string) : string {
return hmacSha1Compat(compatBridge(), key, data)
}
export function hmacSha256(key : string, data : string) : string {
return hmacSha256Compat(compatBridge(), key, data)
}
export function hmacSha512(key : string, data : string) : string {
return hmacSha512Compat(compatBridge(), key, data)
}
export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
}
export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desEncryptCompat(compatBridge(), key, data, mode, iv)
}
export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
return desDecryptCompat(compatBridge(), key, data, mode, iv)
}
export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair {
return generateRsaKeyPairCompat(compatBridge(), keySize)
}
export function rsaEncrypt(publicKey : string, data : string) : string {
return rsaEncryptCompat(compatBridge(), publicKey, data)
}
export function rsaDecrypt(privateKey : string, data : string) : string {
return rsaDecryptCompat(compatBridge(), privateKey, data)
}
export function rc4Encrypt(key : string, data : string) : string {
return rc4EncryptCompat(compatBridge(), key, data)
}
export function rc4Decrypt(key : string, data : string) : string {
return rc4DecryptCompat(compatBridge(), key, data)
}
export function useCrypto() : CompatCrypto {
return createCompatCrypto(compatBridge())
}