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

Swift中RSA加解密超132字符报错的解决方案咨询

解决RSA加密大文本的限制问题

嘿,这个问题其实是RSA算法的固有特性导致的——RSA天生就不适合直接加密大体积的数据,尤其是你使用PKCS#1 padding的时候,它对单次加密的明文长度有严格限制:

最大明文长度 = 密钥块大小(字节) - 11

举个例子,如果你用的是1024位密钥,密钥块大小是128字节,那最多只能加密128-11=117字节的明文。而UTF8编码的字符串每个字符可能占1-4字节,132字符的文本字节数肯定超过这个阈值了,所以会报错。你尝试修改UInt8没用,因为这根本不是数据类型的问题,是RSA算法和系统API的限制。

那怎么处理大文本的加解密呢?行业标准方案是混合加密:用RSA加密对称加密算法(比如AES)的密钥,然后用AES加密大文本。这样既保留了RSA非对称加密的安全性,又能处理任意长度的数据。下面是修改后的RSAWrapper类,整合了AES逻辑:

修正后的完整代码

import Security
import CommonCrypto

class RSAWrapper {
    private var publicKey: SecKey?
    private var privateKey: SecKey?
    
    // 修正原代码bug:privateKey的tag应该用privateTag
    func generateKeyPair(keySize: UInt, privateTag: String, publicTag: String) -> Bool {
        self.publicKey = nil
        self.privateKey = nil
        
        guard [512, 1024, 2048].contains(keySize) else {
            print("无效的密钥长度")
            return false
        }
        
        let publicKeyParameters: [CFString: Any] = [
            kSecAttrIsPermanent: true,
            kSecAttrApplicationTag: publicTag.data(using: .utf8)!
        ]
        
        let privateKeyParameters: [CFString: Any] = [
            kSecAttrIsPermanent: true,
            kSecAttrApplicationTag: privateTag.data(using: .utf8)! // 修正此处的tag
        ]
        
        let parameters: [CFString: Any] = [
            kSecAttrKeyType: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits: keySize,
            kSecPrivateKeyAttrs: privateKeyParameters,
            kSecPublicKeyAttrs: publicKeyParameters
        ]
        
        let status = SecKeyGeneratePair(parameters as CFDictionary, &self.publicKey, &self.privateKey)
        return status == errSecSuccess && self.publicKey != nil && self.privateKey != nil
    }
    
    // MARK: - 混合加密:RSA加密AES密钥 + AES加密明文
    func encrypt(text: String) -> Data? {
        guard let publicKey = self.publicKey else {
            print("公钥未初始化")
            return nil
        }
        
        // 1. 生成随机AES密钥(256位)和IV(GCM模式推荐12字节)
        let aesKey = Data(count: 32)
        let iv = Data(count: 12)
        guard aesKey.withUnsafeMutableBytes({ SecRandomCopyBytes(kSecRandomDefault, $0.count, $0.baseAddress!) }) == errSecSuccess,
              iv.withUnsafeMutableBytes({ SecRandomCopyBytes(kSecRandomDefault, $0.count, $0.baseAddress!) }) == errSecSuccess else {
            print("生成随机密钥/IV失败")
            return nil
        }
        
        // 2. 用RSA加密AES密钥
        let aesKeyBytes = [UInt8](aesKey)
        var encryptedAESKeySize = SecKeyGetBlockSize(publicKey)
        var encryptedAESKey = [UInt8](repeating: 0, count: encryptedAESKeySize)
        let rsaStatus = SecKeyEncrypt(publicKey, .PKCS1, aesKeyBytes, aesKeyBytes.count, &encryptedAESKey, &encryptedAESKeySize)
        guard rsaStatus == errSecSuccess else {
            print("RSA加密AES密钥失败")
            return nil
        }
        let encryptedAESKeyData = Data(bytes: encryptedAESKey, count: encryptedAESKeySize)
        
        // 3. 用AES-GCM加密明文
        guard let plaintextData = text.data(using: .utf8) else {
            print("字符串转Data失败")
            return nil
        }
        var encryptedData = Data(count: plaintextData.count + kCCBlockSizeAES128)
        var encryptedDataLength = 0
        var authenticationTag = Data(count: 16) // GCM认证标签推荐16字节
        let aesStatus = plaintextData.withUnsafeBytes { plaintextBytes in
            aesKey.withUnsafeBytes { keyBytes in
                iv.withUnsafeBytes { ivBytes in
                    encryptedData.withUnsafeMutableBytes { encryptedBytes in
                        authenticationTag.withUnsafeMutableBytes { tagBytes in
                            CCCrypt(
                                CCOperation(kCCEncrypt),
                                CCAlgorithm(kCCAlgorithmAES),
                                CCOptions(kCCModeGCM),
                                keyBytes.baseAddress, keyBytes.count,
                                ivBytes.baseAddress,
                                plaintextBytes.baseAddress, plaintextBytes.count,
                                encryptedBytes.baseAddress, encryptedData.count,
                                &encryptedDataLength,
                                tagBytes.baseAddress, tagBytes.count
                            )
                        }
                    }
                }
            }
        }
        guard aesStatus == kCCSuccess else {
            print("AES加密失败")
            return nil
        }
        encryptedData = encryptedData.prefix(encryptedDataLength)
        
        // 4. 打包所有数据:加密AES密钥长度 + 加密AES密钥 + IV + 认证标签 + AES密文
        var resultData = Data()
        resultData.append(contentsOf: withUnsafeBytes(of: UInt32(encryptedAESKeySize).bigEndian) { Array($0) })
        resultData.append(encryptedAESKeyData)
        resultData.append(iv)
        resultData.append(authenticationTag)
        resultData.append(encryptedData)
        
        return resultData
    }
    
