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

Swift中AES-256-CBC解密Base64加密字符串问题求助

AES-256-CBC解密失败问题排查

加密规则说明

  • 采用带PKCS7填充的AES-256-CBC加密算法
  • 密钥基于手机号的SHA-512哈希值生成:
    • 哈希值的前32字节作为AES密钥(符合AES-256的32字节长度要求)
    • 哈希值的后16字节作为IV(CBC模式要求16字节IV)
  • 加密结果经Base64编码后返回

问题现状

我尝试了两段Swift代码,代码可正常编译运行,但始终无法得到正确的解密结果,请求帮忙定位问题并修复。

第一段代码

// 更新后的版本,仍无法正确解密
import UIKit
import CryptoKit
import Security
import CommonCrypto

class ViewController: UIViewController {

    let encryptedString = "myEncryptedAndEncodedString"
    let phoneNumber = "myPhoneNumber"

    override func viewDidLoad() {
        super.viewDidLoad()

        var sha512HashFromPhoneNumber = ""

        // 1. 生成手机号的SHA-512哈希
        if let tempSha512Hash = sha512(phoneNumber) {
            print("✅ SHA-512 hash: \(tempSha512Hash)")
            sha512HashFromPhoneNumber = tempSha512Hash
        } else {
            print("❌ 计算哈希失败")
        }

        var key = Data()
        var iv = Data()

        // 2. 提取密钥和IV
        if let (tempKey, tempIv) = extractKeyAndIV(from: sha512HashFromPhoneNumber) {
            key = tempKey
            iv = tempIv
        } else {
            print("❌ 提取密钥和IV失败")
        }

        print("✅ Key NSData: \(key as NSData)")
        print("✅ IV NSData: \(iv as NSData)")

        var decodedData = Data()
        // 3. Base64解码加密字符串
        if let tempDecodedData = decodeBase64(encryptedString) {
            decodedData = tempDecodedData
        } else {
            print("❌ Base64解码失败")
        }

        print("✅ 解码后Data: \(decodedData as NSData)")

        // 4. AES解密
        if let decryptedString = decryptAES256CBC(encryptedData: decodedData, key: key, iv: iv) {
            print("✅ 解密结果: \(decryptedString)")
        } else {
            print("❌ AES解密失败")
        }
    }


    // 生成SHA-512哈希(返回十六进制字符串)
    func sha512(_ string: String) -> String? {
        guard let data = string.data(using: .utf8) else { return nil }

        var hash = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
        data.withUnsafeBytes {
            _ = CC_SHA512($0.baseAddress, CC_LONG(data.count), &hash)
        }

        return hash.map { String(format: "%02x", $0) }.joined()
    }

    // 从哈希字符串提取密钥和IV
    func extractKeyAndIV(from hash: String) -> (key: Data, iv: Data)? {
        let keyString = String(hash.prefix(32))
        let ivString = String(hash.dropFirst(32).suffix(16))
        print("keyString \(keyString) 长度 \(keyString.count)")
        print("ivString  \(ivString) 长度 \(ivString.count)")
        // 十六进制字符串转Data
        guard let keyData = Data(hexString: keyString), let ivData = Data(hexString: ivString) else { return nil }
        return (key: keyData, iv: ivData)
    }

    // Base64解码
    func decodeBase64(_ base64String: String) -> Data? {
        return Data(base64Encoded: base64String)
    }

    // AES-256-CBC解密
    func decryptAES256CBC(encryptedData: Data, key: Data, iv: Data) -> String? {
        let bufferSize = encryptedData.count
        var buffer = Data(count: bufferSize)

        let options = CCOptions(kCCOptionPKCS7Padding)
        var numBytesDecrypted: size_t = 0
        // 执行解密操作
        let cryptStatus = key.withUnsafeBytes { keyBytes in
            iv.withUnsafeBytes { ivBytes in
                encryptedData.withUnsafeBytes { dataBytes in
                    buffer.withUnsafeMutableBytes { mutableBytes in
                        CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), options,
                                keyBytes.baseAddress, key.count,
                                ivBytes.baseAddress,
                                dataBytes.baseAddress, bufferSize,
                                mutableBytes.baseAddress, bufferSize,
                                &numBytesDecrypted)
                    }
                }
            }
        }

        if cryptStatus == kCCSuccess {
            buffer.count = numBytesDecrypted
            return String(data: buffer, encoding: .utf8)
        } else {
            return nil
        }
    }

}

// Data扩展:十六进制字符串转Data
extension Data {
    init?(hexString: String) {
        let length = hexString.count / 2
        var data = Data(capacity: length)
        for i in 0..<length {
            let j = hexString.index(hexString.startIndex, offsetBy: i*2)
            let k = hexString.index(j, offsetBy: 2)

            let bytes = hexString[j..<k]
            if var num = UInt8(bytes, radix: 16) {
                data.append(&num, count: 1)
            } else {
                return nil
            }
        }
        self = data
    }
}

第二段代码

import UIKit
import CryptoKit
import CommonCrypto

class ViewController: UIViewController {

    let encryptedString = "myencryptedSring"
    let phoneNumber = "mygivengNumber"
        

    override func viewDidLoad() {
        super.viewDidLoad()

        if let decryptedString = decryptAES256CBC(encryptedData: encryptedString, key: phoneNumber) {
            print("原始邮箱: \(decryptedString)")
        } else {
            print("解码过程出错")
        }
    }

