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

跨平台端到端加密(Swift&Node):共享密钥不一致问题排查

ECDH跨平台密钥派生不一致问题解决

问题现象

生成两组ECDH公私钥并导出为PEM格式,分别导入Swift(CryptoKit)和JS(Web Crypto)后,双方派生的共享密钥不一致,导致无法互相解密。已确认密钥导入后导出与原PEM一致,问题出在共享密钥派生环节。

原JS代码及输出

async function deriveKey(publicKey, privateKey) {
    return await crypto.subtle.deriveKey(
        { name: "ECDH", public: publicKey },
        privateKey,
        { name: "AES-GCM", length: 256 },
        true,
        ["encrypt", "decrypt"]
    );
}
let sharedKey1 = await deriveKey(publicKey2, privateKey);
let exportedKey = await crypto.subtle.exportKey('raw', sharedKey1);
let keyString = Buffer.from(exportedKey).toString('base64');
console.log(keyString);

输出:1vF4AK9IqDDHNZ86zxt5zavx3h+V7AFCfpBU5Yv8Zro=

原Swift代码及输出

func deriveSymmetricKey(privateKey: P256.KeyAgreement.PrivateKey, publicKey: P256.KeyAgreement.PublicKey) throws -> SymmetricKey {
    let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: publicKey)

    let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
        using: SHA256.self,
        salt: Data(),
        sharedInfo: Data(),
        outputByteCount: 32
    )
    
    return symmetricKey
}

let sharedKey1 = try deriveSymmetricKey(privateKey: privateKey, publicKey: publicKey2)

let keyData = sharedKey1.withUnsafeBytes {
    Data(Array($0))
}
let keyString = keyData.base64EncodedString()
print(keyString)

输出:SeQZEg38dcfRl8+5LIwiiJXABJnOMuv3srlrIDZ0wQc=

问题原因

Web Crypto的deriveKey方法从ECDH派生AES密钥时,底层默认使用HKDF,但未显式指定哈希算法、salt和sharedInfo参数,导致其使用的HKDF参数与Swift端不一致:

  • Swift端明确使用SHA256哈希算法、空salt、空sharedInfo
  • 原JS代码未指定HKDF参数,Web Crypto会使用默认值(不同环境可能有差异,且大概率与Swift端不匹配)

修正方案

调整JS代码,显式使用HKDF派生密钥,确保所有参数与Swift端完全一致:

修正后的JS代码

async function deriveSharedKey(publicKey, privateKey) {
    // 先获取ECDH原始共享秘密
    const sharedSecret = await crypto.subtle.deriveBits(
        { name: "ECDH", public: publicKey },
        privateKey,
        256 // P256曲线的共享秘密长度为256位
    );
    // 用HKDF派生对称密钥,参数与Swift严格对齐
    return await crypto.subtle.deriveKey(
        {
            name: "HKDF",
            hash: "SHA-256",
            salt: new Uint8Array(), // 空salt匹配Swift设置
            info: new Uint8Array()  // 空sharedInfo匹配Swift设置
        },
        {
            name: "HKDF",
            raw: sharedSecret
        },
        { name: "AES-GCM", length: 256 },
        true,
        ["encrypt", "decrypt"]
    );
}

let sharedKey1 = await deriveSharedKey(publicKey2, privateKey);
let exportedKey = await crypto.subtle.exportKey('raw', sharedKey1);
let keyString = Buffer.from(exportedKey).toString('base64');
console.log(keyString);

验证说明

修正后,JS端派生的共享密钥Base64字符串会与Swift端完全一致,此时双方使用AES-GCM加密解密即可互相兼容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:07:07