# 加密工具

## AES-256-GCM 加密

### 概述

前端 AES 加密工具，与后端 `AesEncryptUtil` 保持一致，用于敏感信息传输加密。

### 使用场景

1. **密码传输**：登录时加密密码
2. **敏感信息**：加密手机号、身份证号等

### 快速开始

#### 1. 初始化（应用启动时）

```typescript
import { initAesEncrypt } from '@/utils/crypto'

// 在 main.ts 中初始化
const AES_SECRET_KEY = '1234567890qwer'  // 与后端配置保持一致
await initAesEncrypt(AES_SECRET_KEY)
```

#### 2. 使用加密

```typescript
import { getAesInstance } from '@/utils/crypto'

// 获取加密实例
const aes = getAesInstance()

// 加密密码
const encryptedPassword = await aes.encryptPassword('myPassword123')

// 加密手机号
const encryptedPhone = await aes.encryptPhone('13812345678')
```

### 完整示例

#### 登录时加密密码

```typescript
import { authAPI } from '@/api/auth'
import { getAesInstance } from '@/utils/crypto'

async function login(username: string, password: string) {
    try {
        // 加密密码
        const aes = getAesInstance()
        const encryptedPassword = await aes.encryptPassword(password)
        
        // 发送登录请求
        const response = await authAPI.login({
            username,
            password: encryptedPassword,
            loginType: 'password'
        })
        
        return response.data
    } catch (error) {
        console.error('登录失败:', error)
        throw error
    }
}
```

#### 手机号注册

```typescript
import { authAPI } from '@/api/auth'
import { getAesInstance } from '@/utils/crypto'

async function register(phone: string, password: string, smsCode: string) {
    try {
        const aes = getAesInstance()
        
        // 加密密码
        const encryptedPassword = await aes.encryptPassword(password)
        
        // 加密手机号
        const encryptedPhone = await aes.encryptPhone(phone)
        
        // 发送注册请求
        const response = await authAPI.register({
            registerType: 'phone',
            phone: encryptedPhone,
            password: encryptedPassword,
            confirmPassword: encryptedPassword,
            smsCode,
            smsSessionId: 'session-id-from-captcha'
        })
        
        return response.data
    } catch (error) {
        console.error('注册失败:', error)
        throw error
    }
}
```

### 工具函数

#### 数据脱敏

```typescript
import { AesUtils } from '@/utils/crypto'

// 脱敏手机号
const masked = AesUtils.maskPhone('13812345678')
// 输出：138****5678

// 脱敏身份证号
const maskedId = AesUtils.maskIdCard('110101199001011234')
// 输出：110101********1234

// 脱敏邮箱
const maskedEmail = AesUtils.maskEmail('test@example.com')
// 输出：t***@example.com
```

### API 参考

#### AesEncryptUtil 类

| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `init()` | - | `Promise<void>` | 初始化密钥（必须先调用） |
| `encrypt(plaintext)` | `string` | `Promise<string>` | 加密字符串 |
| `decrypt(ciphertext)` | `string` | `Promise<string>` | 解密字符串 |
| `encryptPassword(password)` | `string` | `Promise<string>` | 加密密码 |
| `encryptPhone(phone)` | `string` | `Promise<string>` | 加密手机号 |
| `decryptPhone(encrypted)` | `string` | `Promise<string>` | 解密手机号 |
| `encryptIdCard(idCard)` | `string` | `Promise<string>` | 加密身份证号 |
| `decryptIdCard(encrypted)` | `string` | `Promise<string>` | 解密身份证号 |

#### AesUtils 静态方法

| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `maskPhone(phone)` | `string` | `string` | 脱敏手机号 |
| `maskIdCard(idCard)` | `string` | `string` | 脱敏身份证号 |
| `maskEmail(email)` | `string` | `string` | 脱敏邮箱 |

#### 全局函数

| 函数 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `initAesEncrypt(secretKey)` | `string` | `Promise<void>` | 初始化 AES 加密（应用启动时调用） |
| `getAesInstance()` | - | `AesEncryptUtil` | 获取 AES 加密实例 |
| `createAesEncrypt(secretKey)` | `string` | `Promise<AesEncryptUtil>` | 创建新的 AES 实例 |

### 配置说明

#### 密钥配置

前端密钥必须与后端配置保持一致：

**后端配置（application.yml）**：
```yaml
security:
  aes:
    secret-key: 1234567890qwer
```

**前端配置**：
```typescript
const AES_SECRET_KEY = '1234567890qwer'  // 与后端保持一致
await initAesEncrypt(AES_SECRET_KEY)
```

#### 算法参数

| 参数 | 值 | 说明 |
|------|-----|------|
| 算法 | AES-256-GCM | 高强度加密算法 |
| 密钥长度 | 256 bits | AES-256 |
| IV 长度 | 12 bytes | GCM 推荐长度 |
| Tag 长度 | 128 bits | GCM 认证标签 |
| 编码 | Base64 | 密文编码格式 |

### 安全注意事项

1. **密钥管理**
   - 密钥不要硬编码在代码中
   - 生产环境从配置中心或环境变量获取
   - 定期轮换密钥

2. **HTTPS 传输**
   - 生产环境必须使用 HTTPS
   - 加密只是额外保障，不能替代 HTTPS

3. **密码安全**
   - 密码传输前加密
   - 后端再次使用 BCrypt 等算法加密存储
   - 前端加密防止明文传输被截获

4. **错误处理**
   - 捕获加密失败异常
   - 不要在错误信息中暴露敏感信息

### 浏览器兼容性

使用 Web Crypto API，支持以下浏览器：

- Chrome 37+
- Firefox 34+
- Safari 11+
- Edge 79+

不支持 IE 浏览器。

### 与后端对接

#### 数据流程

```
前端 ----[加密数据]----> Gateway -----> Auth Service
     (AES-256-GCM)                    (AES-256-GCM)
                                      [解密] → [BCrypt] → 数据库
```

#### 示例对比

**前端加密**：
```typescript
const encrypted = await aes.encrypt('13812345678')
// 输出：Base64([IV(12字节)][密文])
```

**后端解密**：
```java
String decrypted = aesEncryptUtil.decrypt(encrypted)
// 输出：'13812345678'
```

### 故障排查

#### 1. "AES 密钥未初始化"

**原因**：未调用 `initAesEncrypt()`

**解决**：在 `main.ts` 中初始化
```typescript
await initAesEncrypt(AES_SECRET_KEY)
```

#### 2. "AES 解密失败"

**原因**：前后端密钥不一致

**解决**：检查前后端密钥配置是否相同

#### 3. 类型错误

**原因**：TypeScript 类型问题

**解决**：确保使用最新版本的工具类

### 性能优化

1. **密钥复用**：使用单例模式，避免重复初始化
2. **异步处理**：加密操作是异步的，注意使用 `await`
3. **批量加密**：如需加密多个字段，可并行处理

```typescript
// 并行加密
const [encryptedPhone, encryptedPassword] = await Promise.all([
    aes.encryptPhone(phone),
    aes.encryptPassword(password)
])
```
