MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C数据加密与安全存储方案

2023-10-204.8k 阅读

一、数据加密基础

在当今数字化时代,数据的安全性至关重要。数据加密是保护敏感信息不被未授权访问和篡改的重要手段。加密的核心思想是将原始数据(明文)通过特定的算法和密钥转换为不可读的形式(密文),只有拥有正确密钥的接收者才能将密文还原为明文。

1.1 对称加密与非对称加密

  1. 对称加密 对称加密使用相同的密钥进行加密和解密。常见的对称加密算法有 AES(高级加密标准)、DES(数据加密标准)等。对称加密的优点是加密和解密速度快,适合对大量数据进行加密。缺点是密钥的管理和分发比较困难,因为通信双方需要共享相同的密钥,如果密钥泄露,密文就可以被轻易破解。 以下是使用 AES 算法在 Objective-C 中进行对称加密的简单示例代码:
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonDigest.h>
#import <Security/Security.h>

NSData *AES256Encrypt(NSData *plainText, NSString *key) {
    char keyPtr[kCCKeySizeAES256 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [plainText length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          nil,
                                          [plainText bytes], dataLength,
                                          buffer, bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
}
  1. 非对称加密 非对称加密使用一对密钥,即公钥和私钥。公钥用于加密数据,私钥用于解密数据。常见的非对称加密算法有 RSA。非对称加密的优点是密钥管理相对简单,因为公钥可以公开分发,只有私钥需要保密。缺点是加密和解密速度较慢,不适合对大量数据进行加密,通常用于加密少量的关键数据,如对称加密的密钥。 在 Objective-C 中使用 Security 框架进行 RSA 加密的示例代码如下:
#import <Security/Security.h>

NSData *RSAEncrypt(NSData *plainText, SecKeyRef publicKey) {
    size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
    uint8_t *cipherBuffer = malloc(cipherBufferSize);
    OSStatus status = SecKeyEncrypt(publicKey,
                                    kSecPaddingPKCS1,
                                    [plainText bytes], [plainText length],
                                    cipherBuffer, &cipherBufferSize);
    if (status == errSecSuccess) {
        NSData *cipherData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
        free(cipherBuffer);
        return cipherData;
    }
    free(cipherBuffer);
    return nil;
}

1.2 哈希算法

哈希算法是一种将任意长度的数据映射为固定长度的哈希值的算法。哈希值具有唯一性,即不同的数据经过哈希算法计算得到的哈希值几乎不可能相同。常见的哈希算法有 MD5、SHA - 1、SHA - 256 等。哈希算法主要用于验证数据的完整性,例如在文件传输过程中,接收方可以通过计算接收到文件的哈希值并与发送方提供的哈希值进行比较,来判断文件是否在传输过程中被篡改。 在 Objective-C 中计算 SHA - 256 哈希值的示例代码如下:

#import <CommonCrypto/CommonDigest.h>

NSData *SHA256Hash(NSData *data) {
    unsigned char digest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256([data bytes], (CC_LONG)[data length], digest);
    return [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
}

需要注意的是,MD5 和 SHA - 1 已经逐渐被弃用,因为它们存在一定的安全漏洞,SHA - 256 及以上版本相对更为安全。

二、Objective-C 中的数据加密实现

2.1 使用 CommonCrypto 框架进行对称加密

CommonCrypto 是苹果提供的用于加密和哈希计算的框架。如前面提到的 AES 加密示例,它提供了一系列函数来实现常见的对称加密算法。

  1. 加密流程
    • 准备密钥:密钥的长度必须符合所选加密算法的要求,如 AES - 256 要求密钥长度为 32 字节。
    • 设置加密选项:包括填充模式等,常见的填充模式有 PKCS7Padding 等,填充的目的是使数据长度满足加密算法的块大小要求。
    • 调用加密函数:通过 CCCrypt 函数进行加密操作,该函数接受加密模式、算法类型、选项、密钥、初始化向量(在某些算法中需要,AES 可以设置为 nil)、明文数据等参数,并返回加密后的密文。
  2. 解密流程 解密流程与加密流程类似,只是调用 CCCrypt 函数时将加密模式设置为 kCCDecrypt
NSData *AES256Decrypt(NSData *cipherText, NSString *key) {
    char keyPtr[kCCKeySizeAES256 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [cipherText length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          nil,
                                          [cipherText bytes], dataLength,
                                          buffer, bufferSize,
                                          &numBytesDecrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    free(buffer);
    return nil;
}

2.2 使用 Security 框架进行非对称加密

Security 框架提供了对非对称加密的支持,包括 RSA 等算法。

  1. 生成密钥对 在 Objective-C 中生成 RSA 密钥对的示例代码如下:
#import <Security/Security.h>

void generateRSAKeyPair(SecKeyRef *publicKey, SecKeyRef *privateKey) {
    NSDictionary *publicKeyAttr = @{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
                                    (id)kSecAttrKeySizeInBits: @2048,
                                    (id)kSecPublicKeyAttrs: @{(id)kSecAttrIsPermanent: @NO}};
    NSDictionary *privateKeyAttr = @{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
                                     (id)kSecAttrKeySizeInBits: @2048,
                                     (id)kSecPrivateKeyAttrs: @{(id)kSecAttrIsPermanent: @NO}};

    NSArray *keyPairAttr = @[publicKeyAttr, privateKeyAttr];
    CFTypeRef result = nil;
    OSStatus status = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &result);
    if (status == errSecSuccess) {
        CFArrayRef keyArray = (CFArrayRef)result;
        *publicKey = (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0);
        *privateKey = (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 1);
    }
}
  1. 加密与解密 加密过程如前面 RSAEncrypt 函数所示,解密过程类似,通过 SecKeyDecrypt 函数实现:
NSData *RSADecrypt(NSData *cipherText, SecKeyRef privateKey) {
    size_t plainBufferSize = SecKeyGetBlockSize(privateKey);
    uint8_t *plainBuffer = malloc(plainBufferSize);
    OSStatus status = SecKeyDecrypt(privateKey,
                                    kSecPaddingPKCS1,
                                    [cipherText bytes], [cipherText length],
                                    plainBuffer, &plainBufferSize);
    if (status == errSecSuccess) {
        NSData *plainData = [NSData dataWithBytes:plainBuffer length:plainBufferSize];
        free(plainBuffer);
        return plainData;
    }
    free(plainBuffer);
    return nil;
}

三、安全存储方案

3.1 Keychain 存储敏感信息

Keychain 是苹果提供的用于安全存储敏感信息的机制,如密码、证书等。它具有很高的安全性,数据存储在设备的硬件安全模块(HSM)中,并且只有经过授权的应用才能访问。

  1. 添加数据到 Keychain
#import <Security/Security.h>

BOOL addItemToKeychain(NSString *service, NSString *account, NSString *password) {
    NSDictionary *query = @{(id)kSecClass: (id)kSecClassGenericPassword,
                            (id)kSecAttrService: service,
                            (id)kSecAttrAccount: account,
                            (id)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]};
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
    return status == errSecSuccess;
}
  1. 从 Keychain 读取数据
NSString *readItemFromKeychain(NSString *service, NSString *account) {
    NSDictionary *query = @{(id)kSecClass: (id)kSecClassGenericPassword,
                            (id)kSecAttrService: service,
                            (id)kSecAttrAccount: account,
                            (id)kSecReturnData: @YES,
                            (id)kSecMatchLimit: (id)kSecMatchLimitOne};
    CFDataRef result = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status == errSecSuccess) {
        NSString *password = [[NSString alloc] initWithData:(__bridge NSData *)result encoding:NSUTF8StringEncoding];
        CFRelease(result);
        return password;
    }
    return nil;
}
  1. 更新 Keychain 中的数据
BOOL updateItemInKeychain(NSString *service, NSString *account, NSString *newPassword) {
    NSDictionary *query = @{(id)kSecClass: (id)kSecClassGenericPassword,
                            (id)kSecAttrService: service,
                            (id)kSecAttrAccount: account};
    NSDictionary *update = @{(id)kSecValueData: [newPassword dataUsingEncoding:NSUTF8StringEncoding]};
    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update);
    return status == errSecSuccess;
}
  1. 从 Keychain 删除数据
BOOL deleteItemFromKeychain(NSString *service, NSString *account) {
    NSDictionary *query = @{(id)kSecClass: (id)kSecClassGenericPassword,
                            (id)kSecAttrService: service,
                            (id)kSecAttrAccount: account};
    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
    return status == errSecSuccess;
}

3.2 加密本地文件存储

对于一些需要在本地存储的敏感数据,如用户的个人资料、配置文件等,可以先对数据进行加密,然后再存储到文件系统中。

  1. 加密文件存储流程
    • 选择加密算法:如前面提到的 AES 算法。
    • 生成密钥:可以使用 Keychain 来安全存储密钥。
    • 读取数据:从应用的内存或其他数据源读取需要存储的数据。
    • 加密数据:使用选择的加密算法和密钥对数据进行加密。
    • 存储加密后的数据:将加密后的密文写入文件系统。
  2. 解密文件读取流程
    • 读取加密数据:从文件系统中读取加密后的密文。
    • 获取密钥:从 Keychain 中获取解密所需的密钥。
    • 解密数据:使用密钥对密文进行解密。
    • 使用解密后的数据:将解密后的数据用于应用的业务逻辑。 以下是一个简单的示例,将加密后的数据写入文件并读取解密:
// 加密并写入文件
BOOL encryptAndWriteToFile(NSData *data, NSString *key, NSString *fileName) {
    NSData *encryptedData = AES256Encrypt(data, key);
    if (encryptedData) {
        NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:fileName];
        return [encryptedData writeToFile:filePath atomically:YES];
    }
    return NO;
}

