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 } }
关键修改说明
- 修正原代码bug:生成密钥对时,私钥的
kSecAttrApplicationTag错误使用了公钥的tag,现在已经修正为privateTag。 - 引入混合加密逻辑:
- 每次加密时生成随机的AES 256位密钥和IV,保证加密安全性。
- 用RSA加密AES密钥,用AES-GCM模式加密大文本(GCM提供认证加密,防止数据篡改)。
- 将加密后的AES密钥、IV、认证标签、AES密文打包成一个
Data返回,方便传输和存储。
- 修正方法名拼写:把
decprypt改为正确的decrypt,避免混淆。
为什么不能直接绕过RSA的长度限制?
RSA的加密过程是基于大数模运算的,单次加密的明文长度必须小于密钥的模长(减去padding占用的字节),这是算法本身的数学限制,无法通过修改数据类型或者调整参数绕过。混合加密是行业通用的解决方案,既解决了大文本加密的问题,又保留了RSA非对称加密的优势——不需要提前交换对称密钥。
内容的提问来源于stack exchange,提问作者Sina Hajian
相关产品推荐
相关产品推荐

