使用 WebCrypto 从 PBKDF2 生成 ECDH 密钥

Using WebCrypto to generate ECDH key from PBKDF2

WARNING The following is not intended as an endorsement of converting passwords to ECDH keys. Create ECDH keys from high-entropy, crypto-safe PRNGs.

我想获取一个秘密并从中生成一个 ECDH public/private 密钥。

在浏览器中,通常的方法是使用 PBKDF2(或其他确定性字节)在 WebCrypto 中生成 ECDH public/private 密钥对。

下面的示例代码应该这样做,但它在 Chrome 中抛出 DOM 异常:

// Generate a random KDF key.
const priv = new Uint8Array(24)
crypto.getRandomValues(priv)
const kdfKey = await crypto.subtle.importKey(
  'raw', priv, { name: 'PBKDF2' }, false, ['deriveKey'])

// Derive the ECDH key.
const salt = new Uint8Array(16)
const iterations = 2000
const hash = { name: 'SHA-512' }
const curve = { name: 'ECDH', namedCurve: 'P-384' }
const usages = ['deriveKey']

crypto.getRandomValues(salt)

const ecdhKey = await crypto.subtle.deriveKey({
  name: 'PBKDF2', salt, iterations, hash
}, kdfKey, curve, true, usages) // throws.

以上算法在算法为 AES-GCM 时有效(即当曲线被替换为 { name: 'AES-GCM', length: 256 } 时),但其他算法也会抛出异常,所以我怀疑我遗漏了一些东西.. .微妙.

我希望 was/is WebCrypto 适合接受随机位并生成 ECDH public/private 密钥对。看起来情况可能并非如此。

另一种方法是使用 PBKDF2deriveBits 来手动创建 ECDH 密钥对。如果这确实是唯一的选择,那么将随机位转换为 public/private 密钥的常用算法是什么(即引用和 public 实现)?如果我必须开发一些东西,我可能会 post 在这里感兴趣并评论。

编辑:用例的其他详细信息

使用 PBKDF 是为了避免在给定(私有)[=18 时生成 ECDH 密钥对的 public 密钥(xy) =] 参数。 xy 是派生的,因此不需要存储,而且我们的数据存储非常有限——仅适用于私钥,例如192 位,或多或少(PBKDF 也可以平滑位大小,但那是一边)。

如果 WebCrypto 在给定(伪)随机 d 参数时计算 xy,则期望的结果 可能 是 achieved/illustrated如下:

>>> curve = { name: 'ECDH', namedCurve: 'P-256' }
>>> k = await crypto.subtle.generateKey(curve, true, ['deriveKey'])
>>> pri = await crypto.subtle.exportKey('jwk', k.privateKey)
>>> delete pri.x
>>> delete pri.y
>>> k2 = await crypto.subtle.importKey('jwk', pri)
    ^^ errors

PBKDF 在许多示例中用于生成 (AES) 密钥。我希望计算椭圆曲线 xy 的功能,它已经存在于 WebCrypto 中,可以通过 PBKDF2 deriveKey.

获得

Javascript 中自己动手的替代方法是解析 JWK/Base64(URL 变体),然后使用具有模运算的大整数函数(例如费马小定理), 最后编写椭圆曲线点加、倍、乘的函数。这就是我所做的 ()。但我希望这一切都是不必要的,因为 WebCrypto 中存在执行此操作的代码,我只是希望使用 importKeyderiveKey 来使用它。

重申一下:没有用户生成的密码;使用它来生成 ECDH 密钥被认为是不明智的。

"subtle."好一个!

SubtleCrypto.generateKey()

在阅读上面 link 处的 SubtleCrypto 密钥生成说明后,我将其弹出到 Chrome 上的浏览​​器控制台中,它运行良好。您可能需要调整它以适应您的情况,尤其是关于 keyUsages 的部分:

(async function() {
    const algo = {
        "name": "RSASSA-PKCS1-v1_5",
        "modulusLength": 256,
        "publicExponent": new Uint8Array([0x01, 0x00, 0x01]),
        "hash": {
            "name": "SHA-256"
        }
    };
    const extractable = true;
    const keyUsages = ["sign","verify"];
    const result = await crypto.subtle.generateKey(algo, extractable, keyUsages);
    console.log(result);
})();

布莱恩,

无法使用 WebCrypto 从密码确定性地创建密钥对。

对不起。

您可以根据 Javascript 中的密码确定性地创建密钥对,然后导入该密钥对,只要它是正确表示的 secp256r1 密钥即可。

有一些 javascript 库支持根据密码确定性地创建密钥对。我不能对这些库的正确性或安全性做出任何声明,或者是否有对 secp256r1 执行此操作的库。当然,密钥生成是使用加密时要考虑的最重要方面之一,基于弱熵源这样做是不好的。

尽管导入给定类型的密钥非常简单,但如果您决定继续使用该方法,请参阅:https://github.com/diafygi/webcrypto-examples#ecdsa---importkey

TL;DR 您的里程会因这种方法而异,但这是可能的。

瑞安

从更新中我们仍然无法判断用例是什么,我们可以判断(我认为)您的目标是出于某种原因节省存储空间 space。 ECC 密钥非常小,很难想象用例会是什么,尤其是在 Web 应用程序中,如此少量的存储需要使用较弱的方法。也就是说,之前的答案保持不变,无法直接使用 webcrypto 执行您想要的操作。