2、数据加密基础:对称加密(AES)、非对称加密(RSA)、哈希算法(SHA)、Base64编码

好,咱们直接进入正题。数据加密这块,是iOS安全防护的基石。你想想看,如果连数据都是明文裸奔的,那后面的反逆向工程做得再好,也等于把家底直接亮给黑客看。我个人习惯把加密分为四大类:对称加密、非对称加密、哈希算法,还有Base64编码。别小看Base64,它虽然不算严格意义上的加密,但在iOS开发里几乎天天用。

2.1 对称加密:AES

对称加密,说白了就是加密和解密用同一把钥匙。就像你家的门锁,用一把钥匙锁上,再用同一把钥匙打开。在iOS里,最常用的对称加密算法就是AES。

AES的核心参数

  • 密钥长度:128位、192位、256位。我个人建议直接用AES-256,安全强度够高,性能损失其实可以忽略不计。
  • 工作模式:ECB、CBC、CFB、OFB、CTR、GCM等。我在项目中遇到过用ECB模式踩坑的案例,后面细说。
  • 填充方式:PKCS7Padding、ZeroPadding等。iOS的CommonCrypto默认用PKCS7Padding。

重要提醒:千万不要用ECB模式!ECB模式下,相同的明文块会加密成相同的密文块,这会给攻击者提供统计分析的机会。我曾经在一个金融类App里看到过ECB模式的使用,当时就建议他们立刻改成了CBC模式。

iOS中的AES实现

iOS原生提供了CommonCrypto库,用起来很方便。我一般这样写:

import CommonCrypto

func aesEncrypt(data: Data, key: Data, iv: Data) -> Data? {
    var outData = Data(count: data.count + kCCBlockSizeAES128)
    var outLength = 0
    
    let status = outData.withUnsafeMutableBytes { outBytes in
        data.withUnsafeBytes { dataBytes in
            key.withUnsafeBytes { keyBytes in
                iv.withUnsafeBytes { ivBytes in
                    CCCrypt(
                        CCOperation(kCCEncrypt),
                        CCAlgorithm(kCCAlgorithmAES),
                        CCOptions(kCCOptionPKCS7Padding),
                        keyBytes.baseAddress, key.count,
                        ivBytes.baseAddress,
                        dataBytes.baseAddress, data.count,
                        outBytes.baseAddress, outData.count,
                        &outLength
                    )
                }
            }
        }
    }
    
    guard status == kCCSuccess else { return nil }
    outData.count = outLength
    return outData
}

嗯,这里要注意:IV(初始化向量)必须是随机的,而且每次加密都要生成新的IV。我见过有人把IV写死在代码里,那跟没加密其实没啥区别。

我的小技巧:IV可以拼接在密文前面一起存储或传输。解密时先取出前16字节作为IV,剩下的就是密文。这样既安全又方便管理。

2.2 非对称加密:RSA

非对称加密就更有意思了。它有两把钥匙:公钥和私钥。公钥可以公开,私钥必须保密。用公钥加密的数据,只能用私钥解密;反过来,用私钥签名的数据,可以用公钥验证。

为什么会这样?说白了就是数学原理。RSA基于大整数分解难题,你想想看,两个大素数相乘很容易,但要把乘积分解回两个素数,那就难了去了。

RSA在iOS中的典型应用场景

  • 密钥交换:客户端用服务器的公钥加密一个对称密钥,服务器用私钥解密。这样后续通信就用对称加密,效率高。
  • 数字签名:用私钥对数据签名,公钥验证签名是否有效。我经常用它来验证App更新的完整性。

iOS中的RSA实现

iOS的Security框架提供了RSA支持。我习惯用SecKeyRef来操作:

import Security

func rsaEncrypt(data: Data, publicKey: SecKey) -> Data? {
    let blockSize = SecKeyGetBlockSize(publicKey)
    var outData = Data(count: blockSize)
    var outLength = blockSize
    
    let status = outData.withUnsafeMutableBytes { outBytes in
        data.withUnsafeBytes { dataBytes in
            SecKeyEncrypt(
                publicKey,
                .PKCS1,
                dataBytes.baseAddress!, data.count,
                outBytes.baseAddress!, &outLength
            )
        }
    }
    
    guard status == errSecSuccess else { return nil }
    outData.count = outLength
    return outData
}

