Files

484 lines
13 KiB
Markdown

# 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` 中注册该页面,可直接运行到对应页面进行体验。