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

如何在Swift中使用CommonCrypto解密JavaScript端CryptoJS加密的数据

问题分析与解决方案

咱们一步步拆解你遇到的问题,再针对性解决:

问题根源

  1. Base64密文处理逻辑错误
    你把JavaScript端传来的Base64加密文本直接转成UTF8字节数组(Array(str.utf8)),这是完全错误的。Base64是二进制密文的文本编码格式,你需要先把这个Base64字符串解码回原始的二进制密文,而不是把Base64字符串本身的字节当作待解密的数据。这就是为什么你看到buf和本地加密生成的encryptedData完全不一致——前者是Base64文本的UTF8字节,后者才是真正的加密二进制数据。

  2. 密钥长度的隐性兼容问题
    你的密钥字符串"MY SECRET KEY 123"转成UTF8后是17字节,但AES-128要求密钥必须是16字节。CryptoJS会自动截断密钥到前16字节,而CommonCrypto在指定kCCKeySizeAES128时也会只取前16字节,但为了两端行为完全一致,最好手动对齐这个处理逻辑。

  3. CryptoJS输出的细节优化
    CryptoJS的AES.encrypt返回的是CipherParams对象,直接console.log它会自动调用toString()输出Base64密文,但建议你显式调用encrypted.toString()来确保输出的是标准Base64格式,避免意外的格式差异。

修正后的完整实现

第一步:确保JavaScript端输出正确的Base64密文

先调整你的JS代码,显式输出标准Base64字符串:

const key = CryptoJS.enc.Utf8.parse("MY SECRET KEY 123"); 
const iv = CryptoJS.enc.Utf8.parse("ui09ji884uh88984"); 
var encrypted = CryptoJS.AES.encrypt("Hello world", key, { 
    mode: CryptoJS.mode.CBC, 
    padding: CryptoJS.pad.Pkcs7, 
    iv: iv 
}); 
// 显式调用toString(),确保输出标准Base64密文
console.log("Encrypted data = " + encrypted.toString());

第二步:修正Swift端的解密逻辑

调整你的Swift代码,重点修复Base64解码步骤,并对齐密钥处理逻辑:

import CommonCrypto

func testCrypt(data: [UInt8], keyData: [UInt8], ivData: [UInt8], operation: Int) -> [UInt8]? {
    let cryptLength = data.count + kCCBlockSizeAES128
    var cryptData = [UInt8](repeating: 0, count: cryptLength)
    let keyLength = size_t(kCCKeySizeAES128)
    let algorithm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
    // PKCS7填充,CBC模式是CommonCrypto默认的AES模式,无需额外指定
    let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
    var numBytesEncrypted: size_t = 0
    
    let cryptStatus = CCCrypt(
        CCOperation(operation),
        algorithm,
        options,
        keyData,
        keyLength,
        ivData,
        data,
        data.count,
        &cryptData,
        cryptLength,
        &numBytesEncrypted
    )
    
    guard cryptStatus == kCCSuccess else {
        print("加解密错误:\(cryptStatus)")
        return nil
    }
    
    cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
    return cryptData
}

// 调用示例
func testJSEncryptedData() {
    let keyString = "MY SECRET KEY 123"
    let ivString = "ui09ji884uh88984"
    
    // 对齐CryptoJS的密钥处理:截断到16字节(AES-128要求)
    let keyData = Array(keyString.utf8.prefix(kCCKeySizeAES128))
    // IV是16字节,符合AES-CBC要求
    let ivData = Array(ivString.utf8)
    
    // 替换成JS端输出的Base64密文
    let jsEncryptedBase64 = "这里填入JS输出的Base64字符串"
    
    // 关键步骤:将Base64字符串解码为原始二进制密文
    guard let encryptedData = Data(base64Encoded: jsEncryptedBase64), !encryptedData.isEmpty else {
        print("无效的Base64字符串")
        return
    }
    let encryptedBytes = Array(encryptedData)
    
    // 执行解密
    if let decryptedBytes = testCrypt(data: encryptedBytes, keyData: keyData, ivData: ivData, operation: kCCDecrypt) {
        if let decryptedString = String(bytes: decryptedBytes, encoding: .utf8) {
            print("解密结果:\(decryptedString)") // 应该输出Hello world
        } else {
            print("解密后的二进制数据无法转换为UTF8字符串")
        }
    } else {
        print("解密失败")
    }
}

// 调用测试
testJSEncryptedData()

额外建议

  • 尽量使用16字节的密钥(比如把密钥改成"MY SECRET KEY 12"),避免依赖两端自动截断的行为,减少潜在兼容性问题。
  • 可以考虑把testCrypt函数改成接收Data类型,这样处理起来更符合Swift的习惯,也能减少数组转换的麻烦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:17:49