@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +66 -257
- package/README.ja.md +66 -257
- package/README.md +66 -257
- package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +435 -247
- package/dist/index.js.map +1 -1
- package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
- package/dist/src-2V6VP4I5.js.map +1 -0
- package/package.json +8 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +5 -0
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +8 -3
- package/templates/base/vite.config.ts.hbs +3 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/skills/AGENTS.md +3 -3
- package/templates/skills/bridges/admin/AGENTS.md +1 -1
- package/templates/skills/bridges/api/AGENTS.md +1 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
- package/templates/skills/bridges/h5/AGENTS.md +1 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
- package/templates/skills/bridges/website/AGENTS.md +1 -1
- package/templates/skills/hai-api-client/SKILL.md +14 -161
- package/templates/skills/hai-api-client/reference.md +166 -0
- package/templates/skills/hai-api-contract/SKILL.md +15 -273
- package/templates/skills/hai-api-contract/reference.md +278 -0
- package/templates/skills/hai-app-create/SKILL.md +2 -2
- package/templates/skills/hai-app-create/development.md +4 -4
- package/templates/skills/hai-app-create/reference.md +5 -4
- package/templates/skills/hai-app-review/SKILL.md +1 -1
- package/templates/skills/hai-app-tests/SKILL.md +4 -3
- package/templates/skills/hai-app-tests/reference.md +2 -2
- package/templates/skills/hai-app-tests/validation.md +31 -0
- package/templates/skills/hai-audit/SKILL.md +17 -142
- package/templates/skills/hai-audit/reference.md +145 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +10 -9
- package/templates/skills/hai-cache/SKILL.md +15 -182
- package/templates/skills/hai-cache/reference.md +187 -0
- package/templates/skills/hai-capacitor/SKILL.md +4 -3
- package/templates/skills/hai-capacitor/reference.md +6 -4
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +3 -3
- package/templates/skills/hai-crypto/SKILL.md +16 -252
- package/templates/skills/hai-crypto/reference.md +256 -0
- package/templates/skills/hai-datapipe/SKILL.md +15 -196
- package/templates/skills/hai-datapipe/reference.md +202 -0
- package/templates/skills/hai-deploy/SKILL.md +20 -55
- package/templates/skills/hai-deploy/middleware.md +5 -11
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +1 -3
- package/templates/skills/hai-kit/SKILL.md +12 -167
- package/templates/skills/hai-kit/reference.md +172 -0
- package/templates/skills/hai-payment/SKILL.md +19 -251
- package/templates/skills/hai-payment/reference.md +256 -0
- package/templates/skills/hai-pr-review/SKILL.md +4 -4
- package/templates/skills/hai-reach/SKILL.md +2 -2
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-scheduler/SKILL.md +15 -195
- package/templates/skills/hai-scheduler/reference.md +202 -0
- package/templates/skills/hai-serv/SKILL.md +2 -2
- package/templates/skills/hai-spec-implement/SKILL.md +1 -1
- package/templates/skills/hai-spec-implement/workflow.md +3 -3
- package/templates/skills/hai-storage/SKILL.md +17 -238
- package/templates/skills/hai-storage/reference.md +243 -0
- package/templates/skills/hai-ui/SKILL.md +1 -1
- package/templates/skills/hai-ui/reference.md +9 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -42,12 +42,14 @@ await capacitor.close()
|
|
|
42
42
|
|
|
43
43
|
### 2. Token 安全存储(与 api-client 配合)
|
|
44
44
|
|
|
45
|
+
先复用应用已有的 API 初始化入口。示例中的 `apiBase` 由应用解析公开运行配置,已经包含 API 前缀,不重复拼接 `/api/v1`。Web 部署可通过 `runtime-config.js` 覆盖地址;原生包在复制静态资源前设置公开配置。
|
|
46
|
+
|
|
45
47
|
```typescript
|
|
46
48
|
import { apiClient } from '@h-ai/api-client'
|
|
47
49
|
import { createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
48
50
|
|
|
49
51
|
await apiClient.init({
|
|
50
|
-
baseUrl:
|
|
52
|
+
baseUrl: apiBase,
|
|
51
53
|
auth: {
|
|
52
54
|
storage: createCapacitorTokenStorage(),
|
|
53
55
|
},
|
|
@@ -236,7 +238,7 @@ export async function initCapacitor() {
|
|
|
236
238
|
|
|
237
239
|
### SPA 模式配置(必需)
|
|
238
240
|
|
|
239
|
-
Capacitor
|
|
241
|
+
Capacitor 原生壳读取 `webDir` 指定的静态产物。纯 Vite 模板默认为 `dist`;SvelteKit 项目需配置静态适配器,输出可能是 `build`。构建器、容器与 `webDir` 必须一致,以下是 Vite 示例:
|
|
240
242
|
|
|
241
243
|
```typescript
|
|
242
244
|
// capacitor.config.ts
|
|
@@ -258,9 +260,9 @@ export default config
|
|
|
258
260
|
import { apiClient } from '@h-ai/api-client'
|
|
259
261
|
import { createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
260
262
|
|
|
261
|
-
export async function initApi() {
|
|
263
|
+
export async function initApi(apiBase: string) {
|
|
262
264
|
return apiClient.init({
|
|
263
|
-
baseUrl:
|
|
265
|
+
baseUrl: apiBase,
|
|
264
266
|
auth: {
|
|
265
267
|
storage: createCapacitorTokenStorage(),
|
|
266
268
|
},
|
|
@@ -52,6 +52,8 @@ pnpm test
|
|
|
52
52
|
|
|
53
53
|
从当前 package.json 确认实际 scripts,使用声明的最低 Node 版本验证并与 engines/README 同步;PowerShell 使用 `pnpm.cmd`。测试内容由 `hai-app-tests` 定义;本技能决定门禁如何稳定执行。
|
|
54
54
|
|
|
55
|
+
AI 本地验收和排查按 [分轮验证与集中修复](../hai-app-tests/validation.md) 收集所有可执行结果,再集中修复;普通失败不立即从头重跑。独立 CI job 可继续收集结果,真实前置失败则阻塞其依赖 job;必需检查与发布步骤仍须全部成功,不以继续收集为由设置 `continue-on-error` 或放行失败。
|
|
56
|
+
|
|
55
57
|
lint/typecheck 只能执行已配置的规则。新增开发规范时区分可静态检测项(如显式 any/console.log)与必须行为验收项(如资源越权、事务/幂等、失败结果、生命周期清理),后者在适用单元/集成/E2E 中验证。禁止通过关闭规则、添加无依据忽略或把 `|| true` 加到门禁来“统一风格”。
|
|
56
58
|
|
|
57
59
|
### E2E 策略
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-core
|
|
3
|
-
description: "
|
|
3
|
+
description: "配置 hai 应用启动、YAML/环境变量、日志、i18n 与 HaiResult;排查配置覆盖和错误本地化。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-core
|
|
@@ -18,7 +18,7 @@ description: "使用 @h-ai/core 配置、日志、i18n、ID、Zod 错误本地
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
20
|
- Node 启动用 core.init({ configDir }) 加载配置;勿传 logging 覆盖 _core.yml 中的日志设置,除非任务明确要求。
|
|
21
|
-
- 应用名称、版本、环境、默认语言和日志设置统一从 `_core.yml` 经 core.config 获取,日志使用 core.logger;UI 不另维护硬编码副本。SvelteKit layout / fullstack app.info 仅下发公开字段白名单;独立 Vite mobile-app 在 Node
|
|
21
|
+
- 应用名称、版本、环境、默认语言和日志设置统一从 `_core.yml` 经 core.config 获取,日志使用 core.logger;UI 不另维护硬编码副本。SvelteKit layout / fullstack app.info 仅下发公开字段白名单;独立 Vite mobile-app 在 Node 构建时解析并注入公开默认值,修改这些编译默认值需要重新构建。静态 Web 的公开 API 地址等通过 `runtime-config.js` 覆盖时无需重建镜像,原生包在复制静态资源前准备配置入口。浏览器不加载 YAML,不接收完整模块配置、密钥或日志脱敏上下文。
|
|
22
22
|
- `HAI_<配置名>_<路径>` 可整体覆盖根/对象/数组;父节点优先,未覆盖时才遍历已有叶子。camelCase 不拆词,约定变量优先于显式插值。
|
|
23
23
|
- 环境文件采用六类:`.env.example` 完整列出字段用途、默认值、枚举和范围;`.env` 为公共基础;`.env.dev`、`.env.test`、`.env.prod` 为公共环境覆盖,这五类均提交 Git。`.env.local` 为本机私有配置,不提交 Git。优先级为进程环境 > `.env.local` > 环境文件 > `.env`;Vite 在 Node 配置阶段调用 `core.config.loadEnvFiles({ mode, envDir })` 并检查 success 后注入进程,Node 脚本按相同顺序传入 `--env-file`。`core.init` 不自行读取 dotenv。
|
|
24
24
|
- 配置 getter 与 ID/logger 方法不是 HaiResult;validate/load 等须检查 success。
|
|
@@ -26,7 +26,7 @@ description: "使用 @h-ai/core 配置、日志、i18n、ID、Zod 错误本地
|
|
|
26
26
|
|
|
27
27
|
## 按需参考
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
30
30
|
|
|
31
31
|
- 运行环境
|
|
32
32
|
- 适用场景
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-crypto
|
|
3
|
-
description: "
|
|
3
|
+
description: "实现 SM2/SM3/SM4 加解密、验签、密码哈希,或通过 serv/kit/api-client 接入传输加密。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-crypto
|
|
@@ -11,261 +11,25 @@ description: "使用 @h-ai/crypto 进行 SM2/SM3/SM4、密码哈希与传输加
|
|
|
11
11
|
| --- | --- |
|
|
12
12
|
| 能力 | 使用 @h-ai/crypto 进行 SM2/SM3/SM4、密码哈希与传输加密 |
|
|
13
13
|
| 适用场景 | 应用加密、验签、密码保存或传输保护 |
|
|
14
|
-
| 输入 |
|
|
14
|
+
| 输入 | 无参 init、字节/文本、密钥和 transport 装配选项 |
|
|
15
15
|
| 输出 | 加解密/验签的 HaiResult;transport 工厂按公开类型使用 |
|
|
16
16
|
| 限制 | 先 init;password 仅服务端,浏览器仅支持部分能力。传输通过 serv/kit/api-client 顶层装配,不导入内部工厂。 |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## 使用路径
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1. `crypto.init()` 无配置参数,检查结果;确认目标端是否支持所需能力,password 仅 Node。
|
|
21
|
+
2. 选用公开 asymmetric/hash/symmetric/password 操作,检查 HaiResult;SM3 HMAC 不等同于 SHA-256 HMAC。
|
|
22
|
+
3. 传输加密经 serv/kit/api-client 顶层配置接入,统一协商与刷新路径;不导入内部工厂。
|
|
23
|
+
4. 验证加解密往返、错误密钥/签名和关闭,密钥与明文不写日志或浏览器公开配置。
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
## 按需参考
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|------|---------|--------|------|
|
|
26
|
-
| `crypto.asymmetric` 非对称加密 | ✅ | ✅ | SM2 加解密、签名验签 |
|
|
27
|
-
| `crypto.hash` 哈希 | ✅ | ✅ | SM3 哈希、HMAC |
|
|
28
|
-
| `crypto.symmetric` 对称加密 | ✅ | ✅ | SM4 ECB/CBC |
|
|
29
|
-
| `crypto.password` 密码哈希 | ✅ | ❌ | 依赖 Node.js crypto(服务端专用) |
|
|
30
|
-
| `crypto.transport` 传输加密 | ✅ | ✅ | 服务端管理器 + 客户端 encryptedFetch |
|
|
27
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
- 密码存储与验证(加盐迭代哈希)
|
|
40
|
-
- 传输加密:为 serv / kit / api-client 提供统一协议、密钥协商和请求响应加解密
|
|
41
|
-
|
|
42
|
-
## 使用步骤
|
|
43
|
-
|
|
44
|
-
### 1. 初始化与关闭
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { crypto } from '@h-ai/crypto'
|
|
48
|
-
|
|
49
|
-
await crypto.init()
|
|
50
|
-
|
|
51
|
-
// 使用后关闭
|
|
52
|
-
await crypto.close()
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## 核心 API
|
|
56
|
-
|
|
57
|
-
### 非对称加密 — `crypto.asymmetric`
|
|
58
|
-
|
|
59
|
-
| 方法 | 签名 | 说明 |
|
|
60
|
-
| ------------------- | ----------------------------------------------------------- | ------------ |
|
|
61
|
-
| `generateKeyPair` | `() => HaiResult<KeyPair>` | 生成密钥对 |
|
|
62
|
-
| `encrypt` | `(data, publicKey, options?) => HaiResult<string>` | 公钥加密 |
|
|
63
|
-
| `decrypt` | `(ciphertext, privateKey, options?) => HaiResult<string>` | 私钥解密 |
|
|
64
|
-
| `sign` | `(data, privateKey, options?) => HaiResult<string>` | 私钥签名 |
|
|
65
|
-
| `verify` | `(data, signature, publicKey, options?) => HaiResult<boolean>` | 公钥验签 |
|
|
66
|
-
| `isValidPublicKey` | `(key: string) => boolean` | 校验公钥格式 |
|
|
67
|
-
| `isValidPrivateKey` | `(key: string) => boolean` | 校验私钥格式 |
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
const keyPair = crypto.asymmetric.generateKeyPair()
|
|
71
|
-
if (keyPair.success) {
|
|
72
|
-
const { publicKey, privateKey } = keyPair.data
|
|
73
|
-
|
|
74
|
-
const encrypted = crypto.asymmetric.encrypt('敏感数据', publicKey)
|
|
75
|
-
if (encrypted.success) {
|
|
76
|
-
const decrypted = crypto.asymmetric.decrypt(encrypted.data, privateKey)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const signature = crypto.asymmetric.sign('待签名数据', privateKey)
|
|
80
|
-
if (signature.success) {
|
|
81
|
-
const valid = crypto.asymmetric.verify('待签名数据', signature.data, publicKey)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**AsymmetricEncryptOptions**:`{ cipherMode?: 0 | 1, outputFormat?: 'hex' | 'base64' }`
|
|
87
|
-
**SignOptions**:`{ hash?: boolean, userId?: string }`
|
|
88
|
-
|
|
89
|
-
### 哈希 — `crypto.hash`
|
|
90
|
-
|
|
91
|
-
| 方法 | 签名 | 说明 |
|
|
92
|
-
| -------- | ---------------------------------------------------------- | -------- |
|
|
93
|
-
| `hash` | `(data: string \| Uint8Array, options?) => HaiResult<string>` | 哈希 |
|
|
94
|
-
| `hmac` | `(data, key) => HaiResult<string>` | HMAC |
|
|
95
|
-
| `verify` | `(data, expectedHash) => HaiResult<boolean>` | 验证哈希 |
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
const hash = crypto.hash.hash('hello')
|
|
99
|
-
const hmac = crypto.hash.hmac('hello', 'secret-key')
|
|
100
|
-
const valid = crypto.hash.verify('hello', hash.data!)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**HashOptions**:`{ inputEncoding?: 'utf8' | 'hex' }`
|
|
104
|
-
|
|
105
|
-
### 对称加密 — `crypto.symmetric`
|
|
106
|
-
|
|
107
|
-
| 方法 | 签名 | 说明 |
|
|
108
|
-
| --------------- | ----------------------------------------------- | --------------------------- |
|
|
109
|
-
| `generateKey` | `() => string` | 生成 128-bit 密钥(32 hex) |
|
|
110
|
-
| `generateIV` | `() => string` | 生成随机 IV(32 hex) |
|
|
111
|
-
| `encrypt` | `(data, key, options?) => HaiResult<SymmetricEncryptedPayload>` | 加密(返回结构化密文) |
|
|
112
|
-
| `decrypt` | `(payload, key) => HaiResult<string>` | 解密结构化密文 |
|
|
113
|
-
| `encryptWithIV` | `(data, key) => HaiResult<EncryptWithIVResult>` | CBC 模式加密(自动生成 IV) |
|
|
114
|
-
| `decryptWithIV` | `(ciphertext, key, iv) => HaiResult<string>` | CBC 模式解密 |
|
|
115
|
-
| `deriveKey` | `(password, salt) => string` | 从密码和盐值派生密钥 |
|
|
116
|
-
| `isValidKey` | `(key: string) => boolean` | 校验密钥格式 |
|
|
117
|
-
| `isValidIV` | `(iv: string) => boolean` | 校验 IV 格式 |
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
const key = crypto.symmetric.generateKey()
|
|
121
|
-
|
|
122
|
-
// 默认安全用法:CBC + 自动 IV,返回 { mode, ciphertext, iv, encoding }
|
|
123
|
-
const encrypted = crypto.symmetric.encrypt('明文数据', key)
|
|
124
|
-
if (encrypted.success) {
|
|
125
|
-
const decrypted = crypto.symmetric.decrypt(encrypted.data, key)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 需要单独存储 IV 时使用结构化结果
|
|
129
|
-
const withIV = crypto.symmetric.encryptWithIV('明文数据', key)
|
|
130
|
-
if (withIV.success) {
|
|
131
|
-
const decrypted = crypto.symmetric.decryptWithIV(withIV.data.ciphertext, key, withIV.data.iv)
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**SymmetricEncryptOptions**:`{ mode?: 'ecb' | 'cbc', iv?: string, outputFormat?: 'hex' | 'base64' }`
|
|
136
|
-
**SymmetricEncryptedPayload**:`{ mode: 'ecb' | 'cbc', ciphertext: string, iv?: string, encoding: 'hex' | 'base64' }`
|
|
137
|
-
|
|
138
|
-
> `encrypt()` 默认使用 CBC 并自动生成 IV。ECB 会泄漏明文结构,只有底层协议明确要求时才显式 `{ mode: 'ecb' }`。
|
|
139
|
-
|
|
140
|
-
### 密码哈希 — `crypto.password`
|
|
141
|
-
|
|
142
|
-
| 方法 | 签名 | 说明 |
|
|
143
|
-
| -------- | --------------------------------------- | -------- |
|
|
144
|
-
| `hash` | `(password, config?) => HaiResult<string>` | 密码哈希 |
|
|
145
|
-
| `verify` | `(password, hash) => HaiResult<boolean>` | 验证密码 |
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
const hashed = crypto.password.hash('MyPassword123')
|
|
149
|
-
if (hashed.success) {
|
|
150
|
-
const valid = crypto.password.verify('MyPassword123', hashed.data)
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
哈希格式:`$hai$<iterations>$<salt>$<hash>`
|
|
155
|
-
**PasswordConfig**:`{ saltLength?: number, iterations?: number }`
|
|
156
|
-
|
|
157
|
-
### 传输加密 — `crypto.transport`
|
|
158
|
-
|
|
159
|
-
传输加密主工厂必须从 `crypto.transport` 访问;共享 `keyStore` provider 请从 `@h-ai/crypto` 根入口导入,不从子目录内部路径导入。
|
|
160
|
-
|
|
161
|
-
| 方法 | 签名 | 说明 |
|
|
162
|
-
| --- | --- | --- |
|
|
163
|
-
| `createServer` | `(options?) => HaiResult<TransportEncryptionManager>` | 创建服务端传输加密管理器 |
|
|
164
|
-
| `createClient` | `({ keyExchangeUrl, fetch? }) => TransportClient` | 创建客户端 encryptedFetch 会话 |
|
|
165
|
-
| `protocol` | `TRANSPORT_PROTOCOL` | 协议常量:`X-Client-Id`、`X-Encrypted`、默认协商路径 |
|
|
166
|
-
|
|
167
|
-
共享 `keyStore` provider(从 `@h-ai/crypto` 根入口导入):
|
|
168
|
-
|
|
169
|
-
| 工厂 | 签名 | 说明 |
|
|
170
|
-
| --- | --- | --- |
|
|
171
|
-
| `createInMemoryKeyStore` | `(maxClients?) => TransportKeyStore` | 默认 FIFO 内存实现 |
|
|
172
|
-
| `createRedisTransportKeyStore` | `({ cache, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/cache` 的共享实现,通常配 Redis |
|
|
173
|
-
| `createReldbTransportKeyStore` | `({ reldb, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/reldb` 的共享实现,自动建表 |
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
const server = crypto.transport.createServer({ maxClients: 10000 })
|
|
177
|
-
if (!server.success)
|
|
178
|
-
return server
|
|
179
|
-
|
|
180
|
-
const client = crypto.transport.createClient({
|
|
181
|
-
keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange',
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
const resp = await client.encryptedFetch('https://api.example.com/api/v1/echo', {
|
|
185
|
-
method: 'POST',
|
|
186
|
-
body: JSON.stringify({ hello: 'world' }),
|
|
187
|
-
})
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
|
|
192
|
-
import { cache } from '@h-ai/cache'
|
|
193
|
-
|
|
194
|
-
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
|
|
195
|
-
|
|
196
|
-
const sharedServer = crypto.transport.createServer({
|
|
197
|
-
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
198
|
-
})
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
使用流程:
|
|
202
|
-
1. 先 `await crypto.init()`。
|
|
203
|
-
2. 服务端 `createServer()` 后暴露一个 POST 密钥协商端点:接收 `clientPublicKey`,返回 `serverPublicKey + clientId`。
|
|
204
|
-
3. 客户端 `createClient({ keyExchangeUrl })` 后,首次 `init()` / `encryptedFetch()` 会自动做密钥协商。
|
|
205
|
-
4. 协商完成后,请求附带 `X-Client-Id`;若请求有 body,则自动包装成 `{ encryptedKey, ciphertext, iv }`。
|
|
206
|
-
5. 服务端先 `decryptRequest()`,业务处理后再 `encryptResponse(clientId, data)`。
|
|
207
|
-
6. 客户端收到 `X-Encrypted: true` 的响应后自动解密;`destroy()` 可清空当前会话。
|
|
208
|
-
|
|
209
|
-
`encryptedFetch()` 与原生 fetch 一样会 reject:网络错误、密钥协商失败、请求加密失败或响应解密失败都应在调用方按 fetch 错误处理策略捕获。
|
|
210
|
-
|
|
211
|
-
常规应用优先使用上层封装:
|
|
212
|
-
- serv:`serv.createApp({ transport: { crypto } })`
|
|
213
|
-
- kit:`kit.createHandle({ crypto: { crypto, transport: true } })` + `kit.client.create({ transport: { crypto } })`
|
|
214
|
-
- api-client:`apiClient.init({ transport: { crypto } })`
|
|
215
|
-
|
|
216
|
-
## 错误码 — `HaiCryptoError`
|
|
217
|
-
|
|
218
|
-
| 错误码 | code | 说明 |
|
|
219
|
-
|--------|------|------|
|
|
220
|
-
| `HaiCryptoError.INVALID_INPUT` | `hai:crypto:002` | 无效输入 |
|
|
221
|
-
| `HaiCryptoError.INVALID_KEY` | `hai:crypto:003` | 无效密钥 |
|
|
222
|
-
| `HaiCryptoError.NOT_INITIALIZED` | `hai:crypto:010` | 未初始化 |
|
|
223
|
-
| `HaiCryptoError.INIT_FAILED` | `hai:crypto:011` | 初始化失败 |
|
|
224
|
-
| `HaiCryptoError.KEY_GENERATION_FAILED` | `hai:crypto:020` | 密钥生成失败 |
|
|
225
|
-
| `HaiCryptoError.ENCRYPTION_FAILED` | `hai:crypto:021` | 加密失败 |
|
|
226
|
-
| `HaiCryptoError.DECRYPTION_FAILED` | `hai:crypto:022` | 解密失败 |
|
|
227
|
-
| `HaiCryptoError.SIGN_FAILED` | `hai:crypto:023` | 签名失败 |
|
|
228
|
-
| `HaiCryptoError.VERIFY_FAILED` | `hai:crypto:024` | 验签失败 |
|
|
229
|
-
| `HaiCryptoError.HASH_FAILED` | `hai:crypto:040` | 哈希计算失败 |
|
|
230
|
-
| `HaiCryptoError.HMAC_FAILED` | `hai:crypto:041` | HMAC 计算失败 |
|
|
231
|
-
| `HaiCryptoError.INVALID_IV` | `hai:crypto:060` | 无效 IV |
|
|
232
|
-
|
|
233
|
-
## 常见模式
|
|
234
|
-
|
|
235
|
-
### 数据加密存储
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
const key = process.env.HAI_CRYPTO_DATA_KEY!
|
|
239
|
-
const encrypted = crypto.symmetric.encrypt(sensitiveData, key)
|
|
240
|
-
// 存入数据库
|
|
241
|
-
|
|
242
|
-
// 读取时解密
|
|
243
|
-
const decrypted = crypto.symmetric.decrypt(storedPayload, key)
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
> 默认密文是结构化对象,必须完整存储 `mode` / `ciphertext` / `iv` / `encoding` 字段;如果数据库字段已拆分 IV 与密文,请改用 `encryptWithIV()` / `decryptWithIV()`。
|
|
247
|
-
|
|
248
|
-
### 密码存储与验证
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
// 注册时哈希存储
|
|
252
|
-
const hashResult = crypto.password.hash(userPassword)
|
|
253
|
-
if (hashResult.success) {
|
|
254
|
-
// 存储 hashResult.data 到数据库
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// 登录时验证
|
|
258
|
-
const verifyResult = crypto.password.verify(inputPassword, storedHash)
|
|
259
|
-
if (verifyResult.success && verifyResult.data) {
|
|
260
|
-
// 密码匹配
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## 相关 Skills
|
|
265
|
-
|
|
266
|
-
- `hai-core`:配置与 HaiResult 模型
|
|
267
|
-
- `hai-iam`:密码哈希(内部自动调用 crypto.password)
|
|
268
|
-
- `hai-kit`:SvelteKit 集成(传输加密)
|
|
269
|
-
- `hai-serv` / `hai-api-client`:跨域 HTTP API 的透明传输加密
|
|
270
|
-
|
|
271
|
-
管理端在线加密演示直接调用 Crypto SM3/SM4 API,SM4 使用随机临时密钥和 IV,并在显示成功前解密校验;密钥不保存。输入改变即清空旧结果,禁止用随机摘要或 Base64 伪装算法结果。
|
|
29
|
+
- [运行环境](reference.md#运行环境)
|
|
30
|
+
- [适用场景](reference.md#适用场景)
|
|
31
|
+
- [使用步骤](reference.md#使用步骤)
|
|
32
|
+
- [核心 API](reference.md#核心-api)
|
|
33
|
+
- [错误码 — `HaiCryptoError`](reference.md#错误码--haicryptoerror)
|
|
34
|
+
- [常见模式](reference.md#常见模式)
|
|
35
|
+
- [相关 Skills](reference.md#相关-skills)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# hai-crypto 参考
|
|
2
|
+
|
|
3
|
+
> `@h-ai/crypto` 提供非对称加密、哈希、对称加密与密码哈希能力,支持 Node.js 与浏览器双端。
|
|
4
|
+
|
|
5
|
+
## 运行环境
|
|
6
|
+
|
|
7
|
+
**Node.js + 浏览器双端可用。** 浏览器端通过 `crypto.init()` 初始化(无需配置参数)。
|
|
8
|
+
|
|
9
|
+
| 能力 | Node.js | 浏览器 | 说明 |
|
|
10
|
+
|------|---------|--------|------|
|
|
11
|
+
| `crypto.asymmetric` 非对称加密 | ✅ | ✅ | SM2 加解密、签名验签 |
|
|
12
|
+
| `crypto.hash` 哈希 | ✅ | ✅ | SM3 哈希、HMAC |
|
|
13
|
+
| `crypto.symmetric` 对称加密 | ✅ | ✅ | SM4 ECB/CBC |
|
|
14
|
+
| `crypto.password` 密码哈希 | ✅ | ❌ | 依赖 Node.js crypto(服务端专用) |
|
|
15
|
+
| `crypto.transport` 传输加密 | ✅ | ✅ | 服务端管理器 + 客户端 encryptedFetch |
|
|
16
|
+
|
|
17
|
+
浏览器端主要用于 kit 传输加密场景(`kit.client.create({ transport: { crypto } })`),一般不需要直接调用 crypto API。
|
|
18
|
+
|
|
19
|
+
## 适用场景
|
|
20
|
+
|
|
21
|
+
- 非对称加密/解密、签名/验签、密钥生成
|
|
22
|
+
- 哈希计算与 HMAC
|
|
23
|
+
- 对称加密/解密(ECB/CBC 模式)
|
|
24
|
+
- 密码存储与验证(加盐迭代哈希)
|
|
25
|
+
- 传输加密:为 serv / kit / api-client 提供统一协议、密钥协商和请求响应加解密
|
|
26
|
+
|
|
27
|
+
## 使用步骤
|
|
28
|
+
|
|
29
|
+
### 1. 初始化与关闭
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { crypto } from '@h-ai/crypto'
|
|
33
|
+
|
|
34
|
+
await crypto.init()
|
|
35
|
+
|
|
36
|
+
// 使用后关闭
|
|
37
|
+
await crypto.close()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 核心 API
|
|
41
|
+
|
|
42
|
+
### 非对称加密 — `crypto.asymmetric`
|
|
43
|
+
|
|
44
|
+
| 方法 | 签名 | 说明 |
|
|
45
|
+
| ------------------- | ----------------------------------------------------------- | ------------ |
|
|
46
|
+
| `generateKeyPair` | `() => HaiResult<KeyPair>` | 生成密钥对 |
|
|
47
|
+
| `encrypt` | `(data, publicKey, options?) => HaiResult<string>` | 公钥加密 |
|
|
48
|
+
| `decrypt` | `(ciphertext, privateKey, options?) => HaiResult<string>` | 私钥解密 |
|
|
49
|
+
| `sign` | `(data, privateKey, options?) => HaiResult<string>` | 私钥签名 |
|
|
50
|
+
| `verify` | `(data, signature, publicKey, options?) => HaiResult<boolean>` | 公钥验签 |
|
|
51
|
+
| `isValidPublicKey` | `(key: string) => boolean` | 校验公钥格式 |
|
|
52
|
+
| `isValidPrivateKey` | `(key: string) => boolean` | 校验私钥格式 |
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const keyPair = crypto.asymmetric.generateKeyPair()
|
|
56
|
+
if (keyPair.success) {
|
|
57
|
+
const { publicKey, privateKey } = keyPair.data
|
|
58
|
+
|
|
59
|
+
const encrypted = crypto.asymmetric.encrypt('敏感数据', publicKey)
|
|
60
|
+
if (encrypted.success) {
|
|
61
|
+
const decrypted = crypto.asymmetric.decrypt(encrypted.data, privateKey)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const signature = crypto.asymmetric.sign('待签名数据', privateKey)
|
|
65
|
+
if (signature.success) {
|
|
66
|
+
const valid = crypto.asymmetric.verify('待签名数据', signature.data, publicKey)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**AsymmetricEncryptOptions**:`{ cipherMode?: 0 | 1, outputFormat?: 'hex' | 'base64' }`
|
|
72
|
+
**SignOptions**:`{ hash?: boolean, userId?: string }`
|
|
73
|
+
|
|
74
|
+
### 哈希 — `crypto.hash`
|
|
75
|
+
|
|
76
|
+
| 方法 | 签名 | 说明 |
|
|
77
|
+
| -------- | ---------------------------------------------------------- | -------- |
|
|
78
|
+
| `hash` | `(data: string \| Uint8Array, options?) => HaiResult<string>` | 哈希 |
|
|
79
|
+
| `hmac` | `(data, key) => HaiResult<string>` | HMAC |
|
|
80
|
+
| `verify` | `(data, expectedHash) => HaiResult<boolean>` | 验证哈希 |
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const hash = crypto.hash.hash('hello')
|
|
84
|
+
const hmac = crypto.hash.hmac('hello', 'secret-key')
|
|
85
|
+
const valid = crypto.hash.verify('hello', hash.data!)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**HashOptions**:`{ inputEncoding?: 'utf8' | 'hex' }`
|
|
89
|
+
|
|
90
|
+
### 对称加密 — `crypto.symmetric`
|
|
91
|
+
|
|
92
|
+
| 方法 | 签名 | 说明 |
|
|
93
|
+
| --------------- | ----------------------------------------------- | --------------------------- |
|
|
94
|
+
| `generateKey` | `() => string` | 生成 128-bit 密钥(32 hex) |
|
|
95
|
+
| `generateIV` | `() => string` | 生成随机 IV(32 hex) |
|
|
96
|
+
| `encrypt` | `(data, key, options?) => HaiResult<SymmetricEncryptedPayload>` | 加密(返回结构化密文) |
|
|
97
|
+
| `decrypt` | `(payload, key) => HaiResult<string>` | 解密结构化密文 |
|
|
98
|
+
| `encryptWithIV` | `(data, key) => HaiResult<EncryptWithIVResult>` | CBC 模式加密(自动生成 IV) |
|
|
99
|
+
| `decryptWithIV` | `(ciphertext, key, iv) => HaiResult<string>` | CBC 模式解密 |
|
|
100
|
+
| `deriveKey` | `(password, salt) => string` | 从密码和盐值派生密钥 |
|
|
101
|
+
| `isValidKey` | `(key: string) => boolean` | 校验密钥格式 |
|
|
102
|
+
| `isValidIV` | `(iv: string) => boolean` | 校验 IV 格式 |
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const key = crypto.symmetric.generateKey()
|
|
106
|
+
|
|
107
|
+
// 默认安全用法:CBC + 自动 IV,返回 { mode, ciphertext, iv, encoding }
|
|
108
|
+
const encrypted = crypto.symmetric.encrypt('明文数据', key)
|
|
109
|
+
if (encrypted.success) {
|
|
110
|
+
const decrypted = crypto.symmetric.decrypt(encrypted.data, key)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 需要单独存储 IV 时使用结构化结果
|
|
114
|
+
const withIV = crypto.symmetric.encryptWithIV('明文数据', key)
|
|
115
|
+
if (withIV.success) {
|
|
116
|
+
const decrypted = crypto.symmetric.decryptWithIV(withIV.data.ciphertext, key, withIV.data.iv)
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**SymmetricEncryptOptions**:`{ mode?: 'ecb' | 'cbc', iv?: string, outputFormat?: 'hex' | 'base64' }`
|
|
121
|
+
**SymmetricEncryptedPayload**:`{ mode: 'ecb' | 'cbc', ciphertext: string, iv?: string, encoding: 'hex' | 'base64' }`
|
|
122
|
+
|
|
123
|
+
> `encrypt()` 默认使用 CBC 并自动生成 IV。ECB 会泄漏明文结构,只有底层协议明确要求时才显式 `{ mode: 'ecb' }`。
|
|
124
|
+
|
|
125
|
+
### 密码哈希 — `crypto.password`
|
|
126
|
+
|
|
127
|
+
| 方法 | 签名 | 说明 |
|
|
128
|
+
| -------- | --------------------------------------- | -------- |
|
|
129
|
+
| `hash` | `(password, config?) => HaiResult<string>` | 密码哈希 |
|
|
130
|
+
| `verify` | `(password, hash) => HaiResult<boolean>` | 验证密码 |
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const hashed = crypto.password.hash('MyPassword123')
|
|
134
|
+
if (hashed.success) {
|
|
135
|
+
const valid = crypto.password.verify('MyPassword123', hashed.data)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
哈希格式:`$hai$<iterations>$<salt>$<hash>`
|
|
140
|
+
**PasswordConfig**:`{ saltLength?: number, iterations?: number }`
|
|
141
|
+
|
|
142
|
+
### 传输加密 — `crypto.transport`
|
|
143
|
+
|
|
144
|
+
传输加密主工厂必须从 `crypto.transport` 访问;共享 `keyStore` provider 请从 `@h-ai/crypto` 根入口导入,不从子目录内部路径导入。
|
|
145
|
+
|
|
146
|
+
| 方法 | 签名 | 说明 |
|
|
147
|
+
| --- | --- | --- |
|
|
148
|
+
| `createServer` | `(options?) => HaiResult<TransportEncryptionManager>` | 创建服务端传输加密管理器 |
|
|
149
|
+
| `createClient` | `({ keyExchangeUrl, fetch? }) => TransportClient` | 创建客户端 encryptedFetch 会话 |
|
|
150
|
+
| `protocol` | `TRANSPORT_PROTOCOL` | 协议常量:`X-Client-Id`、`X-Encrypted`、默认协商路径 |
|
|
151
|
+
|
|
152
|
+
共享 `keyStore` provider(从 `@h-ai/crypto` 根入口导入):
|
|
153
|
+
|
|
154
|
+
| 工厂 | 签名 | 说明 |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `createInMemoryKeyStore` | `(maxClients?) => TransportKeyStore` | 默认 FIFO 内存实现 |
|
|
157
|
+
| `createRedisTransportKeyStore` | `({ cache, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/cache` 的共享实现,通常配 Redis |
|
|
158
|
+
| `createReldbTransportKeyStore` | `({ reldb, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/reldb` 的共享实现,自动建表 |
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const server = crypto.transport.createServer({ maxClients: 10000 })
|
|
162
|
+
if (!server.success)
|
|
163
|
+
return server
|
|
164
|
+
|
|
165
|
+
const client = crypto.transport.createClient({
|
|
166
|
+
keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange',
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const resp = await client.encryptedFetch('https://api.example.com/api/v1/echo', {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
body: JSON.stringify({ hello: 'world' }),
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
|
|
177
|
+
import { cache } from '@h-ai/cache'
|
|
178
|
+
|
|
179
|
+
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
|
|
180
|
+
|
|
181
|
+
const sharedServer = crypto.transport.createServer({
|
|
182
|
+
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
使用流程:
|
|
187
|
+
1. 先 `await crypto.init()`。
|
|
188
|
+
2. 服务端 `createServer()` 后暴露一个 POST 密钥协商端点:接收 `clientPublicKey`,返回 `serverPublicKey + clientId`。
|
|
189
|
+
3. 客户端 `createClient({ keyExchangeUrl })` 后,首次 `init()` / `encryptedFetch()` 会自动做密钥协商。
|
|
190
|
+
4. 协商完成后,请求附带 `X-Client-Id`;若请求有 body,则自动包装成 `{ encryptedKey, ciphertext, iv }`。
|
|
191
|
+
5. 服务端先 `decryptRequest()`,业务处理后再 `encryptResponse(clientId, data)`。
|
|
192
|
+
6. 客户端收到 `X-Encrypted: true` 的响应后自动解密;`destroy()` 可清空当前会话。
|
|
193
|
+
|
|
194
|
+
`encryptedFetch()` 与原生 fetch 一样会 reject:网络错误、密钥协商失败、请求加密失败或响应解密失败都应在调用方按 fetch 错误处理策略捕获。
|
|
195
|
+
|
|
196
|
+
常规应用优先使用上层封装:
|
|
197
|
+
- serv:`serv.createApp({ transport: { crypto } })`
|
|
198
|
+
- kit:`kit.createHandle({ crypto: { crypto, transport: true } })` + `kit.client.create({ transport: { crypto } })`
|
|
199
|
+
- api-client:`apiClient.init({ transport: { crypto } })`
|
|
200
|
+
|
|
201
|
+
## 错误码 — `HaiCryptoError`
|
|
202
|
+
|
|
203
|
+
| 错误码 | code | 说明 |
|
|
204
|
+
|--------|------|------|
|
|
205
|
+
| `HaiCryptoError.INVALID_INPUT` | `hai:crypto:002` | 无效输入 |
|
|
206
|
+
| `HaiCryptoError.INVALID_KEY` | `hai:crypto:003` | 无效密钥 |
|
|
207
|
+
| `HaiCryptoError.NOT_INITIALIZED` | `hai:crypto:010` | 未初始化 |
|
|
208
|
+
| `HaiCryptoError.INIT_FAILED` | `hai:crypto:011` | 初始化失败 |
|
|
209
|
+
| `HaiCryptoError.KEY_GENERATION_FAILED` | `hai:crypto:020` | 密钥生成失败 |
|
|
210
|
+
| `HaiCryptoError.ENCRYPTION_FAILED` | `hai:crypto:021` | 加密失败 |
|
|
211
|
+
| `HaiCryptoError.DECRYPTION_FAILED` | `hai:crypto:022` | 解密失败 |
|
|
212
|
+
| `HaiCryptoError.SIGN_FAILED` | `hai:crypto:023` | 签名失败 |
|
|
213
|
+
| `HaiCryptoError.VERIFY_FAILED` | `hai:crypto:024` | 验签失败 |
|
|
214
|
+
| `HaiCryptoError.HASH_FAILED` | `hai:crypto:040` | 哈希计算失败 |
|
|
215
|
+
| `HaiCryptoError.HMAC_FAILED` | `hai:crypto:041` | HMAC 计算失败 |
|
|
216
|
+
| `HaiCryptoError.INVALID_IV` | `hai:crypto:060` | 无效 IV |
|
|
217
|
+
|
|
218
|
+
## 常见模式
|
|
219
|
+
|
|
220
|
+
### 数据加密存储
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const key = process.env.HAI_CRYPTO_DATA_KEY!
|
|
224
|
+
const encrypted = crypto.symmetric.encrypt(sensitiveData, key)
|
|
225
|
+
// 存入数据库
|
|
226
|
+
|
|
227
|
+
// 读取时解密
|
|
228
|
+
const decrypted = crypto.symmetric.decrypt(storedPayload, key)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> 默认密文是结构化对象,必须完整存储 `mode` / `ciphertext` / `iv` / `encoding` 字段;如果数据库字段已拆分 IV 与密文,请改用 `encryptWithIV()` / `decryptWithIV()`。
|
|
232
|
+
|
|
233
|
+
### 密码存储与验证
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// 注册时哈希存储
|
|
237
|
+
const hashResult = crypto.password.hash(userPassword)
|
|
238
|
+
if (hashResult.success) {
|
|
239
|
+
// 存储 hashResult.data 到数据库
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 登录时验证
|
|
243
|
+
const verifyResult = crypto.password.verify(inputPassword, storedHash)
|
|
244
|
+
if (verifyResult.success && verifyResult.data) {
|
|
245
|
+
// 密码匹配
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## 相关 Skills
|
|
250
|
+
|
|
251
|
+
- `hai-core`:配置与 HaiResult 模型
|
|
252
|
+
- `hai-iam`:密码哈希(内部自动调用 crypto.password)
|
|
253
|
+
- `hai-kit`:SvelteKit 集成(传输加密)
|
|
254
|
+
- `hai-serv` / `hai-api-client`:跨域 HTTP API 的透明传输加密
|
|
255
|
+
|
|
256
|
+
管理端在线加密演示直接调用 Crypto SM3/SM4 API,SM4 使用随机临时密钥和 IV,并在显示成功前解密校验;密钥不保存。输入改变即清空旧结果,禁止用随机摘要或 Base64 伪装算法结果。
|