iOS Swift中AES解密失败,如何用RNCryptor/CryptoSwift解密指定密文?
解决OpenSSL风格AES密文的Swift解密问题
你的解密失败核心原因是:给定的密文是OpenSSL标准的AES加密格式(Base64解码后以Salted__开头),而你之前的代码没有匹配这个格式的加密逻辑:
- RNCryptor默认使用PBKDF2推导密钥,和OpenSSL的
EVP_BytesToKey推导规则不兼容; - CryptoSwift代码错误使用了ECB模式,且没有处理salt和正确的密钥/IV推导,导致密钥长度不匹配报错。
下面分别给出两个库的可行解密方案:
方案1:使用CryptoSwift解密
这个方案更直接,因为我们可以完全复现OpenSSL的密钥推导和加密逻辑:
import CryptoSwift func decryptOpenSSLAES(encryptedBase64: String, password: String) throws -> String { // 1. 解码Base64密文 guard let encryptedData = Data(base64Encoded: encryptedBase64) else { throw NSError(domain: "DecryptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无效的Base64数据"]) } // 2. 解析OpenSSL格式:前8字节是"Salted__",接下来8字节是salt,剩余为加密内容 guard encryptedData.count >= 16 else { throw NSError(domain: "DecryptError", code: 2, userInfo: [NSLocalizedDescriptionKey: "无效的加密数据格式"]) } let salt = encryptedData.subdata(in: 8..<16) let ciphertext = encryptedData.subdata(in: 16..<encryptedData.count) // 3. 实现OpenSSL的EVP_BytesToKey密钥推导(MD5哈希,1次迭代) let passwordData = password.data(using: .utf8)! var derivedData = Data() var currentHash = Data() // 生成足够长度的密钥(32字节AES-256)+ IV(16字节) while derivedData.count < 32 + 16 { currentHash = Data(MD5.hash(data: currentHash + passwordData + salt)) derivedData.append(currentHash) } let key = derivedData.subdata(in: 0..<32).bytes let iv = derivedData.subdata(in: 32..<48).bytes // 4. AES-256-CBC模式解密,PKCS7填充 let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7) let decryptedBytes = try aes.decrypt(ciphertext.bytes) let decryptedData = Data(decryptedBytes) guard let plaintext = String(data: decryptedData, encoding: .utf8) else { throw NSError(domain: "DecryptError", code: 3, userInfo: [NSLocalizedDescriptionKey: "解密数据无法转成字符串"]) } return plaintext } // 调用示例 do { let ciphertext = "U2FsdGVkX187YbST7kZBjdhI6tGa2YQE5e3bS4WNhaXgi0iy//q5TOfR/q6Sc1Lx" let password = "Message" let plaintext = try decryptOpenSSLAES(encryptedBase64: ciphertext, password: password) print("解密结果:\(plaintext)") // 输出: my message is not ready } catch { print("解密失败:\(error)") }
为什么之前的CryptoSwift代码失败?
你之前的代码错误使用了ECB模式(密文实际是CBC模式加密),且直接将密码作为密钥使用——"Message"仅7字节,不符合AES要求的16/24/32字节密钥长度,因此会触发invalid key size错误。
方案2:使用RNCryptor解密
RNCryptor本身不原生支持OpenSSL格式,需要手动推导密钥后调用底层解密引擎:
import RNCryptor func decryptOpenSSLWithRNCryptor(encryptedBase64: String, password: String) throws -> String { // 1. 解码Base64并解析salt和密文 guard let encryptedData = Data(base64Encoded: encryptedBase64) else { throw NSError(domain: "DecryptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无效的Base64数据"]) } guard encryptedData.count >= 16 else { throw NSError(domain: "DecryptError", code: 2, userInfo: [NSLocalizedDescriptionKey: "无效的加密数据格式"]) } let salt = encryptedData.subdata(in: 8..<16) let ciphertext = encryptedData.subdata(in: 16..<encryptedData.count) // 2. 用OpenSSL规则推导密钥和IV let passwordData = password.data(using: .utf8)! var derivedData = Data() var currentHash = Data() while derivedData.count < 32 + 16 { currentHash = Data(MD5.hash(data: currentHash + passwordData + salt)) derivedData.append(currentHash) } let key = derivedData.subdata(in: 0..<32) let iv = derivedData.subdata(in: 32..<48) // 3. 使用RNCryptor底层引擎解密 let engine = RNCryptorEngine(key: key, iv: iv, operation: .decrypt, settings: RNCryptorAES256Settings()) var decryptedData = Data() var buffer = Data(count: 1024) var inputData = ciphertext while inputData.count > 0 { let bytesRead = engine.processData(inputData, into: &buffer) decryptedData.append(buffer.subdata(in: 0..<bytesRead)) inputData = inputData.subdata(in: bytesRead..<inputData.count) } // 处理最终块 let finalBytes = engine.finalData() decryptedData.append(finalBytes) guard let plaintext = String(data: decryptedData, encoding: .utf8) else { throw NSError(domain: "DecryptError", code: 3, userInfo: [NSLocalizedDescriptionKey: "解密数据无法转成字符串"]) } return plaintext } // 调用示例 do { let ciphertext = "U2FsdGVkX187YbST7kZBjdhI6tGa2YQE5e3bS4WNhaXgi0iy//q5TOfR/q6Sc1Lx" let password = "Message" let plaintext = try decryptOpenSSLWithRNCryptor(encryptedBase64: ciphertext, password: password) print("解密结果:\(plaintext)") } catch { print("解密失败:\(error)") }
为什么之前的RNCryptor代码失败?
RNCryptor的decrypt(data:withPassword:)方法是针对自身定义的加密格式(包含版本号、salt、HMAC等字段),而你的密文是OpenSSL格式,两者结构和密钥推导逻辑完全不同,因此直接调用会解密失败。
内容的提问来源于stack exchange,提问作者Abhishek Thapliyal
相关产品推荐
相关产品推荐