    func decryptAES256CBC(encryptedData: String, key: String) -> String? {
        guard let data = Data(base64Encoded: encryptedData) else { return nil }

        let keyData = SHA512.hash(data: key.data(using: .utf8)!)
        let keyBytes = Data(keyData.prefix(32)).bytes
        let ivBytes = Data(keyData.suffix(16)).bytes

        var numBytesDecrypted = 0
        var decryptedData = Data(count: data.count + kCCBlockSizeAES128)

        let status = decryptedData.withUnsafeMutableBytes { (decryptedBytes: UnsafeMutableRawBufferPointer) -> CCCryptorStatus in
            let decryptedDataCopy = Data(decryptedData)
            return decryptedDataCopy.withUnsafeBytes { (dataBytes: UnsafeRawBufferPointer) -> CCCryptorStatus in
                data.withUnsafeBytes { (dataBytes: UnsafeRawBufferPointer) -> CCCryptorStatus in
                    CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), keyBytes, kCCKeySizeAES256, ivBytes, dataBytes.baseAddress, data.count, decryptedBytes.baseAddress, decryptedDataCopy.count, &numBytesDecrypted)
                }
            }
        }

        if status == kCCSuccess {
            decryptedData = Data(decryptedData.prefix(numBytesDecrypted))
            return String(data: decryptedData, encoding: .utf8)
        } else {
            return nil
        }
    }
}

extension Data {
    var bytes: [UInt8] {
        return [UInt8](self)
    }
}

问题分析与修复方案

第一段代码的核心问题

  1. 密钥和IV提取长度错误:SHA-512哈希是64字节,转成十六进制字符串后是128个字符。AES-256需要32字节密钥(对应64个十六进制字符),CBC的IV需要16字节(对应32个十六进制字符)。原代码中取hash.prefix(32)仅得到16字节数据,hash.dropFirst(32).suffix(16)仅得到8字节数据,完全不符合加密规则要求。
  2. 修复方式:修改extractKeyAndIV函数,取前64个字符作为密钥的十六进制字符串,取最后32个字符作为IV的十六进制字符串。

第二段代码的核心问题

  1. CCCrypt参数传递错误:闭包中重复使用dataBytes变量名,导致传入的加密数据指针被覆盖;多余的decryptedDataCopy拷贝会导致解密缓冲区指针无效,最终解密失败。
  2. 修复方式:简化闭包内的指针处理逻辑,直接使用原始数据的指针传递参数。

修正后的整合代码

以下是基于CryptoKit实现的简洁版正确代码:

import UIKit
import CryptoKit
import CommonCrypto

class ViewController: UIViewController {
    let encryptedString = "your_encrypted_base64_string"
    let phoneNumber = "your_phone_number"

    override func viewDidLoad() {
        super.viewDidLoad()
        
        guard let decryptedStr = decryptTargetString() else {
            print("解密失败")
            return
        }
        print("解密结果:\(decryptedStr)")
    }
    
    func decryptTargetString() -> String? {
        // 1. 生成手机号的SHA-512哈希
        guard let phoneData = phoneNumber.data(using: .utf8) else { return nil }
        let sha512Digest = SHA512.hash(data: phoneData)
        let hashData = Data(sha512Digest)
        
        // 2. 提取32字节密钥和16字节IV
        let keyData = hashData.prefix(32)
        let ivData = hashData.suffix(16)
        
        // 3. Base64解码加密字符串
        guard let encryptedData = Data(base64Encoded: encryptedString, options: .ignoreUnknownCharacters) else { return nil }
        
        // 4. AES-256-CBC解密
        return performAESDecryption(encryptedData: encryptedData, key: keyData, iv: ivData)
    }
    
    func performAESDecryption(encryptedData: Data, key: Data, iv: Data) -> String? {
        var decryptedData = Data(count: encryptedData.count + kCCBlockSizeAES128)
        var bytesDecrypted = 0
        
        let status = key.withUnsafeBytes { keyBytes in
            iv.withUnsafeBytes { ivBytes in
                encryptedData.withUnsafeBytes { encBytes in
                    decryptedData.withUnsafeMutableBytes { decBytes in
                        CCCrypt(
                            kCCDecrypt,
                            kCCAlgorithmAES,
                            kCCOptionPKCS7Padding,
                            keyBytes.baseAddress,
                            kCCKeySizeAES256,
                            ivBytes.baseAddress,
                            encBytes.baseAddress,
                            encryptedData.count,
                            decBytes.baseAddress,
                            decryptedData.count,
                            &bytesDecrypted
                        )
                    }
                }
            }
        }
        
        guard status == kCCSuccess else { return nil }
        decryptedData = decryptedData.prefix(bytesDecrypted)
        return String(data: decryptedData, encoding: .utf8)
    }
}

额外验证要点

  1. 确认手机号编码格式:服务器端需与客户端一致使用UTF-8编码生成哈希。
  2. Base64兼容性:若服务器返回带换行的Base64字符串,需添加.ignoreUnknownCharacters选项解码。
  3. 填充方式一致性:确保服务器端使用PKCS7填充,与代码中的kCCOptionPKCS7Padding匹配。

内容的提问来源于stack exchange,提问作者biggreentree

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 05:38:08