在现代应用开发中,确保数据传输的安全性是至关重要的。本文将介绍如何在iOS客户端中使用AES加密数据传输,并与服务器端保持加密解密的一致性。本文不会包含服务器端代码,但会解释其实现原理。
加密与解密的基本原理
AES(Advanced Encryption Standard)是一种对称加密算法,即加密和解密使用相同的密钥。为了增强安全性,AES通常与CBC(Cipher Block Chaining)模式和随机生成的初始化向量(IV)一起使用。
为什么IV明文传输不会影响安全性?
- 防止重复模式:IV确保相同的明文在每次加密时产生不同的密文,从而防止攻击者通过观察加密输出模式来推断输入模式。
- 随机性和唯一性:IV的唯一要求是随机性和唯一性,而不需要保密。因此,IV可以以明文形式与加密数据一起传输。
- 加密标准:大多数现代加密标准和协议(如AES-CBC、AES-GCM)都规定IV可以以明文形式传输。
客户端实现方案
在iOS客户端中,我们将使用以下步骤来实现AES加密数据传输:
- 生成和存储加密密钥:在用户登录成功后,从服务器获取并存储加密密钥。
- 加密请求数据:在每次发送请求时,使用AES和随机生成的IV对请求数据进行加密,并将IV与加密数据一起传输。
- 解密响应数据:在接收到服务器响应后,使用AES和IV对响应数据进行解密。
以下是实现这些步骤的详细代码。
AES加密解密类
import CommonCrypto
class AES {
// 加密方法
static func encrypt(data: Data, key: String) -> (Data, Data)? {
return crypt(data: data, key: key, operation: CCOperation(kCCEncrypt))
}
// 解密方法
static func decrypt(data: Data, key: String, iv: Data) -> Data? {
return crypt(data: data, key: key, iv: iv, operation: CCOperation(kCCDecrypt)).map { $0.0 }
}
/**
通用加密解密方法
- Parameters:
- data: 需要加密或解密的数据
- key: 用于加密或解密的密钥
- iv: 初始化向量。如果为空,则在加密时生成一个新的IV。在解密时需要传入之前生成的IV。
- operation: 加密或解密操作(kCCEncrypt 或 kCCDecrypt)
- Returns: 返回一个元组,包含加密或解密后的数据,以及用于加密的IV。如果操作失败,返回nil。
*/
private static func crypt(data: Data, key: String, iv: Data? = nil, operation: CCOperation) -> (Data, Data)? {
// 确定AES密钥长度为128位(16字节)
let keyLength = kCCKeySizeAES128
var keyBytes = [UInt8](repeating: 0, count: keyLength)
// 将密钥字符串转换为字节数组,并确保其长度为16字节
key.getCString(&keyBytes, maxLength: keyLength + 1, encoding: .utf8)
// 初始化向量(IV)的字节数组
var ivBytes: [UInt8]
if let iv = iv {
// 如果传入了IV,使用传入的IV
ivBytes = [UInt8](iv)
} else {
// 如果没有传入IV,在加密时生成一个随机的IV
let ivSize = kCCBlockSizeAES128
ivBytes = [UInt8](repeating: 0, count: ivSize)
_ = SecRandomCopyBytes(kSecRandomDefault, ivSize, &ivBytes)
}
// 输入数据的长度
let dataLength = data.count
// 输出缓冲区大小应为数据长度加上一个AES块的大小
let bufferSize = dataLength + kCCBlockSizeAES128
var buffer = [UInt8](repeating: 0, count: bufferSize)
// 存储实际加密或解密后的字节数
var numBytesCrypted: size_t = 0
// 调用CCCrypt函数执行加密或解密操作
let cryptStatus = CCCrypt(
operation, // 指定加密或解密操作
CCAlgorithm(kCCAlgorithmAES128), // 使用AES-128加密算法
CCOptions(kCCOptionPKCS7Padding), // 使用PKCS7填充
keyBytes, // 密钥字节数组
keyLength, // 密钥长度
ivBytes, // 初始化向量(IV)
[UInt8](data), // 输入数据字节数组
dataLength, // 输入数据长度
&buffer, // 输出缓冲区
bufferSize, // 输出缓冲区大小
&numBytesCrypted // 实际加密或解密后的字节数
)
// 检查加密或解密操作是否成功
if cryptStatus == kCCSuccess {
// 返回加密或解密后的数据,以及用于加密的IV
let cryptData = Data(bytes: buffer, count: numBytesCrypted)
return (cryptData, Data(ivBytes))
}
// 如果操作失败,返回nil
return nil
}
}
自定义Request和Response Serializer
为了处理请求和响应的加密解密,我们将创建自定义的AFJSONRequestSerializer
和AFHTTPResponseSerializer
。
import AFNetworking
// 自定义Request Serializer
class EncryptedJSONRequestSerializer: AFJSONRequestSerializer {
var encryptionKey: String?
override func request(bySerializingRequest request: URLRequest, withParameters parameters: Any?, error: NSErrorPointer) -> URLRequest {
var mutableRequest = request
if let encryptionKey = encryptionKey, let parameters = parameters {
do {
// 将参数序列化为JSON数据
let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [])
// 使用AES加密数据
if let (encryptedData, iv) = AES.encrypt(data: jsonData, key: encryptionKey) {
let base64String = encryptedData.base64EncodedString()
let ivString = iv.base64EncodedString()
// 将加密数据和IV一起传输
let encryptedParameters = ["data": base64String, "iv": ivString]
let encryptedJsonData = try JSONSerialization.data(withJSONObject: encryptedParameters, options: [])
mutableRequest.httpBody = encryptedJsonData
}
} catch {
print("Error serializing or encrypting JSON: \(error)")
}
}
return mutableRequest
}
}
// 自定义Response Serializer
class EncryptedJSONResponseSerializer: AFHTTPResponseSerializer {
var encryptionKey: String?
override func responseObject(for response: URLResponse, data: Data?, error: NSErrorPointer) -> Any? {
guard let encryptionKey = encryptionKey else {
if error != nil {
error?.pointee = NSError(domain: "Missing encryption key", code: -1, userInfo: nil)
}
return nil
}
guard let data = data else {
if error != nil {
error?.pointee = NSError(domain: "No data", code: -1, userInfo: nil)
}
return nil
}
do {
// 解密响应数据
if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let encryptedDataString = jsonResponse["data"] as? String,
let ivString = jsonResponse["iv"] as? String,
let encryptedData = Data(base64Encoded: encryptedDataString),
let ivData = Data(base64Encoded: ivString) {
if let decryptedData = AES.decrypt(data: encryptedData, key: encryptionKey, iv: ivData) {
return try JSONSerialization.jsonObject(with: decryptedData, options: [])
}
}
} catch {
if error != nil {
error?.pointee = error as NSError
}
}
return nil
}
}
网络管理类
我们将创建一个网络管理类来处理登录和发送加密请求。
import AFNetworking
class NetworkManager {
static let shared = NetworkManager()
private let baseURL = "https://localhost:8443"
private var token: String?
private var encryptionKey: String?
private init() {}
/**
用户登录方法
- Parameters:
- username: 用户名
- password: 密码
- completion: 登录完成后的回调,传递登录是否成功的布尔值
*/
func login(username: String, password: String, completion: @escaping (Bool) -> Void) {
let parameters: [String: Any] = ["
username": username, "password": password]
// 发起POST请求进行登录
AFHTTPSessionManager().post("\(baseURL)/login", parameters: parameters, headers: nil, progress: nil, success: { [weak self] (task, responseObject) in
if let response = responseObject as? [String: Any], let token = response["token"] as? String, let encryptionKey = response["encryptionKey"] as? String {
// 保存token和加密密钥
self?.token = token
self?.encryptionKey = encryptionKey
completion(true)
} else {
completion(false)
}
}) { (task, error) in
print("Login failed: \(error)")
completion(false)
}
}
/**
发送加密请求方法
- Parameters:
- parameters: 请求参数
- completion: 请求完成后的回调,传递结果
*/
func sendSecureRequest(parameters: [String: Any], completion: @escaping (Result<[String: Any], Error>) -> Void) {
guard let token = token, let encryptionKey = encryptionKey else {
completion(.failure(NSError(domain: "No token or encryption key", code: 0, userInfo: nil)))
return
}
let manager = AFHTTPSessionManager()
let requestSerializer = EncryptedJSONRequestSerializer()
requestSerializer.encryptionKey = encryptionKey
manager.requestSerializer = requestSerializer
let responseSerializer = EncryptedJSONResponseSerializer()
responseSerializer.encryptionKey = encryptionKey
manager.responseSerializer = responseSerializer
let headers: HTTPHeaders = ["Authorization": token]
// 发起POST请求发送加密数据
manager.post("\(baseURL)/secure-data", parameters: parameters, headers: headers, progress: nil, success: { (task, responseObject) in
if let response = responseObject as? [String: Any] {
completion(.success(response))
} else {
completion(.failure(NSError(domain: "Invalid response", code: 0, userInfo: nil)))
}
}) { (task, error) in
completion(.failure(error))
}
}
}
使用示例
let username = "testuser"
let password = "testpassword"
// 用户登录
NetworkManager.shared.login(username: username, password: password) { success in
if success {
print("Login successful")
// 发送加密请求
let parameters: [String: Any] = ["example": "data"]
NetworkManager.shared.sendSecureRequest(parameters: parameters) { result in
switch result {
case .success(let response):
print("Secure data response: \(response)")
case .failure(let error):
print("Request failed: \(error)")
}
}
} else {
print("Login failed")
}
}
总结
本文介绍了如何在iOS客户端中使用AES加密数据传输,并确保与服务器端的一致性。通过使用随机生成的IV并将其明文传输,我们可以有效地防止重复模式攻击,增强数据传输的安全性。希望这篇文章对你在应用开发中的数据安全保护有所帮助。