使用AES加密数据传输的iOS客户端实现方案

发布于:2024-07-03 ⋅ 阅读:(14) ⋅ 点赞:(0)

在现代应用开发中,确保数据传输的安全性是至关重要的。本文将介绍如何在iOS客户端中使用AES加密数据传输,并与服务器端保持加密解密的一致性。本文不会包含服务器端代码,但会解释其实现原理。

加密与解密的基本原理

AES(Advanced Encryption Standard)是一种对称加密算法,即加密和解密使用相同的密钥。为了增强安全性,AES通常与CBC(Cipher Block Chaining)模式和随机生成的初始化向量(IV)一起使用。

为什么IV明文传输不会影响安全性?
  1. 防止重复模式:IV确保相同的明文在每次加密时产生不同的密文,从而防止攻击者通过观察加密输出模式来推断输入模式。
  2. 随机性和唯一性:IV的唯一要求是随机性和唯一性,而不需要保密。因此,IV可以以明文形式与加密数据一起传输。
  3. 加密标准:大多数现代加密标准和协议(如AES-CBC、AES-GCM)都规定IV可以以明文形式传输。

客户端实现方案

在iOS客户端中,我们将使用以下步骤来实现AES加密数据传输:

  1. 生成和存储加密密钥:在用户登录成功后,从服务器获取并存储加密密钥。
  2. 加密请求数据:在每次发送请求时,使用AES和随机生成的IV对请求数据进行加密,并将IV与加密数据一起传输。
  3. 解密响应数据:在接收到服务器响应后,使用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

为了处理请求和响应的加密解密,我们将创建自定义的AFJSONRequestSerializerAFHTTPResponseSerializer

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并将其明文传输,我们可以有效地防止重复模式攻击,增强数据传输的安全性。希望这篇文章对你在应用开发中的数据安全保护有所帮助。


网站公告

今日签到

点亮在社区的每一天
去签到