You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 22:37:38