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

news2024/11/19 0:21:20

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1896604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Bellman equation的不同形式及变化

总忘记贝尔曼方程的推导过程&#xff0c;自己推一遍吧 matrix-vector form就省略了 对于matrix-vector form形式的状态价值贝尔曼方程求解&#xff0c;若已知MDP的动态&#xff08;转移矩阵P和奖励函数R&#xff09;&#xff0c;则计算复杂度的贡献主要来自矩阵求逆&#xff…

Solo 开发者周刊 (第12期):连接独立开发者,共享开源智慧

这里会整合 Solo 社区每周推广内容、产品模块或活动投稿&#xff0c;每周五发布。在这期周刊中&#xff0c;我们将深入探讨开源软件产品的开发旅程&#xff0c;分享来自一线独立开发者的经验和见解。本杂志开源&#xff0c;欢迎投稿。 产品推荐 1、Soju————一个现代的书签…

在线图片转文字的软件,分享3种强大的软件!

在信息爆炸的时代&#xff0c;图片作为信息的重要载体之一&#xff0c;其内容往往蕴含着巨大的价值。然而&#xff0c;面对海量的图片信息&#xff0c;如何高效、准确地将其转化为文字&#xff0c;成为了许多人的迫切需求。今天&#xff0c;就为大家盘点几款功能强大的在线图片…

马斯克宣布xAI将在8月份推出Grok-2大模型 预计年底推出Grok-3

在今年内&#xff0c;由特斯拉创始人马斯克创立的人工智能初创公司xAI将推出两款重要产品Grok-2和Grok-3。马斯克在社交平台上透露了这一消息&#xff0c;其中Grok-2预计在今年8月份面世&#xff0c;而Grok-3则计划于年底前亮相。 除此之外&#xff0c;马斯克还表示&#xff0c…

SQLyog脚本无限试用重置脚本

文章目录 引言脚本(win)必要操作、说明 引言 SQLyog 需要po jie&#xff0c;但是网上的没看到很好使的&#xff0c;直接下的官方。能处理14天试用也是很ok的。 脚本(win) echo offREM SQLyog注册表key&#xff0c;可能跟你的不一样&#xff0c;如果不一样&#xff0c;请替换…

【TB作品】体重监控系统,ATMEGA16单片机,Proteus仿真

机电荷2018级课程设计题目及要求 题1:电子称重器设计 功能要求: 1)开机显示时间(小时、分)、时分可修改; 2)用滑动变阻器模拟称重传感器(测量范围0- 200g),数码管显示当前重量值,当重量值高于高 值时,红灯长亮; 3)当重量值低于低值时,黄灯长亮; 4)当重量值在正常值时,绿灯亮; 5…

【堆 优先队列】23. 合并 K 个升序链表

本文涉及知识点 堆 优先队列 LeetCode23. 合并 K 个升序链表 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#…

使用任意电脑通过内网穿透生成的公网地址远程SSH连接本地Windows电脑

文章目录 前言1. Windows安装SSH服务2. Windows本地连接测试3. Windows安装Cpolar工具4. 配置SSH公网地址5. 远程SSH 连接测试6. 固定SSH公网地址7. 固定SSH地址测试 前言 在当今的数字化转型时代&#xff0c;远程连接和管理计算机已成为日常工作中不可或缺的一部分。对于Wind…

JAVA--JSON转换工具类

JSON转换工具类 import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackso…

浅析MySQL-索引篇01

什么是索引&#xff1f; 索引是帮助存储引擎快速获取数据的一种数据结构&#xff0c;类似于数据的目录。 索引的分类 按数据结构分类&#xff1a; MySQL 常见索引有 BTree 索引、HASH 索引、Full-Text 索引。 Innodb是MySQL5.5之后的默认存储引擎&#xff0c;BTree索引类型也…

一些感想。

1.double必须用double的输出&#xff08;“%lf”&#xff09; 我还以为是什么bug。。 2.sqrt&#xff0c;pow只要include cmath之后就能用了&#xff0c;我pow()没有devc艹的提示&#xff0c;还以为我记错了&#xff0c;早知道运行一下了 cnm公式写错了 #include <iostre…

无人机人员搜救

人员搜救-水域救援 水域搜救&#xff1a;快速水面搜查 物资抛投&#xff1a;救生物资抛投 绳索牵引&#xff1a;牵引救援绳索 领航船艇&#xff1a;水面侦察领航 人员搜救 昼夜搜救&#xff0c;精准定位 水域搜救 经纬 M300 RTK 搭载禅思 H20T 能够满足全天候作业需求&a…

YUM——简介、安装(Ubuntu22.04)

1、简介 YUM&#xff08;Yellowdog Updater, Modified&#xff09;是一个开源的命令行软件包管理工具&#xff0c;主要用于基于 RPM 包管理系统的 Linux 发行版&#xff0c;如 CentOS、Red Hat Enterprise Linux (RHEL) 和 Fedora。YUM 使用户能够轻松地安装、更新、删除和管理…

【项目管理】项目风险管理(Word原件)

风险和机会管理就是在一个项目开发过程中对风险进行识别、跟踪、控制的手段。风险和机会管理提供了对可能出现的风险进行持续评估&#xff0c;确定重要的风险机会以及实施处理的策略的一种规范化的环境。包括识别、分析、制定处理和减缓行动、跟踪 。合理的风险和机会管理应尽力…

深度学习-数学基础(四)

深度学习数学基础 数学基础线性代数-标量和向量线性代数-向量运算向量加和向量内积向量夹角余弦值 线性代数-矩阵矩阵加法矩阵乘法矩阵点乘矩阵计算的其他内容 人工智能-矩阵的操作矩阵转置&#xff08;transpose&#xff09;矩阵与向量的转化 线性代数-张量&#xff08;tensor…

计算机应用数学--第一次作业

第一次作业计算题编程题 &#xff08;20分&#xff09; 第一次作业 计算题 &#xff08;20分&#xff09;求 E ( X ) E(X) E(X)&#xff0c; V a r ( X ) Var(X) Var(X) &#xff08;1&#xff09; X X X 服从 [ a , b ] [a,b] [a,b] 均匀分布。 &#xff08;2&#xff09;…

鸿蒙开发设备管理:【@ohos.thermal (热管理)】

热管理 该模块提供热管理相关的接口&#xff0c;包括热档位查询及注册回调等功能。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shi…

24西安电子科技大学数学与统计学院—考研录取情况

24西安电子科技大学—数学与统计学院—考研录取统计 01、数学与统计学院各个方向 02、24数学与统计学院近三年复试分数线对比 数统院24年院线相对于23年院线增加高达30分&#xff0c;确实增长浮动比较高&#xff0c;接近30分的水平&#xff0c;因此大家更需要好好去努力&#x…

swiftui中设置建议最多5个tabItem项,多个tabItem项会被自动折叠起来

在swiftui中设置底部的菜单栏的时候&#xff0c;最多建议设置5个&#xff0c;如果超过了&#xff0c;会被自动折叠到More中&#xff0c;点击More就会出现类似list的样式显示&#xff0c;不是很友好。 最多按照5个默认设置的话&#xff0c;就会正常全部显示出来&#xff1a; 测…

idea如何连接gitee仓库

1.先在idea上登录gitee账号 在gitee官网上生成令牌。 点击生成新令牌。 复制到idea上就行了。然后登陆成功。 2.连接gitee 把项目的https克隆到idea中就行了。