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

Swift4中AES解密偶尔返回null值问题求助

Troubleshooting AES Decryption Returning Nil in Swift (PHP Encrypted Source)

我帮你梳理几个最可能导致这个解密方法偶尔返回nil的原因,以及对应的排查和解决思路:

1. Key/IV长度不符合AES标准

AES-128要求密钥(Key)和初始向量(IV)必须是16字节(UTF-8编码下对应16个字符),但你调用时传的key: "mykey"(仅5字节)和iv: "value"(仅5字节)完全不满足要求!

PHP的加密函数(比如openssl_encrypt)如果收到短key/IV,会自动做补全(比如用零字节填充),但你的Swift代码直接用原始短key/IV传入CCCrypt,会触发参数错误导致解密失败。

解决方法:

  • 确保key和IV的长度符合AES规格:AES-128用16字节,AES-256用32字节。
  • 统一Swift和PHP的key/IV处理逻辑:比如两边都对原始key做哈希(比如SHA256),再截取对应长度的字节;或者直接补零到指定长度。示例代码里我已经帮你加了key/IV的长度处理逻辑。

2. 加密模式或填充方式不匹配

你的Swift代码默认用的是CBC模式+PKCS7填充(因为传入了IV参数,且options设为kCCOptionPKCS7Padding),但如果PHP端用的是其他模式(比如ECB、GCM)或者填充方式(比如零填充),两边不匹配就会解密失败。

解决方法:

  • 检查PHP端的加密代码,比如如果是openssl_encrypt,确认第三个参数是AES-128-CBC,且填充方式是PKCS7(openssl_encrypt默认就是PKCS7,不用额外设置)。
  • 如果PHP用的是零填充,把Swift代码的options改为0,解密后手动去掉末尾的零字节。

3. Base64编码/解码存在异常

虽然你设置了.ignoreUnknownCharacters,但如果responseString里包含大量非Base64字符(比如换行、空格、传输过程中混入的乱码),还是会导致Base64解码失败,进而返回nil。

解决方法:

  • 先打印responseString的原始内容,检查是否有多余字符,预处理时可以先去掉首尾空白和换行:
    let cleanedResponse = responseString?.trimmingCharacters(in: .whitespacesAndNewlines)
    unencode = cleanedResponse?.aesDecrypt(key: "mykey", iv: "value")
    
  • 确认PHP端的Base64编码逻辑:比如不要用chunk_split给Base64字符串加换行,避免Swift解码时出错。

4. 数据传输过程中被截断或损坏

AES加密后的数据长度必须是16字节(AES块大小)的整数倍,如果responseString对应的Base64解码后的数据长度不符合这个要求,说明数据在传输或加密过程中被截断了。

解决方法:

  • 在Swift里打印解码后的data长度:
    if let data = Data(base64Encoded: responseString!) {
        print("Decoded data length: \(data.count)")
    }
    
    如果长度不是16的倍数,说明数据有问题,需要排查PHP端的加密输出或网络传输环节。

5. 打印错误码精准定位问题

你的代码只判断了是否成功,没有输出具体错误码,其实CCCrypt返回的状态码能直接告诉你失败原因(比如参数错误、填充错误等)。

解决方法:
在解密失败的分支里添加错误打印:

} else {
    print("Decryption failed with code: \(cryptStatus)")
    // 常见错误码:
    // -4300: 参数错误(比如key/IV长度不对)
    // -4303: 解码错误(比如填充不匹配)
    return nil
}

修改后的示例代码

我调整了你的扩展方法,加入了key/IV长度处理和错误码打印:

extension String {
    func aesDecrypt(key: String, iv: String, options: Int = kCCOptionPKCS7Padding) -> String? {
        // 处理Key,确保符合AES-128的16字节要求
        var keyData = key.data(using: .utf8)!
        if keyData.count < kCCKeySizeAES128 {
            let padding = Data(repeating: 0, count: kCCKeySizeAES128 - keyData.count)
            keyData.append(padding)
        } else if keyData.count > kCCKeySizeAES128 {
            keyData = keyData.prefix(kCCKeySizeAES128)
        }
        
        // 处理IV,确保符合16字节要求
        var ivData = iv.data(using: .utf8)!
        if ivData.count < kCCBlockSizeAES128 {
            let padding = Data(repeating: 0, count: kCCBlockSizeAES128 - ivData.count)
            ivData.append(padding)
        } else if ivData.count > kCCBlockSizeAES128 {
            ivData = ivData.prefix(kCCBlockSizeAES128)
        }
        
        guard let data = Data(base64Encoded: self, options: .ignoreUnknownCharacters),
              let cryptData = NSMutableData(length: data.count + kCCBlockSizeAES128) else {
            return nil
        }
        
        let keyLength = size_t(kCCKeySizeAES128)
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options: CCOptions = UInt32(options)
        var numBytesEncrypted: size_t = 0
        
        let cryptStatus = CCCrypt(operation, algoritm, options,
                                  keyData.bytes, keyLength,
                                  ivData.bytes,
                                  data.bytes, data.count,
                                  cryptData.mutableBytes, cryptData.length,
                                  &numBytesEncrypted)
        
        if cryptStatus == kCCSuccess {
            cryptData.length = Int(numBytesEncrypted)
            return String(data: cryptData as Data, encoding: .utf8)
        } else {
            print("Decryption failed with code: \(cryptStatus)")
            return nil
        }
    }
}

另外,确保PHP端的加密逻辑和Swift对齐,比如PHP的加密示例:

function aesEncrypt($data, $key, $iv) {
    // 统一截取/补全到16字节
    $key = substr($key, 0, 16);
    $iv = substr($iv, 0, 16);
    // 用RAW模式加密,再转Base64
    $encrypted = openssl_encrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($encrypted);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:25:30