Files
plates-server/docs/ios-encryption-guide.md
2025-08-13 15:17:33 +08:00

224 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# iOS端加密对接指南
## 概述
本文档描述了如何在iOS端实现与服务端兼容的AES-256-GCM加解密功能。
## 加密规格
- **算法**: AES-256-GCM
- **密钥长度**: 256位 (32字节)
- **IV长度**: 96位 (12字节)
- **认证标签长度**: 128位 (16字节)
- **数据格式**: Base64编码的 `IV + AuthTag + Ciphertext`
## 环境变量配置
在服务端设置环境变量 `ENCRYPTION_KEY`,确保密钥安全:
```bash
export ENCRYPTION_KEY="your-32-character-secret-key-here"
```
## iOS Swift 实现
### 1. 创建加密工具类
```swift
import Foundation
import CryptoKit
class EncryptionHelper {
private static let keyLength = 32
private static let ivLength = 12
private static let tagLength = 16
private static let additionalData = "additional-data".data(using: .utf8)!
// 从配置或钥匙串获取密钥
private static let encryptionKey: SymmetricKey = {
let keyString = "your-32-character-secret-key-here" // 应从安全存储获取
let keyData = keyString.prefix(keyLength).padding(toLength: keyLength, withPad: "0", startingAt: 0).data(using: .utf8)!
return SymmetricKey(data: keyData)
}()
/// 加密数据
/// - Parameter plaintext: 要加密的明文字符串
/// - Returns: Base64编码的加密数据格式iv + tag + ciphertext
static func encrypt(_ plaintext: String) throws -> String {
guard let data = plaintext.data(using: .utf8) else {
throw EncryptionError.invalidInput
}
// 生成随机IV
let iv = Data((0..<ivLength).map { _ in UInt8.random(in: 0...255) })
// 使用AES-GCM加密
let sealedBox = try AES.GCM.seal(data, using: encryptionKey, nonce: AES.GCM.Nonce(data: iv), authenticating: additionalData)
// 组合数据IV + Tag + Ciphertext
var combined = Data()
combined.append(iv)
combined.append(sealedBox.tag)
combined.append(sealedBox.ciphertext)
return combined.base64EncodedString()
}
/// 解密数据
/// - Parameter encryptedData: Base64编码的加密数据
/// - Returns: 解密后的明文字符串
static func decrypt(_ encryptedData: String) throws -> String {
guard let combined = Data(base64Encoded: encryptedData) else {
throw EncryptionError.invalidInput
}
guard combined.count >= ivLength + tagLength else {
throw EncryptionError.invalidDataLength
}
// 提取各部分
let iv = combined.prefix(ivLength)
let tag = combined.dropFirst(ivLength).prefix(tagLength)
let ciphertext = combined.dropFirst(ivLength + tagLength)
// 创建密封盒进行解密
let sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: iv), ciphertext: ciphertext, tag: tag)
let decryptedData = try AES.GCM.open(sealedBox, using: encryptionKey, authenticating: additionalData)
guard let plaintext = String(data: decryptedData, encoding: .utf8) else {
throw EncryptionError.decodingFailed
}
return plaintext
}
}
enum EncryptionError: Error {
case invalidInput
case invalidDataLength
case decodingFailed
}
```
### 2. 使用示例
```swift
// 创建用户数据
struct CreateUserRequest: Codable {
let id: String
let name: String
let mail: String
}
// 加密请求体
struct EncryptedRequest: Codable {
let encryptedData: String
}
// 加密响应体
struct EncryptedResponse: Codable {
let success: Bool
let message: String
let encryptedData: String?
}
// 使用示例
func createUserWithEncryption() async {
do {
// 1. 准备用户数据
let userData = CreateUserRequest(
id: "1234567890",
name: "张三",
mail: "zhangsan@example.com"
)
// 2. 序列化为JSON
let jsonData = try JSONEncoder().encode(userData)
let jsonString = String(data: jsonData, encoding: .utf8)!
// 3. 加密数据
let encryptedData = try EncryptionHelper.encrypt(jsonString)
// 4. 创建请求体
let request = EncryptedRequest(encryptedData: encryptedData)
// 5. 发送请求
let response = try await sendEncryptedRequest(request)
// 6. 解密响应(如果有加密数据)
if let encryptedResponseData = response.encryptedData {
let decryptedResponse = try EncryptionHelper.decrypt(encryptedResponseData)
print("解密后的响应: \(decryptedResponse)")
}
} catch {
print("加密通信失败: \(error)")
}
}
func sendEncryptedRequest(_ request: EncryptedRequest) async throws -> EncryptedResponse {
let url = URL(string: "https://your-server.com/users/encrypted")!
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestData = try JSONEncoder().encode(request)
urlRequest.httpBody = requestData
let (data, _) = try await URLSession.shared.data(for: urlRequest)
return try JSONDecoder().decode(EncryptedResponse.self, from: data)
}
```
## API 接口
### 加密版创建用户接口
**端点**: `POST /users/encrypted`
**请求体**:
```json
{
"encryptedData": "base64编码的加密数据"
}
```
**响应体**:
```json
{
"success": true,
"message": "用户创建成功",
"encryptedData": "base64编码的加密响应数据"
}
```
## 安全建议
1. **密钥管理**:
- 生产环境必须使用环境变量或安全密钥管理服务
- iOS端应将密钥存储在钥匙串中
- 定期轮换密钥
2. **传输安全**:
- 始终使用HTTPS
- 考虑添加请求签名验证
3. **错误处理**:
- 不要在错误信息中暴露加密细节
- 记录加密失败日志用于监控
4. **测试**:
- 确保iOS端和服务端使用相同的密钥
- 测试各种边界情况和错误场景
## 故障排除
1. **解密失败**: 检查密钥是否一致
2. **格式错误**: 确认Base64编码格式正确
3. **数据长度错误**: 验证IV和Tag长度是否正确