iOS Swift中AES 128位密文解密问题及崩溃求助
解决Swift中AES-128解密崩溃及跨平台解密问题
首先,你遇到的Unexpectedly found nil while unwrapping an Optional value崩溃,根源是代码里滥用了强制解包!——如果密文是Base64编码,直接用.utf8转换肯定会失败,或者解密后decrypted为nil时,强制解包都会触发崩溃。这是第一个要紧急修复的点。
另外,你提到其他库只能解密同代码加密的内容,核心原因是AES解密必须和加密时使用完全一致的参数,这些参数包括:
- 密钥派生方式(比如PBKDF2、HKDF,以及对应的哈希算法、迭代次数)
- AES加密模式(CBC、GCM、ECB等,ECB不推荐但旧系统常用)
- 填充规则(PKCS7、ZeroPadding等)
- 初始化向量(IV)的长度和来源(是否和密文拼接传输,或是固定值)
- 密文的编码格式(Base64或十六进制)
你的密文NilZ9gpu1M3KUnEyN9scllyV3aN7NSSPJXwbOy7qTOk=是Base64编码的,第一步应该先解码成Data,而不是直接转UTF8!
推荐方案:使用Apple原生CryptoKit框架
CryptoKit是Apple官方提供的加密库,稳定可靠,支持跨场景解密。下面是一个基于常见参数的示例(假设加密时用了PBKDF2派生密钥、CBC模式、PKCS7填充,且IV是密文的前16字节):
import CryptoKit func decryptAES128(ciphertextBase64: String, password: String) throws -> String { // 1. 解码Base64密文为Data guard let ciphertextData = Data(base64Encoded: ciphertextBase64) else { throw NSError(domain: "DecryptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无效的Base64密文"]) } // 2. 分离IV和实际密文(AES-128的IV通常为16字节,需和加密端一致) guard ciphertextData.count >= 16 else { throw NSError(domain: "DecryptError", code: 2, userInfo: [NSLocalizedDescriptionKey: "密文长度不足"]) } let iv = ciphertextData.prefix(16) let actualCiphertext = ciphertextData.dropFirst(16) // 3. 从密码派生密钥(盐、迭代次数必须和加密端完全一致!) // 注意:这里的盐需要替换成加密时使用的真实值,不能随便填写 guard let salt = "your-encryption-salt".data(using: .utf8) else { throw NSError(domain: "DecryptError", code: 3, userInfo: [NSLocalizedDescriptionKey: "盐转换失败"]) } guard let passwordData = password.data(using: .utf8) else { throw NSError(domain: "DecryptError", code: 4, userInfo: [NSLocalizedDescriptionKey: "密码转换失败"]) } let derivedKey = try PKCS5.PBKDF2SHA1( password: passwordData, salt: salt, iterations: 10000, // 替换为加密时的迭代次数 keyLength: 16 // AES-128对应16字节密钥 ).calculate() // 4. 执行AES-CBC解密 let aesKey = SymmetricKey(data: derivedKey) let decryptedData = try AES.CBC.decrypt( actualCiphertext, using: aesKey, iv: iv, padding: .pkcs7 ) // 5. 转换为明文字符串 guard let plaintext = String(data: decryptedData, encoding: .utf8) else { throw NSError(domain: "DecryptError", code: 5, userInfo: [NSLocalizedDescriptionKey: "解密数据转字符串失败"]) } return plaintext } // 使用示例 do { let password = "Q@*Amn!" let encrypted = "NilZ9gpu1M3KUnEyN9scllyV3aN7NSSPJXwbOy7qTOk=" let plaintext = try decryptAES128(ciphertextBase64: encrypted, password: password) print("解密结果:\(plaintext)") } catch { print("解密失败:\(error.localizedDescription)") }
关键注意事项
- 严格匹配加密参数:上面的示例是基于通用配置,你必须确认加密端的具体参数(比如盐、迭代次数、AES模式、IV的位置),参数不匹配的话解密必然失败。
- 杜绝强制解包:所有可选值都用
guard let或if let处理,彻底避免nil解包崩溃。 - 如果是GCM模式:CryptoKit同样支持AES-GCM,这种模式会附带认证标签,需要把标签、IV、密文一起处理,代码逻辑会略有调整。
如果不确定加密参数,可以尝试询问提供密文的一方,或者用在线AES解密工具(输入密码和密文,尝试不同参数组合)验证,再将对应参数移植到Swift代码中。
内容的提问来源于stack exchange,提问作者Ashique
相关产品推荐
相关产品推荐