    // MARK: - 混合解密:RSA解密AES密钥 + AES解密密文
    func decrypt(data: Data) -> String? {
        guard let privateKey = self.privateKey else {
            print("私钥未初始化")
            return nil
        }
        
        // 1. 解析数据结构
        guard data.count >= 4 + 12 + 16 else { // 密钥长度(4) + IV(12) + 认证标签(16)
            print("数据格式错误")
            return nil
        }
        let encryptedAESKeySize = data.subdata(in: 0..<4).withUnsafeBytes { $0.load(as: UInt32.self).bigEndian }
        let encryptedAESKeyEndIndex = 4 + Int(encryptedAESKeySize)
        
        guard encryptedAESKeyEndIndex + 12 + 16 <= data.count else {
            print("数据格式错误")
            return nil
        }
        
        let encryptedAESKeyData = data.subdata(in: 4..<encryptedAESKeyEndIndex)
        let iv = data.subdata(in: encryptedAESKeyEndIndex..<encryptedAESKeyEndIndex+12)
        let authenticationTag = data.subdata(in: encryptedAESKeyEndIndex+12..<encryptedAESKeyEndIndex+28)
        let encryptedData = data.subdata(in: encryptedAESKeyEndIndex+28..<data.endIndex)
        
        // 2. RSA解密AES密钥
        let encryptedAESKeyBytes = [UInt8](encryptedAESKeyData)
        var aesKeySize = SecKeyGetBlockSize(privateKey)
        var aesKeyBytes = [UInt8](repeating: 0, count: aesKeySize)
        let rsaStatus = SecKeyDecrypt(privateKey, .PKCS1, encryptedAESKeyBytes, encryptedAESKeyBytes.count, &aesKeyBytes, &aesKeySize)
        guard rsaStatus == errSecSuccess else {
            print("RSA解密AES密钥失败")
            return nil
        }
        let aesKey = Data(bytes: aesKeyBytes, count: aesKeySize)
        
        // 3. AES-GCM解密密文
        var decryptedData = Data(count: encryptedData.count)
        var decryptedDataLength = 0
        let aesStatus = encryptedData.withUnsafeBytes { encryptedBytes in
            aesKey.withUnsafeBytes { keyBytes in
                iv.withUnsafeBytes { ivBytes in
                    authenticationTag.withUnsafeBytes { tagBytes in
                        decryptedData.withUnsafeMutableBytes { decryptedBytes in
                            CCCrypt(
                                CCOperation(kCCDecrypt),
                                CCAlgorithm(kCCAlgorithmAES),
                                CCOptions(kCCModeGCM),
                                keyBytes.baseAddress, keyBytes.count,
                                ivBytes.baseAddress,
                                encryptedBytes.baseAddress, encryptedBytes.count,
                                decryptedBytes.baseAddress, decryptedData.count,
                                &decryptedDataLength,
                                tagBytes.baseAddress, tagBytes.count
                            )
                        }
                    }
                }
            }
        }
        guard aesStatus == kCCSuccess else {
            print("AES解密失败")
            return nil
        }
        decryptedData = decryptedData.prefix(decryptedDataLength)
        
        // 4. 转成字符串
        return String(data: decryptedData, encoding: .utf8)
    }
    
    // MARK: - Base64版本的混合加解密
    func encryptBase64(text: String) -> String? {
        guard let encryptedData = encrypt(text: text) else {
            return nil
        }
        return encryptedData.base64EncodedString(options: .lineLength64Characters)
    }
    
    func decryptBase64(encryptedBase64: String) -> String? {
        guard let encryptedData = Data(base64Encoded: encryptedBase64, options: .ignoreUnknownCharacters) else {
            print("Base64解码失败")
            return nil
        }
        return decrypt(data: encryptedData)
    }
    
    func getPublicKey() -> SecKey? {
        return self.publicKey
    }
    
    func getPrivateKey() -> SecKey? {
        return self.privateKey
    }
}

关键修改说明

  1. 修正原代码bug:生成密钥对时,私钥的kSecAttrApplicationTag错误使用了公钥的tag,现在已经修正为privateTag。
  2. 引入混合加密逻辑:
    • 每次加密时生成随机的AES 256位密钥和IV,保证加密安全性。
    • 用RSA加密AES密钥,用AES-GCM模式加密大文本(GCM提供认证加密,防止数据篡改)。
    • 将加密后的AES密钥、IV、认证标签、AES密文打包成一个Data返回,方便传输和存储。
  3. 修正方法名拼写:把decprypt改为正确的decrypt,避免混淆。

为什么不能直接绕过RSA的长度限制?

RSA的加密过程是基于大数模运算的,单次加密的明文长度必须小于密钥的模长(减去padding占用的字节),这是算法本身的数学限制,无法通过修改数据类型或者调整参数绕过。混合加密是行业通用的解决方案,既解决了大文本加密的问题,又保留了RSA非对称加密的优势——不需要提前交换对称密钥。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:21