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

使用CryptoSwift将AES-256-CBC的PHP代码转Swift后结果不匹配

解决CryptoSwift与PHP AES-256-CBC加密结果不匹配的问题

你的问题核心在于Swift代码没有完全复刻PHP的加密流程,尤其是密钥和IV的生成逻辑——PHP中使用的是哈希结果的十六进制字符串,而你在Swift中直接用了哈希的二进制数据转UTF-8字符串,这导致两者的密钥/IV字节完全不一致。以下是修正后的代码和详细解释:

修正后的Swift代码

首先需要添加几个工具方法来处理哈希和十六进制转换,然后严格按照PHP的流程实现:

import CryptoSwift

extension String {
    // 计算字符串的SHA1小写十六进制结果(和PHP sha1()行为一致)
    func sha1Hex() -> String {
        guard let data = self.data(using: .utf8) else { return "" }
        let digest = SHA1.hash(data: data)
        return digest.map { String(format: "%02x", $0) }.joined()
    }
    
    // 计算字符串的SHA256小写十六进制结果(和PHP hash('sha256', ...)行为一致)
    func sha256Hex() -> String {
        guard let data = self.data(using: .utf8) else { return "" }
        let digest = SHA256.hash(data: data)
        return digest.map { String(format: "%02x", $0) }.joined()
    }
}

func encryptString() -> String? {
    let plaintext = "MyStringToEncrypt"
    let originalSecretKey = "MySecretKey"
    let originalSecretIV = "MySecretIV"
    
    // 1. 复刻PHP的密钥生成流程
    let secretKeyHex = originalSecretKey.sha1Hex()
    let keyHex = secretKeyHex.sha256Hex()
    // 将64位十六进制字符串转成UTF-8字节,取前32字节(AES-256需要32字节密钥,和openssl截断逻辑一致)
    guard let keyData = keyHex.data(using: .utf8), keyData.count >= 32 else {
        print("密钥生成失败")
        return nil
    }
    let key = Array(keyData.prefix(32))
    
    // 2. 复刻PHP的IV生成流程
    let secretIVHex = originalSecretIV.sha1Hex()
    let ivFullHex = secretIVHex.sha256Hex()
    // 取十六进制字符串前16个字符,转成UTF-8字节(正好16字节,符合CBC要求)
    let ivHexPrefix = String(ivFullHex.prefix(16))
    guard let ivData = ivHexPrefix.data(using: .utf8), ivData.count == 16 else {
        print("IV生成失败")
        return nil
    }
    let iv = Array(ivData)
    
    // 3. 处理明文
    guard let plaintextData = plaintext.data(using: .utf8) else {
        print("明文转Data失败")
        return nil
    }
    let plaintextBytes = Array(plaintextData)
    
    // 4. AES-256-CBC加密(使用PKCS7填充,和PHP默认行为一致)
    do {
        let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7)
        let encryptedBytes = try aes.encrypt(plaintextBytes)
        // 转成Base64字符串
        let encryptedData = Data(encryptedBytes)
        return encryptedData.base64EncodedString()
    } catch {
        print("加密错误: \(error)")
        return nil
    }
}

// 调用测试
if let result = encryptString() {
    print("加密结果: \(result)")
}

错误原因分析

对比你的原始Swift代码和PHP流程,核心差异点:

  • 哈希结果的处理:PHP的sha1()和hash('sha256', ...)返回的是小写十六进制字符串,而你之前的Swift代码直接使用了哈希的二进制Data转UTF-8字符串,这导致两者的中间值完全不同。
  • 密钥截断逻辑:PHP中$key是64位十六进制字符串(UTF-8转成64字节),openssl会自动截断前32字节作为AES-256密钥;你之前的Swift代码错误地截取了SHA256二进制Data的前32字节转字符串,和PHP的密钥字节完全不匹配。
  • IV生成逻辑:PHP是取SHA256十六进制字符串的前16个字符转UTF-8字节作为IV,而你之前的Swift代码取的是SHA256二进制Data的前16字节转字符串,两者的IV字节不一致。
  • 填充方式:CryptoSwift默认的填充方式可能和PHP默认的PKCS7不一致,需要显式指定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:00:25