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

Swift中使用Security框架DH交换EC密钥后私钥恢复失败(返回nil)

解决Swift中ECDH密钥交换后无法从CFData恢复私钥的问题

兄弟,我太懂你这个坑了!之前我在做ECDH加解密的时候也踩过一模一样的雷——你拿到的shared1/shared2根本不是可以直接恢复的非对称私钥,这就是为啥你调用恢复API会返回nil的核心原因!

问题根源:对ECDH共享密钥的误解

ECDH(椭圆曲线迪菲-赫尔曼)交换后生成的sharedSecret是对称密钥原材料,不是非加密体系里的私钥。它只是一个字节序列,需要经过KDF(密钥派生函数)处理后,才能变成可用的对称加密密钥(比如AES密钥),不能直接用私钥恢复的API去解析它。

正确的实现步骤

我结合你的代码片段,给你梳理完整的正确流程:

1. 生成EC密钥对并计算共享密钥材料

首先补全密钥生成和共享密钥计算的代码,确保拿到正确的密钥材料:

import Security
import CryptoKit

// 定义密钥属性
let attributes: [String: Any] = [
    kSecAttrKeySizeInBits as String: 256,
    kSecAttrKeyType as String: kSecAttrKeyTypeEC,
    kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
    kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]
]

var error: Unmanaged<CFError>?

// 生成本地密钥对
guard let localPrivateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
    fatalError("生成本地私钥失败: \(error!.takeRetainedValue())")
}
let localPublicKey = SecKeyCopyPublicKey(localPrivateKey)!

// 模拟获取对方公钥(实际场景中从网络接收对方的公钥数据)
// 这里假设我们已经拿到了对方公钥的Data
guard let peerPublicKey = SecKeyCreateWithData(peerPublicKeyData, attributes as CFDictionary, &error) else {
    fatalError("解析对方公钥失败: \(error!.takeRetainedValue())")
}

// 计算ECDH共享密钥材料
guard let sharedSecretData = SecKeyCopyKeyExchangeResult(
    localPrivateKey,
    .ecdhKeyExchangeStandard,
    peerPublicKey,
    attributes as CFDictionary,
    &error
) as Data? else {
    fatalError("计算共享密钥失败: \(error!.takeRetainedValue())")
}

2. 用KDF派生可用的对称密钥

拿到sharedSecretData后,我们用HKDF(基于哈希的密钥派生函数)把它转换成标准的AES密钥:

// 自定义盐和信息字段(可以根据业务需求调整,增强密钥唯一性)
let salt = Data("MyECDHSalt".utf8)
let info = "ECDH-AES-256-Encryption".data(using: .utf8)!

// 使用SHA256作为哈希算法派生256位AES密钥
let hkdf = HKDF<SHA256>(
    inputKeyMaterial: sharedSecretData,
    salt: salt,
    info: info
)
let aes256Key = hkdf.deriveSymmetricKey(size: 256)

3. 用派生的对称密钥完成加解密

现在就可以用这个AES密钥来加密和解密字符串了,推荐用CryptoKit的AES-GCM模式(自带认证,更安全):

// 加密字符串
func encryptString(_ plainText: String, with key: SymmetricKey) throws -> Data {
    let plainData = plainText.data(using: .utf8)!
    let sealedBox = try AES.GCM.seal(plainData, using: key)
    // 返回包含密文、IV和认证标签的组合数据
    return sealedBox.combined!
}

// 解密数据
func decryptData(_ encryptedData: Data, with key: SymmetricKey) throws -> String {
    let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
    let decryptedData = try AES.GCM.open(sealedBox, using: key)
    guard let plainText = String(data: decryptedData, encoding: .utf8) else {
        throw NSError(domain: "ECDH解密失败", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法将解密数据转换为字符串"])
    }
    return plainText
}

最后再划个重点

别再把ECDH的共享密钥材料当成私钥来处理啦!它的作用是给双方生成一个相同的对称密钥原材料,经过KDF处理后,就可以用对称加密算法来完成数据的加解密了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:42:56