通过 WebCryptoAPI 而不是浏览器化节点加密模块在浏览器中生成 ECDH 密钥
Generating ECDH keys in the browser through WebCryptoAPI instead of the browserified node crypto module
我有一个非常小的节点脚本来创建 public/private 密钥
有没有什么办法可以在客户端做到这一点而不必浏览 hole 加密模块?
var crypto = require('crypto');
var userCurve = crypto.createECDH('prime256v1');
var userPublicKey = userCurve.generateKeys()
var userPrivateKey = userCurve.getPrivateKey();
到目前为止我已经试过了:
// https://github.com/diafygi/webcrypto-examples#ecdh---generatekey
window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-256", //can be "P-256", "P-384", or "P-521"
},
true, //whether the key is extractable (i.e. can be used in exportKey)
["deriveKey", "deriveBits"] //can be any combination of "deriveKey" and "deriveBits"
)
.then(function(key){
//returns a keypair object
console.log(key);
console.log(key.publicKey);
console.log(key.privateKey);
})
.catch(function(err){
console.error(err);
});
但是当我登录时它看起来不像节点版本
让我们做一个完整的椭圆曲线 Diffie-Hellman (ECDH) 交换来建立双方之间的共享秘密。 Alice 使用 Node.js,Bob 坐在他的浏览器前(Chrome 或 Firefox 的最新版本)。 (无需浏览任何内容。)
(1) Alice 生成私钥和 public 密钥。
const crypto = require('crypto');
const alice = crypto.createECDH('prime256v1');
alice.generateKeys()
const alicePublicKey = alice.getPublicKey('hex')
const alicePrivateKey = alice.getPrivateKey('hex')
console.log(`publicKey: ${alicePublicKey}`)
console.log(`privateKey: ${alicePrivateKey}`)
示例输出:
publicKey: 043a3770a8068738ded16c9409e1a6fbf6dde2360ac5b3fd3e5bb8d9fd6adaed6ea83ff5153f58ae13098e86da89df1beb14ef46388d3df76e8fe2ee0ff9e926d5
privateKey: 03ce9cb317c8761699f174943dc9b2d2b7991515b48216a4c677fcf5ee879f2c
(2) Alice 将她的 public 密钥发送给 Bob (043a3770...
)。 Bob 编写了一些帮助程序将十六进制字符串转换为 Uint8Arrays
并将缓冲区转换为十六进制字符串。
const hex2Arr = str => {
if (!str) {
return new Uint8Array()
}
const arr = []
for (let i = 0, len = str.length; i < len; i+=2) {
arr.push(parseInt(str.substr(i, 2), 16))
}
return new Uint8Array(arr)
}
const buf2Hex = buf => {
return Array.from(new Uint8Array(buf))
.map(x => ('00' + x.toString(16)).slice(-2))
.join('')
}
(3) Bob 收到 Alices 的密钥并计算共享密钥
- 他生成自己的私钥和 public 密钥
- 他导出他的 public 密钥并将其发送给 Alice
- 他导入了 Alice 的 public 密钥
他使用他的私钥和 Alice 的 public 密钥计算共享密钥
// Alice's public key (received over an [insecure] connection)
const alicePublicKeyHex = '043a3770a8068738ded16c9409e1a6fbf6dde2360ac5b3fd3e5bb8d9fd6adaed6ea83ff5153f58ae13098e86da89df1beb14ef46388d3df76e8fe2ee0ff9e926d5'
const alicePublicKey = hex2Arr(alicePublicKeyHex)
console.log(`Alice's publicKey: ${alicePublicKeyHex}`)
let bob = null
// generate Bob's private and public key
window.crypto.subtle.generateKey(
{
name: 'ECDH',
namedCurve: 'P-256'
},
false, // no need to make Bob's private key exportable
['deriveKey', 'deriveBits'])
.then(bobKey => {
bob = bobKey
// export Bob's public key
return window.crypto.subtle.exportKey(
'raw', bobKey.publicKey
)
})
.then(bobPublicKeyExported => {
const bobPublicKeyHex = buf2Hex(bobPublicKeyExported)
// display and send Bob's public key to Alice
console.log(`Bob's publicKey: ${bobPublicKeyHex}`)
// import Alice's public key
return window.crypto.subtle.importKey(
'raw',
alicePublicKey,
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[])
})
.then(aliceKeyImported => {
// use Alice's imported public key and
// Bob's private key to compute the shared secret
return window.crypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: 'P-256',
public: aliceKeyImported
},
bob.privateKey,
256)
})
.then(sharedSecret => {
const sharedSecretHex = buf2Hex(sharedSecret)
console.log(`sharedSecret: ${sharedSecretHex}`)
})
.catch(err => {
console.log(err)
})
示例输出:
Alice's publicKey: 043a3770a8068738ded16c9409e1a6fbf6dde2360ac5b3fd3e5bb8d9fd6adaed6ea83ff5153f58ae13098e86da89df1beb14ef46388d3df76e8fe2ee0ff9e926d5
Bob's publicKey: 04aeceba6ae783c9b705833c2fa8822281f47f6f36bc867e4d398fa7a744d4fc63a010cbce1e6c9ac8858ad376a24ee8551615560f01c8bb63c86335c046b18962
sharedSecret: c26c9f370f001a947d7fec4dc9282d3e9ea718e1de487eb4f6fa7d6f0a311b97
(4) Alice 收到 Bob 的 public 密钥 (04aece...
)。她也计算共享秘密。
const crypto = require('crypto')
const alice = crypto.createECDH('prime256v1')
// Alice's privateKey (generated previously)
const alicePrivateKey = '937cdd11062b612ff3cb3e4a3c183254b9728b4c8c3a64de799ed196b672734b'
// Bob's publicKey transmitted to Alice
const bobPublicKey = '04aeceba6ae783c9b705833c2fa8822281f47f6f36bc867e4d398fa7a744d4fc63a010cbce1e6c9ac8858ad376a24ee8551615560f01c8bb63c86335c046b18962'
// set Alice's private key (not needed if continuing from (1))
alice.setPrivateKey(alicePrivateKey, 'hex')
const sharedSecret = alice.computeSecret(bobPublicKey, 'hex', 'hex')
console.log(`sharedSecret: ${sharedSecret}`)
示例输出:
sharedSecret: c26c9f370f001a947d7fec4dc9282d3e9ea718e1de487eb4f6fa7d6f0a311b97
秘密已共享(相同)。
(5) 共享密钥通常用于派生对称密钥,用于加密 Alice 和 Bob 之间的消息(并且他们从此愉快地通信)。
备注:
通常不需要显示或导出私钥。 Alice 通常会从步骤 (1) 开始继续计算共享秘密(并省略 alice.setPrivateKey(alicePrivateKey, 'hex')
)。
由于共享密钥最常用于派生对称密钥,所以有window.crypto.subtle.deriveKey
,deriveBits
可以省略。 deriveBits
用于说明 Alice 和 Bob 确实就共享秘密达成一致。
我有一个非常小的节点脚本来创建 public/private 密钥 有没有什么办法可以在客户端做到这一点而不必浏览 hole 加密模块?
var crypto = require('crypto');
var userCurve = crypto.createECDH('prime256v1');
var userPublicKey = userCurve.generateKeys()
var userPrivateKey = userCurve.getPrivateKey();
到目前为止我已经试过了:
// https://github.com/diafygi/webcrypto-examples#ecdh---generatekey
window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-256", //can be "P-256", "P-384", or "P-521"
},
true, //whether the key is extractable (i.e. can be used in exportKey)
["deriveKey", "deriveBits"] //can be any combination of "deriveKey" and "deriveBits"
)
.then(function(key){
//returns a keypair object
console.log(key);
console.log(key.publicKey);
console.log(key.privateKey);
})
.catch(function(err){
console.error(err);
});
但是当我登录时它看起来不像节点版本
让我们做一个完整的椭圆曲线 Diffie-Hellman (ECDH) 交换来建立双方之间的共享秘密。 Alice 使用 Node.js,Bob 坐在他的浏览器前(Chrome 或 Firefox 的最新版本)。 (无需浏览任何内容。)
(1) Alice 生成私钥和 public 密钥。
const crypto = require('crypto');
const alice = crypto.createECDH('prime256v1');
alice.generateKeys()
const alicePublicKey = alice.getPublicKey('hex')
const alicePrivateKey = alice.getPrivateKey('hex')
console.log(`publicKey: ${alicePublicKey}`)
console.log(`privateKey: ${alicePrivateKey}`)
示例输出:
publicKey: 043a3770a8068738ded16c9409e1a6fbf6dde2360ac5b3fd3e5bb8d9fd6adaed6ea83ff5153f58ae13098e86da89df1beb14ef46388d3df76e8fe2ee0ff9e926d5
privateKey: 03ce9cb317c8761699f174943dc9b2d2b7991515b48216a4c677fcf5ee879f2c
(2) Alice 将她的 public 密钥发送给 Bob (043a3770...
)。 Bob 编写了一些帮助程序将十六进制字符串转换为 Uint8Arrays
并将缓冲区转换为十六进制字符串。
const hex2Arr = str => {
if (!str) {
return new Uint8Array()
}
const arr = []
for (let i = 0, len = str.length; i < len; i+=2) {
arr.push(parseInt(str.substr(i, 2), 16))
}
return new Uint8Array(arr)
}
const buf2Hex = buf => {
return Array.from(new Uint8Array(buf))
.map(x => ('00' + x.toString(16)).slice(-2))
.join('')
}
(3) Bob 收到 Alices 的密钥并计算共享密钥
- 他生成自己的私钥和 public 密钥
- 他导出他的 public 密钥并将其发送给 Alice
- 他导入了 Alice 的 public 密钥
他使用他的私钥和 Alice 的 public 密钥计算共享密钥
// Alice's public key (received over an [insecure] connection) const alicePublicKeyHex = '043a3770a8068738ded16c9409e1a6fbf6dde2360ac5b3fd3e5bb8d9fd6adaed6ea83ff5153f58ae13098e86da89df1beb14ef46388d3df76e8fe2ee0ff9e926d5' const alicePublicKey = hex2Arr(alicePublicKeyHex) console.log(`Alice's publicKey: ${alicePublicKeyHex}`) let bob = null // generate Bob's private and public key window.crypto.subtle.generateKey( { name: 'ECDH', namedCurve: 'P-256' }, false, // no need to make Bob's private key exportable ['deriveKey', 'deriveBits']) .then(bobKey => { bob = bobKey // export Bob's public key return window.crypto.subtle.exportKey( 'raw', bobKey.publicKey ) }) .then(bobPublicKeyExported => { const bobPublicKeyHex = buf2Hex(bobPublicKeyExported) // display and send Bob's public key to Alice console.log(`Bob's publicKey: ${bobPublicKeyHex}`) // import Alice's public key return window.crypto.subtle.importKey( 'raw', alicePublicKey, { name: 'ECDH', namedCurve: 'P-256' }, true, []) }) .then(aliceKeyImported => { // use Alice's imported public key and // Bob's private key to compute the shared secret return window.crypto.subtle.deriveBits( { name: 'ECDH', namedCurve: 'P-256', public: aliceKeyImported }, bob.privateKey, 256) }) .then(sharedSecret => { const sharedSecretHex = buf2Hex(sharedSecret) console.log(`sharedSecret: ${sharedSecretHex}`) }) .catch(err => { console.log(err) })
示例输出:
Alice's publicKey: 043a3770a8068738ded16c9409e1a6fbf6dde2360ac5b3fd3e5bb8d9fd6adaed6ea83ff5153f58ae13098e86da89df1beb14ef46388d3df76e8fe2ee0ff9e926d5
Bob's publicKey: 04aeceba6ae783c9b705833c2fa8822281f47f6f36bc867e4d398fa7a744d4fc63a010cbce1e6c9ac8858ad376a24ee8551615560f01c8bb63c86335c046b18962
sharedSecret: c26c9f370f001a947d7fec4dc9282d3e9ea718e1de487eb4f6fa7d6f0a311b97
(4) Alice 收到 Bob 的 public 密钥 (04aece...
)。她也计算共享秘密。
const crypto = require('crypto')
const alice = crypto.createECDH('prime256v1')
// Alice's privateKey (generated previously)
const alicePrivateKey = '937cdd11062b612ff3cb3e4a3c183254b9728b4c8c3a64de799ed196b672734b'
// Bob's publicKey transmitted to Alice
const bobPublicKey = '04aeceba6ae783c9b705833c2fa8822281f47f6f36bc867e4d398fa7a744d4fc63a010cbce1e6c9ac8858ad376a24ee8551615560f01c8bb63c86335c046b18962'
// set Alice's private key (not needed if continuing from (1))
alice.setPrivateKey(alicePrivateKey, 'hex')
const sharedSecret = alice.computeSecret(bobPublicKey, 'hex', 'hex')
console.log(`sharedSecret: ${sharedSecret}`)
示例输出:
sharedSecret: c26c9f370f001a947d7fec4dc9282d3e9ea718e1de487eb4f6fa7d6f0a311b97
秘密已共享(相同)。
(5) 共享密钥通常用于派生对称密钥,用于加密 Alice 和 Bob 之间的消息(并且他们从此愉快地通信)。
备注:
通常不需要显示或导出私钥。 Alice 通常会从步骤 (1) 开始继续计算共享秘密(并省略
alice.setPrivateKey(alicePrivateKey, 'hex')
)。由于共享密钥最常用于派生对称密钥,所以有
window.crypto.subtle.deriveKey
,deriveBits
可以省略。deriveBits
用于说明 Alice 和 Bob 确实就共享秘密达成一致。