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

iOS Swift中使用Security框架生成兼容OpenSSL的EC PEM密钥对问题

问题原因

你导出的公钥格式不符合OpenSSL的解析要求。SecKeyCopyExternalRepresentation导出的是EC公钥的原始二进制数据(仅包含x、y坐标),而OpenSSL解析-----BEGIN PUBLIC KEY-----格式时,期望的是符合X.509标准的SubjectPublicKeyInfo(SPKI)结构(经ASN.1 DER编码后再Base64编码)。RSA密钥能正常解析是因为其外部表示刚好就是SPKI格式,但EC密钥并非如此。

另外你指定了kSecAttrTokenIDSecureEnclave,Secure Enclave生成的EC密钥在导出原始数据时无异常,转换SPKI格式的逻辑和普通密钥一致。

修复代码

利用CryptoKit与Security框架的互转能力,将SecKey转为CryptoKit的公钥后导出SPKI格式数据,即可生成OpenSSL可识别的PEM:

import Foundation
import Security
import CryptoKit

func generateKeys() throws -> (privateKey: SecKey, publicKey: SecKey)  {
    let query: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeEC,
        kSecAttrKeySizeInBits as String: 256,
        kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
        kSecAttrIsPermanent as String: false
    ]

    var error: Unmanaged<CFError>?
       
    guard let privateKey = SecKeyCreateRandomKey(query as CFDictionary, &error) else {
        throw error!.takeRetainedValue() as Error
    }
    let publicKey = SecKeyCopyPublicKey(privateKey)!
    return (privateKey, publicKey)
}

extension SecKey {
    func exportSPKIPEM() throws -> String {
        // 将SecKey转为CryptoKit的P256公钥
        guard let rawData = SecKeyCopyExternalRepresentation(self, nil) else {
            throw NSError(domain: "KeyExportError", code: -1, userInfo: [NSLocalizedDescriptionKey: "获取密钥数据失败"])
        }
        let cryptoKitPublicKey = try P256.KeyAgreement.PublicKey(rawRepresentation: rawData as Data)
        
        // 导出SPKI格式的DER数据
        let spkiData = cryptoKitPublicKey.rawRepresentation
        
        // 转为带换行的Base64并添加PEM头脚
        let base64Str = spkiData.base64EncodedString(options: [.lineLength64Characters])
        return "-----BEGIN PUBLIC KEY-----\n\(base64Str)\n-----END PUBLIC KEY-----"
    }
}

func printPublicKey() {
    do {
        let keyPair = try generateKeys()
        let pemKey = try keyPair.publicKey.exportSPKIPEM()
        print(pemKey)
    } catch {
        print("错误:\(error)")
    }
}

printPublicKey()
验证方法

将输出的PEM内容保存为文件,执行以下OpenSSL命令:

openssl pkey -pubin -in my-public-ec.pem -text -noout

会输出符合prime256v1(即NIST P-256)曲线的公钥信息,与OpenSSL原生生成的结果一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 01:52:19