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

Swift中AES CBC PKCS5Padding加密乱码问题求助(适配iOS13及以下)

解决iOS 13及以下AES-CBC-PKCS5Padding加密乱码问题

你的问题核心很明确:加密后的二进制数据不能直接通过UTF8解码成字符串——Java代码里最后是对加密字节做了Base64编码返回,而你的Swift代码错误地把加密后的原始二进制数据直接转成UTF8字符串,这必然会出现乱码,因为加密后的字节流不是合法的UTF8文本。

接下来我们一步步修正你的Swift实现,使其和Java代码逻辑完全对齐:

问题分析

先回顾你的Java代码关键步骤:

  1. 明文、密钥、IV都转成UTF8字节
  2. 使用AES/CBC/PKCS5Padding模式加密
  3. 加密后的字节做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: [])
}

关键修正点说明

  1. 替换UTF8解码为Base64编码:用cryptData.base64EncodedString(options: [])代替String(decoding: cryptData, as: UTF8.self),这和Java的Base64.encodeToString(ciphertext, Base64.NO_WRAP)行为完全一致(不添加任何换行符)。
  2. 增强输入合法性校验:添加了密钥和IV长度的校验,避免因为输入不符合AES规范导致的加密失败。
  3. 兼容iOS13及以下的指针处理:在调用CCCrypt时,对baseAddress做assumingMemoryBound(to: UInt8.self)绑定,适配iOS13及以下版本Data.withUnsafeBytes的返回类型要求。
  4. 采用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 09:22:55