使用WebCrypto从PBKDF2生成ECDH密钥

时间:2018-11-30 13:43:14

标签: javascript cryptography diffie-hellman

  

警告:以下内容不旨在支持将密码转换为ECDH密钥。从高熵,安全加密的PRNG创建ECDH密钥。

我想保密,并据此生成ECDH公钥/私钥。

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

以下示例代码应执行此操作,但它会在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 }时),但是其他算法也会抛出异常,因此我怀疑我缺少了一些东西……

我的希望是/希望WebCrypto适合接受随机位并生成ECDH公共/私有密钥对。看起来可能并非如此。

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

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

使用PBKDF是为了避免在给定({private} x参数的情况下必须生成ECDH密钥对的公钥(yd)。 xy是派生的,因此不需要存储,并且我们的数据存储区非常有限-仅适用于私钥,例如或多或少为192位(PBKDF也可以平滑位大小,但这是一个问题)。

如果WebCrypto在给定(伪)随机x参数时计算了yd,则可以实现/说明所需的结果 ,如下所示:< / p>

>>> 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变体),然后使用具有模算术的大整数函数(例如Fermat的Little定理),最后编写用于添加椭圆曲线点的函数,加倍和乘法。这就是我所做的(ECC math here)。但是我希望这一切都是不必要的,因为WebCrypto中存在执行此操作的代码,而我只是希望使用importKeyderiveKey来使用它。

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

3 个答案:

答案 0 :(得分:2)

“微妙”。好人!

SubtleCrypto.generateKey()

在阅读了上面链接中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);
})();

答案 1 :(得分:2)

布莱恩

不可能使用WebCrypto从密码确定性地创建密钥对。

对不起。

您可以使用Javascript中的密码确定性地创建密钥对,然后导入该密钥对,只要它以正确的表示形式提供secp256r1密钥即可。

那里有一些JavaScript库,它们支持从密码确定性地创建密钥对。对于这些库的正确性或安全性,或者对于secp256r1,我无法发表任何声明。当然,密钥生成是使用加密时要考虑的最重要方面之一,而基于弱的熵源这样做是不好的。

如果尽管导入了给定类型的密钥很简单,但您还是决定继续采用这种方法,请参见:https://github.com/diafygi/webcrypto-examples#ecdsa---importkey

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

瑞安

答案 2 :(得分:1)

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