Swift(iOS/macOS)中的密码学:加密与哈希
在现代 iOS 和 macOS 开发中,数据安全是至关重要的方面。Swift 提供了强大的内置密码学工具,从底层的 CommonCrypto(C-API)到现代的 CryptoKit 框架(iOS 13+/macOS 10.15+)。在本文中,我们将介绍 Swift 开发者可用的主要加密和哈希方法。
1. 哈希(Hashing)
哈希是将数据单向转换为固定长度字符串的过程。它用于验证数据完整性和存储密码(加盐)。
使用 CryptoKit 的示例(SHA-256)
import CryptoKit
func hashSHA256(_ input: String) -> String { let inputData = Data(input.utf8) let hashed = SHA256.hash(data: inputData) return hashed.compactMap { String(format: "%02x", $0) }.joined()}
// 使用let hash = hashSHA256("Hello, Swift!")print("SHA-256: \\(hash)")// 输出:8e3a...(64 个字符)使用 CommonCrypto 的示例(MD5,已弃用)
注意:MD5 不推荐用于安全目的,但可用于完整性校验。
import CommonCrypto
func md5Hash(_ input: String) -> String { let data = Data(input.utf8) var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) } return digest.map { String(format: "%02x", $0) }.joined()}2. 对称加密(AES-GCM)
对称加密使用同一个密钥进行加密和解密。CryptoKit 提供了安全的 AES-GCM(伽罗瓦/计数器模式)实现。
使用 CryptoKit 的加密和解密示例
import CryptoKit
func encryptAESGCM(plaintext: String, key: SymmetricKey) throws -> Data { let plaintextData = Data(plaintext.utf8) let sealedBox = try AES.GCM.seal(plaintextData, using: key) return sealedBox.combined! // combined = nonce + ciphertext + tag}
func decryptAESGCM(ciphertext: Data, key: SymmetricKey) throws -> String { let sealedBox = try AES.GCM.SealedBox(combined: ciphertext) let decryptedData = try AES.GCM.open(sealedBox, using: key) return String(data: decryptedData, encoding: .utf8)!}
// 使用let key = SymmetricKey(size: .bits256) // 256 位密钥let encrypted = try encryptAESGCM(plaintext: "秘密消息", key: key)let decrypted = try decryptAESGCM(ciphertext: encrypted, key: key)print("解密结果:\\(decrypted)")3. 非对称加密(RSA)
非对称加密使用一对密钥:公钥(用于加密)和私钥(用于解密)。在 iOS/macOS 中,使用 Security.framework 和 SecKey 进行 RSA 操作。
RSA 密钥生成和加密示例
import Security
func generateRSAKeyPair() -> (privateKey: SecKey, publicKey: SecKey)? { let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits as String: 2048, kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false], kSecPublicKeyAttrs as String: [kSecAttrIsPermanent as String: false] ] guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, nil) else { return nil } let publicKey = SecKeyCopyPublicKey(privateKey)! return (privateKey, publicKey)}
func encryptRSA(plaintext: String, publicKey: SecKey) -> Data? { let plaintextData = Data(plaintext.utf8) var error: Unmanaged<CFError>? guard let ciphertext = SecKeyCreateEncryptedData( publicKey, .rsaEncryptionPKCS1, plaintextData as CFData,