避坑指南:我曾经在项目中遇到过一个坑——RSA加密的数据长度有限制。对于2048位的RSA密钥,一次最多只能加密245字节(因为要留出填充空间)。如果数据太长,需要分段加密,或者先用对称加密加密数据,再用RSA加密对称密钥。

2.3 哈希算法:SHA

哈希算法跟前面两种加密完全不同。它是单向的,不可逆的。你输入任意长度的数据,它输出固定长度的哈希值。而且,哪怕输入只改了一个比特,输出的哈希值也会天差地别。

常见的SHA系列

算法 输出长度 安全性
SHA-1 160位 已不推荐(有碰撞攻击)
SHA-256 256位 安全(推荐)
SHA-384 384位 安全
SHA-512 512位 安全

我个人习惯用SHA-256,它在安全性和性能之间取得了很好的平衡。SHA-1就别用了,Google早在2017年就成功实现了SHA-1碰撞攻击。

iOS中的SHA实现

import CommonCrypto

func sha256(data: Data) -> Data {
    var hash = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
    data.withUnsafeBytes { dataBytes in
        hash.withUnsafeMutableBytes { hashBytes in
            CC_SHA256(dataBytes.baseAddress, CC_LONG(data.count), hashBytes.bindMemory(to: UInt8.self).baseAddress)
        }
    }
    return hash
}

我的经验:哈希算法在iOS安全里最常见的用途是校验文件完整性。比如下载一个配置文件,先计算它的SHA-256哈希值,再跟服务器返回的哈希值对比。如果一致,说明文件没有被篡改过。

2.4 Base64编码

Base64不是加密算法,它是一种编码方式。它的作用是把二进制数据转换成可打印的ASCII字符。你想想看,如果加密后的密文里包含不可见字符,直接传输或存储就会出问题。Base64就是来解决这个问题的。

Base64的原理

  • 把3个字节(24位)分成4组,每组6位
  • 每组6位对应一个Base64字符(共64个字符:A-Z、a-z、0-9、+、/)
  • 如果最后不足3个字节,用=填充

iOS中的Base64实现

// 编码
let data = "Hello, iOS Security!".data(using: .utf8)!
let base64String = data.base64EncodedString()
print(base64String) // SGVsbG8sIGlPUyBTZWN1cml0eSE=

// 解码
if let decodedData = Data(base64Encoded: base64String),
   let decodedString = String(data: decodedData, encoding: .utf8) {
    print(decodedString) // Hello, iOS Security!
}

重要提醒:Base64只是编码,不是加密!我见过有人把Base64当成加密手段来保护敏感数据,这完全是在掩耳盗铃。Base64编码后的数据,任何人都可以轻松解码。它只适合用来传输二进制数据,不能提供任何安全性保障。

2.5 四种技术的配合使用

在实际的iOS项目中,这四种技术往往是配合使用的。我给大家画一个典型的场景:

  1. 客户端生成一个随机的AES密钥(对称加密)
  2. 用服务器的RSA公钥加密这个AES密钥(非对称加密)
  3. 用AES密钥加密实际的数据(对称加密,效率高)
  4. 对加密后的数据计算SHA-256哈希值(哈希算法,用于完整性校验)
  5. 把RSA加密后的AES密钥、AES加密后的数据、SHA-256哈希值,都用Base64编码(编码,方便传输)

你看,这样一套组合拳下来,既保证了数据的机密性(AES+RSA),又保证了完整性(SHA-256),还方便了传输(Base64)。

我的建议:在iOS开发中,尽量使用系统自带的加密库(CommonCrypto、Security),不要自己造轮子。系统库经过了严格的测试和审查,比自己写的实现要安全得多。而且,苹果每年都会在WWDC上更新安全相关的API,记得关注。

好了,数据加密基础就讲到这里。下一章我们会深入讨论iOS中的Keychain和Secure Enclave,这些是保护密钥安全的关键技术。到时候我会分享一些我在实际项目中遇到的坑和解决方案。