Swift中AES CBC PKCS5Padding加密乱码问题求助(适配iOS13及以下)
解决iOS 13及以下AES-CBC-PKCS5Padding加密乱码问题
你的问题核心很明确:加密后的二进制数据不能直接通过UTF8解码成字符串——Java代码里最后是对加密字节做了Base64编码返回,而你的Swift代码错误地把加密后的原始二进制数据直接转成UTF8字符串,这必然会出现乱码,因为加密后的字节流不是合法的UTF8文本。
接下来我们一步步修正你的Swift实现,使其和Java代码逻辑完全对齐:
问题分析
先回顾你的Java代码关键步骤:
- 明文、密钥、IV都转成UTF8字节
- 使用
AES/CBC/PKCS5Padding模式加密 - 加密后的字节做Base64编码(
Base64.encodeToString(ciphertext, Base64.NO_WRAP))返回
而你的Swift代码:
- 加密逻辑本身是正确的(用了CommonCrypto的
CCCrypt,指定了AES、CBC模式、PKCS7Padding——注意:PKCS5Padding和PKCS7Padding在AES的128字节块大小下是等价的,所以这里没问题) - 错误点:最后把加密后的
Data直接用String(decoding: cryptData, as: UTF8.self)转字符串,这完全不符合Java的逻辑,应该做Base64编码。
修正后的Swift代码
import CommonCrypto public func encrypt1(plainText: String, key: String, iv: String) -> String? { let operation = kCCEncrypt guard let data = plainText.data(using: .utf8), let ivData = iv.data(using: .utf8), let keyData = key.data(using: .utf8) else { print("Failed to convert input strings to data") return nil } // 验证密钥长度:AES-128需要16字节,AES-256需要32字节,和Java逻辑保持一致 guard keyData.count == kCCKeySizeAES128 || keyData.count == kCCKeySizeAES256 else { print("Invalid key length: must be 16 (AES-128) or 32 (AES-256) bytes") return nil } // IV长度必须等于AES块大小(16字节) guard ivData.count == kCCBlockSizeAES128 else { print("Invalid IV length: must be 16 bytes") return nil } let cryptLength = size_t(data.count + kCCBlockSizeAES128) var cryptData = Data(count: cryptLength) var numBytesEncrypted: size_t = 0 let cryptStatus = cryptData.withUnsafeMutableBytes { cryptBytes in data.withUnsafeBytes { dataBytes in ivData.withUnsafeBytes { ivBytes in keyData.withUnsafeBytes { keyBytes in CCCrypt(CCOperation(operation), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), keyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), keyData.count, ivBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), dataBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), data.count, cryptBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), cryptLength, &numBytesEncrypted) } } } } guard cryptStatus == kCCSuccess else { print("Encryption error: \(cryptStatus)") return nil } // 截断到实际加密后的有效长度 cryptData.removeSubrange(numBytesEncrypted..<cryptData.count) // 和Java的Base64.NO_WRAP行为一致:返回无换行的Base64字符串 return cryptData.base64EncodedString(options: []) }
关键修正点说明
- 替换UTF8解码为Base64编码:用
cryptData.base64EncodedString(options: [])代替String(decoding: cryptData, as: UTF8.self),这和Java的Base64.encodeToString(ciphertext, Base64.NO_WRAP)行为完全一致(不添加任何换行符)。 - 增强输入合法性校验:添加了密钥和IV长度的校验,避免因为输入不符合AES规范导致的加密失败。
- 兼容iOS13及以下的指针处理:在调用
CCCrypt时,对baseAddress做assumingMemoryBound(to: UInt8.self)绑定,适配iOS13及以下版本Data.withUnsafeBytes的返回类型要求。 - 采用Swift风格的错误处理:把返回值从
String改为String?,方便调用方处理加密失败的场景,更符合Swift的编程习惯。
测试验证
调用这个方法时,传入和Java代码相同的明文、密钥、IV,应该能得到完全一致的Base64字符串。例如:
if let encrypted = encrypt1(plainText: "test", key: "1234567890123456", iv: "1234567890123456") { print("Encrypted: \(encrypted)") }
这个结果会和Java代码调用encrypt1("test", "1234567890123456", "1234567890123456")返回的字符串完全匹配。
内容的提问来源于stack exchange,提问作者sujit amin
相关产品推荐
相关产品推荐

