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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:18:23