Swift中用外部公钥加密数据:字符串转SecKey崩溃问题
iOS RSA公钥加密问题:无法将字符串公钥转换为SecKey
我明白你现在卡在iOS里用RSA公钥加密的问题上了——想把服务器给的字符串格式公钥转成SecKey,然后调用SecKeyEncrypt加密数据,但现在代码一运行就崩,之前查了不少资料还是没搞定对吧?
你的核心问题
你代码第一行直接崩溃的原因很明确:不能把PEM格式的公钥字符串直接强制类型转换成SecKey。SecKey是Core Security框架中的安全对象,必须通过系统提供的解析方法从PEM格式的字符串生成,而不是简单的as! SecKey强转。
解决方案:从PEM字符串生成SecKey
要实现这个功能,需要分三步处理PEM公钥,再生成SecKey,最后完成加密。下面是完整的可运行代码示例:
第一步:编写PEM公钥解析工具函数
import Security func secKeyFromPEMPublicKey(pemString: String) -> SecKey? { // 1. 清理PEM格式的冗余内容:去掉头部、尾部、换行和空白字符 let cleanedKey = pemString .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "") .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "") .replacingOccurrences(of: "\n", with: "") .trimmingCharacters(in: .whitespacesAndNewlines) // 2. 将清理后的Base64字符串转换为Data guard let keyData = Data(base64Encoded: cleanedKey) else { print("Failed to convert cleaned key string to Data") return nil } // 3. 配置SecKey的属性(根据你的公钥实际情况调整) let keyAttributes: [CFString: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits: 2048, // 常见的RSA公钥长度是2048或4096,按需修改 kSecReturnPersistentRef: kCFBooleanFalse! ] // 4. 生成SecKey对象 var error: Unmanaged<CFError>? guard let secKey = SecKeyCreateWithData(keyData as CFData, keyAttributes as CFDictionary, &error) else { if let error = error?.takeRetainedValue() { print("Failed to create SecKey: \(error.localizedDescription)") } return nil } return secKey }
第二步:使用解析后的SecKey进行加密
// 替换成你实际的PEM格式公钥字符串 let pemPublicKey = "# -----BEGIN PUBLIC KEY----- some key ---- END PUBLIC KEY----- #" // 生成SecKey guard let publicKey = secKeyFromPEMPublicKey(pemString: pemPublicKey) else { print("Failed to parse public key from PEM string") return } // 准备要加密的明文 let plainText = "plain text" guard let plainTextData = plainText.data(using: .utf8) else { print("Failed to convert plain text to Data") return } // 配置加密参数 let blockSize = SecKeyGetBlockSize(publicKey) var encryptedBytes = [UInt8](repeating: 0, count: blockSize) var encryptedBytesSize = blockSize // 执行加密(注意Padding要和服务器端解密时一致,这里用PKCS1是最常见的) let encryptionStatus = SecKeyEncrypt( publicKey, SecPadding.PKCS1, plainTextData.bytes.assumingMemoryBound(to: UInt8.self), plainTextData.count, &encryptedBytes, &encryptedBytesSize ) // 处理加密结果 if encryptionStatus == errSecSuccess { let encryptedData = Data(bytes: encryptedBytes, count: encryptedBytesSize) // 转成Base64字符串传给服务器 let encryptedBase64 = encryptedData.base64EncodedString() print("Encrypted data (Base64): \(encryptedBase64)") } else { print("Encryption failed with status code: \(encryptionStatus)") }
关键注意事项
- Padding匹配:绝对不能用
SecPadding(rawValue: 0)(即无填充),一定要和服务器端解密使用的Padding保持一致,SecPadding.PKCS1是最常用的标准,优先选择这个。 - 公钥长度匹配:
kSecAttrKeySizeInBits的值必须和你的公钥实际长度一致(比如2048或4096),否则SecKeyCreateWithData会失败。 - PEM格式清理:必须确保去掉所有非Base64的内容,包括头部、尾部、换行符和多余空格,否则Base64转Data会失败。
内容的提问来源于stack exchange,提问作者hossein
相关产品推荐
相关产品推荐