// 从文件读取并解密
NSData *readAndDecryptFromFile(NSString *key, NSString *fileName) {
    NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:fileName];
    NSData *encryptedData = [NSData dataWithContentsOfFile:filePath];
    if (encryptedData) {
        return AES256Decrypt(encryptedData, key);
    }
    return nil;
}

3.3 网络传输中的安全存储

在网络传输过程中,数据同样面临被截获和篡改的风险。为了确保数据的安全传输,可以采用 SSL/TLS 协议进行加密传输。在 Objective - C 中,AFNetworking 等网络框架已经对 SSL/TLS 进行了很好的支持。

  1. 配置 AFNetworking 进行 SSL/TLS 验证 首先,需要获取服务器的证书,可以通过浏览器导出服务器的证书文件(通常为.crt 格式),并将其添加到项目中。
#import "AFNetworking.h"

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server_cert" ofType:@"cer"];
NSData *serverCertData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.pinnedCertificates = @[serverCertData];
manager.securityPolicy = securityPolicy;

这样配置后,AFNetworking 在与服务器进行通信时,会验证服务器证书的有效性,只有证书验证通过才会进行数据传输,从而保证了网络传输过程中数据的安全性。

四、安全漏洞与防范

4.1 常见的安全漏洞

  1. 缓冲区溢出 当程序向缓冲区写入的数据超过了缓冲区的容量时,就会发生缓冲区溢出。这可能导致程序崩溃,甚至被攻击者利用来执行恶意代码。在 Objective - C 中,特别是在处理 C 语言风格的数组和字符串时,需要注意边界检查。例如,在使用 strcpy 等函数时,如果目标缓冲区不够大,就可能发生缓冲区溢出。应尽量使用安全的字符串处理函数,如 strncpy,并确保正确设置了目标缓冲区的大小。
  2. SQL 注入 如果应用程序使用 SQL 数据库,并且在构建 SQL 查询语句时直接拼接用户输入的数据,就可能存在 SQL 注入漏洞。攻击者可以通过输入恶意的 SQL 语句,来获取数据库中的敏感信息或执行恶意的数据库操作。防范 SQL 注入的方法是使用参数化查询,在 Objective - C 中,使用 FMDB 等数据库框架时,可以使用 executeQuery: 等方法并传递参数,而不是直接拼接用户输入。
  3. 代码注入 攻击者可能通过各种方式将恶意代码注入到应用程序中执行。例如,通过动态链接库注入等方式。防范代码注入需要对应用程序的入口点进行严格的权限控制,并且避免加载不可信的动态链接库。

