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) } }
问题分析与修复方案
第一段代码的核心问题
- 密钥和IV提取长度错误:SHA-512哈希是64字节,转成十六进制字符串后是128个字符。AES-256需要32字节密钥(对应64个十六进制字符),CBC的IV需要16字节(对应32个十六进制字符)。原代码中取
hash.prefix(32)仅得到16字节数据,hash.dropFirst(32).suffix(16)仅得到8字节数据,完全不符合加密规则要求。 - 修复方式:修改
extractKeyAndIV函数,取前64个字符作为密钥的十六进制字符串,取最后32个字符作为IV的十六进制字符串。
第二段代码的核心问题
- CCCrypt参数传递错误:闭包中重复使用
dataBytes变量名,导致传入的加密数据指针被覆盖;多余的decryptedDataCopy拷贝会导致解密缓冲区指针无效,最终解密失败。 - 修复方式:简化闭包内的指针处理逻辑,直接使用原始数据的指针传递参数。
修正后的整合代码
以下是基于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) } }
额外验证要点
- 确认手机号编码格式:服务器端需与客户端一致使用UTF-8编码生成哈希。
- Base64兼容性:若服务器返回带换行的Base64字符串,需添加
.ignoreUnknownCharacters选项解码。 - 填充方式一致性:确保服务器端使用PKCS7填充,与代码中的
kCCOptionPKCS7Padding匹配。
内容的提问来源于stack exchange,提问作者biggreentree
相关产品推荐
相关产品推荐

