iOS 开发中的AES加密

前言

在iOS的日常开发中,特别是设计网络请求时,会用到加密算法,例如在客户端需要发起一个HTTP请求给服务端,其中会传递一些参数,为了防止参数在网络传输过程中被窃取或者篡改,我们就需要使用一些加密算法来对请求参数加密和签名。今天就重点介绍一下AES和HMAC_SHA256两个算法,因为服务端大多数都是使用java语言来编写,AES算法在iOS的Objective-C中和java的实现有些差异,本文重点介绍AES在iOS开发中的应用和需要注意的事项。

AES 加密算法简介

AES是一种典型的对称加密/解密算法,使用加密函数和密钥来完成对明文的加密,然后使用相同的密钥和对应的函数来完成解密。AES的优点在于效率非常高,相比RSA要高得多。AES共有ECB、CBC、CFB和OFB四种加密模式。

在iOS中的实现

Objective-C中支持AES的ECB和CBC两种模式。 1、电码本模式(Electronic Codebook Book (ECB)) 这种模式主要是将明文划分为几个明文段,分块加密,但是加密密钥是相同的。 2、密码分组链接模式(Cipher Block Chaining (CBC)) 这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

ECB是最简单的一种模式,只需要传入待加密的内容和加密的key即可。(一般不推荐ECB模式) CBC的特点是,除了需要传入加密的内容和加密的key,还需要传入初始化向量iv。即使每次加密的内容和加密的key相同,只要调整iv就可以让最终生成的密文不同。 在客户端和服务端之间传输数据一般是使用约定好的key对指定参数做AES的CBC加密,初始化向量可以随机动态生成,最终将生成好的密文和随机向量iv拼接在一起传给服务端。如:iv+密文。 iv是指定的长度如16位,这样服务端拿到客户端传输过来的数据可以先取前16位作为iv,剩余的是需要解析的密文。这么做大大提升了数据的安全性和破解难度。即使相同的带加密参数,因为有随机向量的参入,最终生成的密文也不相同。

iOS中一般使用#import <CommonCrypto/CommonCryptor.h>库中的这个函数:

Objc 复制代码
CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. */
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector */
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved)
    API_AVAILABLE(macos(10.4), ios(2.0));
  • CCOperationkCCEncrypt 加密,kCCDecrypt 解密
ini 复制代码
enum {
    kCCEncrypt = 0,
    kCCDecrypt,
};
typedef uint32_t CCOperation;
  • CCAlgorithm:加密算法、默认为AES
arduino 复制代码
enum {
    kCCAlgorithmAES128 = 0, /* Deprecated, name phased out due to ambiguity with key size */
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,
    kCCAlgorithmCAST,
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,
    kCCAlgorithmBlowfish
};
typedef uint32_t CCAlgorithm;
  • CCOptions:加密模式
    ECBkCCOptionPKCS7Padding | kCCOptionECBMode
    CBCkCCOptionPKCS7Padding
arduino 复制代码
enum {
    /* options for block ciphers */
    kCCOptionPKCS7Padding   = 0x0001,
    kCCOptionECBMode        = 0x0002
    /* stream ciphers currently have no options */
};
typedef uint32_t CCOptions;
  • key:密钥
  • keyLength:密钥长度
  • iviv 初始化向量,ECB 不需要。iv定长所以不需要长度(8字节)。
  • dataIn:加密/解密的数据
  • dataInLength:加密/解密的数据长度
  • dataOut:缓冲区(地址),存放密文/明文
  • dataOutAvailable:缓冲区大小
  • dataOutMoved:加密/解密结果大小

封装如下:

ini 复制代码
/**
     *  解密字符串
     *
     *  @param string    加密并base64编码后的字符串
     *  @param keyString 解密密钥
     *  @param iv        初始化向量(8个字节)
     *
     *  @return 返回解密后的字符串
     */
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 设置秘钥
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    
    // 设置iv
    uint8_t cIv[self.blockSize];
    bzero(cIv, self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;//CBC 加密!
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;//ECB加密!
    }
    
    // 设置输出缓冲区
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 开始解密
    size_t decryptedSize = 0;

    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          self.algorithm,
                                          option,
                                          cKey,
                                          self.keySize,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &decryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
        NSLog(@"[错误] 解密失败|状态编码: %d", cryptStatus);
    }
    
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}

上文提到使用CBC模式,可以创建一个随机的iv:

objectivec 复制代码
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>

NSData *generateRandomIV(size_t length) {
    NSMutableData *randomIV = [NSMutableData dataWithLength:length];
    int result = SecRandomCopyBytes(kSecRandomDefault, length, randomIV.mutableBytes);
    
    if (result == errSecSuccess) {
        return randomIV;
    } else {
        // 处理生成随机IV失败的情况
        return nil;
    }
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 设置AES加密参数
        NSData *key = [@"YourAESKey123456" dataUsingEncoding:NSUTF8StringEncoding];
        size_t ivLength = kCCBlockSizeAES128; // IV长度为16字节(AES-128)
        
        // 生成随机IV
        NSData *randomIV = generateRandomIV(ivLength);
        
        if (randomIV) {
            // 使用randomIV进行AES加密
            // 这里你可以调用相应的加密方法,传入randomIV作为IV参数
            // 例如,使用CommonCrypto库进行AES加密
            // 具体实现将取决于你所使用的加密库和算法
            
            // 示例:在这里调用AES加密函数,传入key和randomIV
            // ...
        } else {
            NSLog(@"生成随机IV失败");
        }
    }
    return 0;
}
相关推荐
Rhys..13 分钟前
如何禁止chrome自动更新
前端·chrome
巴巴_羊20 分钟前
AJAX 使用 和 HTTP
前端·http·ajax
刺客-Andy27 分钟前
React 第四十一节Router 中 useActionData 使用方法案例以及注意事项
前端·react.js·前端框架
岁岁岁平安31 分钟前
Vue3学习(组合式API——reactive()和ref()函数详解)
前端·javascript·vue.js·学习·vue3·reactive·ref
肠胃炎33 分钟前
React事件机制
前端·javascript·react.js
CUIYD_198940 分钟前
javascript —— ! 和 !! 的区别与作用
前端·javascript·vue.js
帅帅哥的兜兜2 小时前
next.js实现项目搭建
前端·react.js·next.js
筱歌儿2 小时前
css 左右布局
前端·css
GISer_Jing3 小时前
编译原理AST&以Babel为例进行解读、Webpack中自定义loader与plugin
前端·webpack·node.js
GISer_Jing3 小时前
Webpack中Compiler详解以及自定义loader和plugin详解
前端·webpack·node.js