4.2 防范措施

  1. 输入验证 对所有用户输入的数据进行严格的验证,确保数据的格式和内容符合预期。例如,对于用户输入的电子邮件地址,使用正则表达式进行格式验证;对于数字输入,检查是否在合理的范围内。在 Objective - C 中,可以使用 NSPredicate 等类进行正则表达式匹配和数据验证。
  2. 代码审查 定期进行代码审查,检查代码中是否存在潜在的安全漏洞。特别是对涉及到用户输入、文件操作、网络通信等敏感部分的代码进行仔细审查。代码审查可以发现一些隐藏的逻辑漏洞和安全风险。
  3. 更新与补丁 及时更新应用程序所依赖的第三方库和框架,因为这些库和框架可能会发现安全漏洞并发布相应的补丁。同时,关注操作系统和设备的安全更新,及时进行安装,以确保整个运行环境的安全性。

五、性能优化与权衡

5.1 加密算法性能

不同的加密算法在性能上有很大差异。对称加密算法如 AES 速度较快,适合对大量数据进行加密,但密钥管理较为复杂。非对称加密算法如 RSA 速度较慢,适合对少量关键数据进行加密,如加密对称加密的密钥。在选择加密算法时,需要根据应用场景和数据量来权衡。如果应用需要处理大量的用户数据加密,应优先选择对称加密算法;如果是对一些敏感的配置信息或用户认证信息进行加密,非对称加密算法可能更合适。 在 Objective - C 中,对性能进行测试可以使用 CACurrentMediaTime() 等函数来记录加密和解密操作的开始和结束时间,从而计算出执行时间。例如:

