iOS开发基础90-iOS 加密:从 RSA/AES/MD5 到 HMAC 与钥匙串
iOS 加密算法完全指南:从 RSA/AES/MD5 到 HMAC 与钥匙串
数据安全是 iOS 开发中不可忽视的重要话题。本文从三大加密算法(非对称、对称、哈希)讲起,深入解析 RSA、AES、MD5/SHA 的原理与使用场景,重点讲解 iOS 开发的两条安全原则(网络传输不允许明文、本地存储不允许明文),补充加盐、HMAC、HMAC+时间戳防重放、钥匙串等实际方案,纠正常见的错误认知,最后给出 iOS 加密框架、Swift 版本对照和常见坑排查。
一、加密算法概述
一句话原理
加密算法分为三大类:非对称加密(公钥私钥配对,如 RSA)、对称加密(同一个密钥加解密,如 AES)、哈希散列(不可逆的摘要,如 MD5/SHA),三者各有适用场景,通常组合使用。
三大类对比
| 类型 | 代表算法 | 特点 | 典型用途 |
|---|---|---|---|
| 非对称加密 | RSA、ECC | 公钥加密私钥解密,性能差,安全 | HTTPS 握手、数字签名、密钥交换 |
| 对称加密 | AES、DES、3DES | 同一个密钥加解密,性能好 | 大量数据加密、HTTPS 数据传输 |
| 哈希散列 | MD5、SHA1、SHA256 | 不可逆,定长输出,用于摘要/指纹 | 密码存储、数据校验、数字签名 |
实际系统中通常是组合使用:HTTPS 用 RSA 交换对称密钥,用 AES 加密传输数据,用 SHA 做数据完整性校验和数字签名。
二、非对称加密(RSA)
一句话原理
非对称加密有一对密钥:公钥(可以公开)和私钥(必须保密)。公钥加密的数据只有私钥能解密,私钥签名的数据只有公钥能验证。
RSA 原理简述
RSA 的安全性基于大整数因式分解的困难性:
- 找两个很大的质数 p 和 q,相乘得到 n = p × q。
- 公钥是 (n, e),私钥是 (n, d)。
- 破解 RSA 需要从 n 反推出 p 和 q,当 n 足够大时(如 2048 位),目前的计算能力无法在合理时间内分解。
两种核心用法
1. 加密:公钥加密,私钥解密
发送方:用接收方的公钥加密数据 → 密文
接收方:用自己的私钥解密 → 明文
- 任何人都可以用公钥加密,但只有私钥持有者能解密。
- 用于:HTTPS 密钥交换、加密发送给特定人的数据。
2. 数字签名:私钥签名,公钥验签
发送方:用自己的私钥对数据摘要签名 → 签名
接收方:用发送方的公钥验证签名 → 确认数据来自发送方且未被篡改
- 只有私钥持有者能签名,任何人都可以用公钥验证。
- 用于:数字证书、App 签名、API 接口签名、防篡改。
常见混淆:"私钥加密,公钥解密"严格来说叫数字签名,不是普通的加密。因为私钥只有一个人有,用私钥加密的数据所有人都能用公钥解密,没有保密性,但能证明数据来自私钥持有者。
RSA 的使用场景
| 场景 | 说明 |
|---|---|
| HTTPS 握手 | 客户端用服务器公钥加密预主密钥,只有服务器私钥能解密 |
| 数字签名 | App 签名、证书验证、JWT 签名 |
| API 接口签名 | 服务端用私钥签名,客户端用公钥验签,防篡改防伪造 |
| 密钥交换 | 安全地交换对称加密的密钥 |
注意事项
- RSA 不适合加密大数据:性能差(比 AES 慢几百倍),通常只用来加密对称密钥或数据摘要。
- 密钥长度:推荐 2048 位及以上,1024 位已被认为不安全。
- 填充方式:必须用安全的填充方式(OAEP),不要用原始 RSA 或 PKCS1_v1.5(有攻击风险)。
三、对称加密(DES / 3DES / AES)
一句话原理
对称加密用同一个密钥进行加密和解密,性能好,适合加密大量数据,但密钥的安全分发是难点。
1. DES(已淘汰)
- 56 位密钥,太短,1999 年就被暴力破解。
- 不要在新项目中使用。
2. 3DES(逐渐淘汰)
- DES 的改进版,对数据进行三次 DES 加密(加密-解密-加密)。
- 密钥长度 112 位或 168 位,安全性比 DES 高,但性能差。
- NIST 已在 2023 年废弃 3DES,新项目不推荐使用。
3. AES(当前标准,推荐)
- 高级加密标准(Advanced Encryption Standard),美国国家安全局(NSA)用于加密绝密信息。
- 密钥长度:128 位、192 位、256 位(AES-256 最安全)。
- iOS 系统内部的加密、钥匙串、文件保护都用 AES。
- AES 算法本身目前没有被破解,但弱密钥、错误的加密模式会导致安全问题。
加密模式(重要)
AES 有多种加密模式,选择错误的模式会导致安全漏洞:
| 模式 | 特点 | 安全性 | 推荐 |
|---|---|---|---|
| ECB | 相同明文块加密后相同,暴露数据模式 | ❌ 极不安全(可被图案攻击) | 不推荐 |
| CBC | 每个明文块与前一个密文块异或,需要 IV | ✅ 安全 | 推荐 |
| CTR | 计数器模式,流加密,需要 IV/nonce | ✅ 安全 | 推荐 |
| GCM | 计数器模式 + 认证,同时保证机密性和完整性 | ✅✅ 最安全(带认证) | 最推荐 |
ECB 模式是最常见的坑:相同的明文块会产生相同的密文块,导致加密后的图片还能看出轮廓。必须用 CBC、CTR 或 GCM 模式。
初始化向量(IV)
- CBC、CTR、GCM 模式都需要一个初始化向量(IV / Nonce)。
- IV 不需要保密,但必须是随机的,且每次加密都不同。
- IV 通常和密文一起存储/传输。
- 不要用固定 IV,否则相同明文会产生相同密文,降低安全性。
填充方式(Padding)
- 对称加密是按块加密的(AES 块大小 16 字节),数据长度不是块的整数倍时需要填充。
- 推荐用 PKCS7(PKCS5 是 PKCS7 的子集,块大小固定 8 字节)。
- 不要用零填充(Zero Padding),无法区分数据末尾的零和填充的零。
iOS 中的 AES 加密示例(CommonCrypto)
#import <CommonCrypto/CommonCrypto.h>
// AES 加密(CBC 模式 + PKCS7 填充)
- (NSData *)aesEncryptData:(NSData *)data key:(NSData *)key iv:(NSData *)iv {
NSUInteger dataLength = data.length;
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus status = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding, // CBC 模式(默认)
key.bytes, key.length,
iv.bytes, // IV
data.bytes, dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (status == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer);
return nil;
}
注意:
kCCOptionPKCS7Padding单独使用时是 CBC 模式。如果加kCCOptionECBMode就是 ECB 模式(不推荐)。
四、哈希散列函数(MD5 / SHA)
一句话原理
哈希函数把任意长度的数据转换成固定长度的摘要(指纹),过程不可逆,相同输入永远得到相同输出,不同输入几乎不可能得到相同输出(抗碰撞)。
哈希函数的特点
| 特点 | 说明 |
|---|---|
| 不可逆 | 无法从摘要反推出原始数据 |
| 定长输出 | MD5 输出 32 位十六进制(128 位),SHA256 输出 64 位(256 位) |
| 确定性 | 相同输入永远得到相同输出 |
| 雪崩效应 | 输入改变一点点,输出完全不同 |
| 抗碰撞 | 很难找到两个不同输入产生相同输出 |
常见算法安全性对比
| 算法 | 输出长度 | 安全性 | 现状 |
|---|---|---|---|
| MD5 | 128 位(32字符) | ❌ 已被破解(碰撞攻击) | 不推荐用于安全场景 |
| SHA1 | 160 位(40字符) | ❌ 已被破解(SHAttered 碰撞攻击) | 不推荐用于安全场景 |
| SHA256 | 256 位(64字符) | ✅ 安全 | 推荐 |
| SHA512 | 512 位(128字符) | ✅✅ 更安全 | 推荐(高安全需求) |
重要纠正:有一种常见说法是"MD5 不能反算所以安全",这是对的,但 MD5 已经被证明有碰撞攻击(可以构造两个不同文件产生相同 MD5),所以 MD5 不能用于安全场景(如密码存储、数字签名)。MD5 还可以用于非安全场景(如文件去重、简单校验),但安全相关的必须用 SHA256 及以上。
哈希的用途
| 用途 | 说明 |
|---|---|
| 密码存储 | 存储密码的哈希而不是明文(但需要加盐,见下文) |
| 数据完整性校验 | 下载文件后校验 MD5/SHA,确认文件未被篡改 |
| 数字签名 | 对数据摘要签名,而不是对原始数据签名 |
| 去重 | 文件/图片的 MD5 作为唯一标识去重 |
| API 签名 | 对请求参数排序后拼接,计算哈希作为签名 |
密码存储的正确姿势:慢哈希
普通哈希(MD5/SHA)计算速度太快,攻击者可以用彩虹表或暴力破解每秒计算上亿次。密码存储必须用慢哈希算法:
| 算法 | 特点 | 推荐 |
|---|---|---|
| bcrypt | 自带盐,可配置计算成本,专门为密码设计 | ✅ 推荐 |
| scrypt | 内存困难型,抗 ASIC 攻击 | ✅ 推荐 |
| Argon2 | 密码哈希竞赛冠军,最安全 | ✅✅ 最推荐 |
| PBKDF2 | 标准算法,可配置迭代次数 | 可用 |
iOS 中可以用
CommonCrypto的CCKeyDerivationPBKDF实现 PBKDF2,或用第三方库(如BCrypt)。
五、iOS 安全原则一:网络传输不允许明文
一句话原理
用户隐私数据(密码、token、身份证号等)绝对不能以明文形式在网络上传输,必须加密。下面介绍加盐、HMAC、HMAC+时间戳等方案,并补充更安全的做法。
方案一:加盐(Salt)
原理
在用户密码后面拼接一串随机字符串(盐),再计算哈希:
存储:hash(密码 + 盐) → 数据库存储哈希和盐
登录:hash(输入的密码 + 盐) → 和数据库存储的哈希对比
静态盐 vs 动态盐
| 类型 | 说明 | 安全性 |
|---|---|---|
| 静态盐(常见错误做法) | 所有用户用同一个盐,写死在客户端和服务端 | ⚠️ 较低,盐泄露后所有用户都受影响 |
| 动态盐(推荐) | 每个用户注册时随机生成一个盐,和哈希一起存在数据库 | ✅ 高,一个用户的盐泄露不影响其他用户 |
常见错误做法:把盐写死在客户端代码里,所有用户用同一个盐。这样一旦盐被逆向出来,所有用户的密码都可以被彩虹表攻击。正确的做法是动态盐:每个用户注册时随机生成盐,和密码哈希一起存在服务端,客户端登录时先从服务端获取该用户的盐,再计算哈希。
加盐的注意事项
- 盐必须足够长(至少 16 字节)、随机(用密码学安全的随机数生成器)。
- 每个用户的盐不同。
- 盐不需要保密,和哈希一起存储即可。
- 加盐能有效防御彩虹表攻击,但不能防御暴力破解(所以还要用慢哈希)。
方案二:HMAC(哈希消息认证码)
一句话原理
HMAC 是用一个密钥和哈希算法生成消息认证码,既能验证数据完整性,又能验证数据来源(只有持有密钥的人才能生成正确的 HMAC)。
原理
HMAC(密钥, 消息) = 哈希((密钥 XOR opad) + 哈希((密钥 XOR ipad) + 消息))
常用:HMAC-MD5、HMAC-SHA1、HMAC-SHA256(推荐)。
HMAC 登录方案
一种常见的 HMAC 登录方案:
- 注册时:服务端给每个用户生成一个随机密钥 KEY,客户端和服务端都保存。
- 登录时:客户端计算
HMAC(KEY, 密码)发给服务端,服务端用保存的值对比。
这个方案的好处:
- 网络上传输的是 HMAC 结果,不是密码明文。
- 即使被截获,攻击者也无法反推出密码。
- 每个用户的 KEY 不同,一个用户的 KEY 泄露不影响其他用户。
隐患:
- 攻击者截获 HMAC 结果后,可以重放攻击(直接用这个密文登录)。
- 所以需要加时间戳(见下文)。
方案三:HMAC + 时间戳(防重放攻击)
原理
在 HMAC 结果中加入时间戳,使每次登录的密文都不同,且有有效期:
客户端:HMAC(KEY, 密码 + 时间戳(精确到分)) → 发送给服务端
服务端:用当前时间和前一分钟分别计算 HMAC,和客户端发来的对比
这样:
- 密文每分钟变化一次。
- 攻击者截获的密文只有 1~2 分钟有效期。
- 有效防御重放攻击。
这是一种很实用的防重放思路。实际项目中更常用的是 nonce(随机数)+ 时间戳 的方式,服务端记录已使用的 nonce,防止重放。
更安全的方案:HTTPS + 慢哈希
加盐和 HMAC 方案都是在应用层做加密,但最基础也是最重要的是传输层用 HTTPS:
| 层级 | 方案 | 说明 |
|---|---|---|
| 传输层 | HTTPS(TLS) | 加密整个传输通道,防窃听、防篡改、防中间人 |
| 应用层 | 密码哈希(bcrypt/Argon2) | 服务端存储密码的哈希,不存明文 |
| 应用层 | Token(JWT/Session) | 登录后用 token 认证,不传密码 |
最佳实践:
- 所有接口必须用 HTTPS。
- 客户端发送密码明文(HTTPS 通道已加密),服务端用 bcrypt/Argon2 哈希后存储。
- 登录成功后返回 token,后续请求用 token 认证。
- 高安全需求的接口可以再加一层应用层签名(HMAC + 时间戳 + nonce)。
六、iOS 安全原则二:本地存储不允许明文
一句话原理
用户隐私数据(密码、token、身份证号等)绝对不能以明文形式保存在本地(NSUserDefaults、plist、文件等),必须加密存储。iOS 最推荐的方案是钥匙串(Keychain)。
方案一:钥匙串(Keychain,推荐)
什么是钥匙串
钥匙串是 iOS 系统级的安全存储机制,用 AES 加密存储敏感信息,数据保存在系统的沙盒之外(独立的数据库),由系统统一管理。
常见错误说法:有一种说法是"钥匙串从 iOS 7.0.3 版本才开放给开发者访问",这是不准确的。钥匙串从 iOS 2.0 就存在并开放给开发者使用,iOS 7.0.3 是修复了钥匙串的某个安全漏洞。
钥匙串的特点
| 特点 | 说明 |
|---|---|
| 系统级加密 | 用 AES 加密,硬件级安全(Secure Enclave) |
| App 删除后数据不丢失 | 卸载 App 后钥匙串数据仍然保留,重装 App 还能读取 |
| 跨 App 共享 | 通过 App Group 可以在同一开发者的多个 App 间共享钥匙串数据 |
| iCloud 同步 | 开启 Keychain Sync 后可以在多设备间同步 |
| 只能存小数据 | 适合存密码、token、密钥、证书等小数据,不适合存大文件 |
| C 语言 API | 原生 API 是 Security.framework 的 C 接口,繁琐难用 |
钥匙串能存什么
- 密码(用户密码、Wi-Fi 密码)
- Token(登录 token、refresh token)
- 加密密钥(对称密钥、私钥)
- 证书
- 信用卡信息(高安全 App)
SSKeyChain / SAMKeychain 用法
SSKeyChain(SAMKeychain 是其 fork)是对钥匙串 C API 的轻量封装,只有 5 个方法:
#import <SSKeychain/SSKeychain.h>
// 存储密码
[SSKeychain setPassword:@"用户密码123"
forService:@"com.example.app" // 通常用 Bundle ID
account:@"user@example.com"]; // 账号
// 读取密码
NSString *password = [SSKeychain passwordForService:@"com.example.app"
account:@"user@example.com"];
// 删除密码
[SSKeychain deletePasswordForService:@"com.example.app"
account:@"user@example.com"];
// 获取所有账号
NSArray *accounts = [SSKeychain accountsForService:@"com.example.app"];
// 获取所有服务
NSArray *allAccounts = [SSKeychain allAccounts];
三个参数的含义
| 参数 | 说明 | 示例 |
|---|---|---|
| service | 服务名,通常用 App 的 Bundle ID | @"com.example.app" |
| account | 账号标识,区分不同用户 | @"user@example.com" 或用户 ID |
| password | 要存储的密码/数据 | @"password123" 或 token 字符串 |
注意:钥匙串存的是字符串,如果要存 NSData 或自定义对象,需要先序列化成字符串(如 Base64 编码)。
Xcode 配置
使用钥匙串需要打开 Keychain Sharing 开关(即使不需要共享,也建议打开,否则可能出现奇怪的问题):
- 项目设置 → Signing & Capabilities → + Capability → Keychain Sharing。
- 添加一个 Keychain Group(通常用 Bundle ID)。
方案二:NSUserDefaults + 加密
如果数据量小且不需要系统级安全,可以用 AES 加密后存 NSUserDefaults:
// 加密 token 后存储
NSData *encryptedToken = [self aesEncryptData:[token dataUsingEncoding:NSUTF8StringEncoding]
key:encryptionKey
iv:iv];
[[NSUserDefaults standardUserDefaults] setObject:encryptedToken forKey:@"token"];
但这种方式的安全性不如钥匙串(加密密钥也存在本地,可能被逆向获取),敏感数据优先用钥匙串。
方案三:文件加密
大文件(如数据库、下载的敏感文件)可以用 AES 加密后存储,或用 iOS 的数据保护(Data Protection):
// 写文件时设置数据保护选项
NSData *data = ...;
[data writeToFile:path options:NSDataWritingFileProtectionComplete error:nil];
NSDataWritingFileProtectionComplete 会用设备密钥加密文件,设备锁定时无法读取。
绝对不要做的事
- ❌ 不要用 NSUserDefaults 存密码、token 等敏感信息(明文存储,容易被导出)。
- ❌ 不要用 plist 文件存敏感信息。
- ❌ 不要把加密密钥写死在代码里(容易被逆向获取)。
- ❌ 不要自己发明加密算法(用标准的 AES/RSA/SHA)。
七、MD5 的其他用法
1. 搜索(关键字无序匹配)
场景
搜索"深圳 福田 喜年支行"和搜索"福田 喜年支行 深圳",结果应该一样(关键字顺序不影响搜索结果)。
原理
常见错误认知:有一种说法是"将三个关键字转为 MD5 字符串后按位相加结果一样",这个说法不准确。正确的原理是:
- 将搜索关键字按某种规则排序(如按拼音首字母、按字典序)。
- 排序后拼接成一个字符串。
- 对拼接后的字符串计算 MD5。
这样不管用户输入的顺序如何,排序后的字符串都一样,MD5 也就一样。
- (NSString *)searchKeyForKeywords:(NSArray *)keywords {
// 1. 排序(按字典序)
NSArray *sorted = [keywords sortedArrayUsingSelector:@selector(compare:)];
// 2. 拼接
NSString *combined = [sorted componentsJoinedByString:@""];
// 3. MD5
return [self md5:combined];
}
不是"按位相加",而是"排序后拼接再哈希"。
2. 版权/去重(文件指纹)
原理
文件的 MD5 是文件内容的指纹:
- 相同内容的文件(即使文件名不同、后缀不同),MD5 相同。
- 修改过的文件(PS 过的图片、编辑过的文档),MD5 不同。
- 文件后缀名只影响操作系统用什么程序打开,不影响文件内容,所以不影响 MD5。
用途
| 用途 | 说明 |
|---|---|
| 图片/文件去重 | 用 MD5 作为唯一标识,避免重复存储 |
| 版权验证 | 对比 MD5 判断是否为原图/原文件 |
| 下载校验 | 下载后对比 MD5,确认文件完整未被篡改 |
| 秒传 | 上传前计算 MD5,如果服务器已有相同 MD5 的文件,直接秒传 |
注意
- MD5 有碰撞攻击风险,如果是版权保护/安全校验,推荐用 SHA256。
- 如果只是去重/秒传(非安全场景),MD5 仍然够用。
3. 数据完整性校验
下载文件、传输数据后,计算 MD5/SHA 和发送方提供的校验值对比,确认数据在传输过程中没有损坏或被篡改:
// 下载文件后校验
NSData *downloadedData = [NSData dataWithContentsOfURL:fileURL];
NSString *fileMD5 = [self md5ForData:downloadedData];
if ([fileMD5 isEqualToString:expectedMD5]) {
NSLog(@"文件完整");
} else {
NSLog(@"文件损坏,重新下载");
}
八、iOS 加密框架与工具
1. CommonCrypto(系统底层,C 语言 API)
- iOS 系统自带的加密框架,C 语言接口。
- 支持:AES、DES、3DES、SHA、MD5、HMAC、PBKDF2。
- 不需要额外引入库,
#import <CommonCrypto/CommonCrypto.h>即可。 - 性能好,但 API 繁琐,容易出错。
2. CryptoKit(iOS 13+,Swift 原生,推荐)
- iOS 13 引入的 Swift 原生加密框架,API 简洁安全。
- 支持:SHA256/SHA384/SHA512、HMAC、AES-GCM、ChaCha20-Poly1305、Curve25519 等。
- 自动管理内存,类型安全,不容易出错。
- Swift 项目推荐用 CryptoKit。
3. Security.framework
- 系统级安全框架,C 语言接口。
- 支持:钥匙串、证书、RSA/ECC 密钥生成、数字签名、TLS。
- 钥匙串操作的底层 API。
4. 第三方库
| 库 | 语言 | 说明 |
|---|---|---|
| CryptoSwift | Swift | 纯 Swift 实现的加密库,支持多种算法 |
| RNCryptor | OC/Swift | 封装好的 AES 加密库,处理了 IV、盐、密钥派生等细节 |
| SSKeychain/SAMKeychain | OC | 钥匙串轻量封装 |
| KeychainAccess | Swift | 钥匙串 Swift 封装,API 优雅 |
| BCrypt | OC/Swift | bcrypt 密码哈希 |
九、Swift 版本对照(CryptoKit)
import CryptoKit
// MARK: - SHA256 哈希
func sha256(_ string: String) -> String {
let data = Data(string.utf8)
let hash = SHA256.hash(data: data)
return hash.compactMap { String(format: "%02x", $0) }.joined()
}
// MARK: - HMAC
func hmacSHA256(message: String, key: String) -> String {
let keyData = SymmetricKey(data: Data(key.utf8))
let messageData = Data(message.utf8)
let hmac = HMAC<SHA256>.authenticationCode(for: messageData, using: keyData)
return Data(hmac).compactMap { String(format: "%02x", $0) }.joined()
}
// MARK: - AES-GCM 加密
func aesEncrypt(message: String, key: SymmetricKey) throws -> Data {
let data = Data(message.utf8)
let sealedBox = try AES.GCM.seal(data, using: key)
// combined 包含 nonce + 密文 + 标签
return sealedBox.combined!
}
func aesDecrypt(combinedData: Data, key: SymmetricKey) throws -> String {
let sealedBox = try AES.GCM.SealedBox(combined: combinedData)
let decryptedData = try AES.GCM.open(sealedBox, using: key)
return String(data: decryptedData, encoding: .utf8)!
}
// MARK: - 钥匙串(KeychainAccess 库)
import KeychainAccess
let keychain = Keychain(service: "com.example.app")
// 存储
try keychain.set("password123", key: "user@example.com")
// 读取
let password = try keychain.get("user@example.com")
// 删除
try keychain.remove("user@example.com")
十、常见问题与坑
Q1:MD5 还能用吗?
答案:分场景:
- ❌ 安全场景(密码存储、数字签名、防篡改):不要用 MD5,用 SHA256 及以上。MD5 已被证明有碰撞攻击。
- ✅ 非安全场景(文件去重、秒传、简单校验):可以用 MD5,性能好,够用。
Q2:密码应该怎么存?
答案:
- 客户端通过 HTTPS 发送密码明文(HTTPS 通道已加密)。
- 服务端用慢哈希算法(bcrypt、scrypt、Argon2、PBKDF2)加盐哈希后存储。
- 绝对不要存明文,不要用 MD5/SHA 直接哈希(太快,容易暴力破解)。
- 客户端本地如果需要记住密码,用钥匙串存储。
Q3:AES 加密用什么模式?
答案:
- 推荐 GCM(带认证,最安全)或 CBC(需要 IV + HMAC 认证)。
- 绝对不要用 ECB 模式(相同明文产生相同密文,可被图案攻击)。
- IV 必须随机且每次加密不同,不要用固定 IV。
- 填充用 PKCS7。
Q4:钥匙串数据会被备份吗?
答案:默认情况下,iTunes/iCloud 备份会包含钥匙串数据,但钥匙串数据是加密的,只有在同一设备上恢复才能解密。如果开启了 iCloud Keychain,钥匙串数据会加密同步到 iCloud。
如果不希望钥匙串数据被备份,可以设置 kSecAttrAccessible 为 kSecAttrAccessibleThisDeviceOnly。
Q5:HTTPS 还需要应用层加密吗?
答案:看安全需求:
- 普通 App:HTTPS 足够,不需要额外应用层加密。
- 高安全需求(金融、医疗、企业):可以在 HTTPS 基础上再加应用层加密(如请求参数加密、响应加密、HMAC 签名),防御客户端被逆向、SSL Pinning 被绕过等风险。
- 但应用层加密不能替代 HTTPS,HTTPS 是基础。
Q6:RSA 能加密大数据吗?
答案:不推荐。RSA 加密性能差,且加密数据长度有限制(2048 位密钥最多加密 245 字节数据)。
正确做法:
- 生成一个随机的对称密钥(AES 密钥)。
- 用 AES 加密大数据。
- 用 RSA 公钥加密 AES 密钥。
- 把加密后的 AES 密钥和 AES 密文一起发送。
- 接收方用 RSA 私钥解密得到 AES 密钥,再用 AES 解密数据。
这就是 HTTPS 的基本思路(非对称加密交换对称密钥,对称加密传输数据)。
Q7:加盐后还需要用慢哈希吗?
答案:需要。加盐只能防御彩虹表攻击,不能防御暴力破解。如果用 MD5/SHA 加盐,攻击者拿到盐后仍然可以每秒计算上亿次哈希进行暴力破解。
正确做法:加盐 + 慢哈希(bcrypt/scrypt/Argon2/PBKDF2),慢哈希算法故意设计得计算很慢(可配置迭代次数/内存),让暴力破解的成本变得不可接受。
Q8:自己实现加密算法可以吗?
答案:绝对不要。加密算法的实现有很多细节(侧信道攻击、填充预言攻击、随机数生成等),自己实现几乎肯定会有安全漏洞。用系统库(CommonCrypto/CryptoKit)或经过审计的成熟库。
密码学第一原则:不要自己造密码学轮子(Don't roll your own crypto)。
Q9:HTTPS 抓包能看到明文吗?
答案:
- 正常情况下:HTTPS 数据是加密的,抓包工具只能看到密文。
- 但如果用户在设备上安装了抓包工具的根证书并信任(如 Charles),且 App 没有做 SSL Pinning,抓包工具可以做中间人攻击,看到明文。
- 防御:高安全 App 应该做 SSL Pinning(证书绑定),只信任指定的服务器证书,即使用户安装了恶意根证书也无法解密。
Q10:钥匙串存 token 和 NSUserDefaults 存 token 有什么区别?
| 对比 | 钥匙串 | NSUserDefaults |
|---|---|---|
| 存储方式 | AES 加密,系统级安全 | 明文 plist |
| App 删除后 | 数据保留 | 数据删除 |
| 安全性 | 高(硬件级加密) | 低(可被导出读取) |
| 适合存储 | 密码、token、密钥 | 非敏感的用户偏好设置 |
| 跨 App 共享 | 支持(App Group) | 不支持 |
token 虽然不是密码,但也是敏感信息(拿到 token 就能冒充用户),推荐存钥匙串。如果只是普通的非敏感设置(如主题、语言),用 NSUserDefaults。
十一、总结
- 三大加密算法:
- 非对称加密(RSA):公钥私钥配对,用于密钥交换和数字签名,不适合加密大数据。
- 对称加密(AES):同一密钥加解密,性能好,用于大量数据加密;用 GCM 或 CBC 模式,不要用 ECB;IV 必须随机。
- 哈希散列(MD5/SHA):不可逆摘要,用于密码存储、数据校验、数字签名;MD5/SHA1 已不安全,安全场景用 SHA256+;密码存储用慢哈希(bcrypt/Argon2)。
- 网络传输安全:
- 基础:必须用 HTTPS。
- 密码:服务端用慢哈希加盐存储,不传明文。
- 高安全:HMAC + 时间戳 + nonce 防重放防篡改。
- 本地存储安全:
- 敏感数据(密码、token、密钥)存钥匙串(系统级 AES 加密)。
- 不要用 NSUserDefaults/plist 存敏感信息。
- 大文件用数据保护(Data Protection)或 AES 加密。
- MD5 的非安全用途:文件去重、秒传、简单校验(安全校验用 SHA256)。
- iOS 加密框架:CommonCrypto(C 底层)、CryptoKit(iOS 13+ Swift 推荐)、Security.framework(钥匙串/证书)、第三方库(CryptoSwift、RNCryptor、SSKeychain)。
- 核心原则:不要自己造加密轮子,用标准算法和成熟库;HTTPS 是基础,应用层加密是补充;密码必须用慢哈希加盐。

浙公网安备 33010602011771号