#import <QuartzCore/QuartzCore.h>

CFTimeInterval start = CACurrentMediaTime();
NSData *encryptedData = AES256Encrypt(plainTextData, key);
CFTimeInterval end = CACurrentMediaTime();
NSLog(@"AES256 Encryption time: %f seconds", end - start);

5.2 存储性能

在安全存储方面,Keychain 存储虽然安全,但由于其内部机制和硬件交互等原因,读写操作相对较慢。如果应用需要频繁地读写一些敏感信息,可能需要在安全性和性能之间进行权衡。例如,可以将一些不太敏感但经常使用的信息存储在普通的加密文件中,而将最重要的敏感信息存储在 Keychain 中。 对于加密文件存储,加密和解密操作也会带来一定的性能开销。可以考虑在应用启动时预先加载一些常用的加密密钥,减少每次读写文件时生成密钥的开销。同时,合理选择加密算法和块大小也会影响存储性能。例如,较小的块大小可能会减少加密和解密的延迟,但可能会增加数据的总体大小。

5.3 网络传输性能

在网络传输中,使用 SSL/TLS 协议进行加密传输会增加一定的开销,因为加密和解密操作需要消耗 CPU 资源。为了优化网络传输性能,可以采取以下措施:

  1. 优化网络请求:减少不必要的网络请求,合并多个小的请求为一个大的请求,以减少 SSL/TLS 握手的次数。
  2. 使用连接池:在应用中使用连接池技术,复用已经建立的 SSL/TLS 连接,避免每次请求都重新进行握手。
  3. 选择合适的加密套件:不同的 SSL/TLS 加密套件在性能上有所差异,可以根据服务器和客户端的支持情况,选择性能较好的加密套件。在 Objective - C 中,使用网络框架时,可以通过配置相关参数来选择加密套件。

通过综合考虑这些性能因素,并根据应用的具体需求进行优化和权衡,可以在保证数据安全的前提下,提供较好的用户体验。