2016-11-29 17:54:41 +01:00
|
|
|
'use strict'
|
|
|
|
|
2019-07-17 11:17:18 +01:00
|
|
|
const webcrypto = require('../webcrypto')
|
2019-01-08 18:37:03 +00:00
|
|
|
const randomBytes = require('../random-bytes')
|
2016-11-29 17:54:41 +01:00
|
|
|
|
|
|
|
exports.utils = require('./rsa-utils')
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
exports.generateKey = async function (bits) {
|
2019-07-17 11:17:18 +01:00
|
|
|
const pair = await webcrypto.get().subtle.generateKey(
|
2016-11-29 17:54:41 +01:00
|
|
|
{
|
|
|
|
name: 'RSASSA-PKCS1-v1_5',
|
|
|
|
modulusLength: bits,
|
|
|
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
2019-01-03 08:13:07 -08:00
|
|
|
hash: { name: 'SHA-256' }
|
2016-11-29 17:54:41 +01:00
|
|
|
},
|
|
|
|
true,
|
|
|
|
['sign', 'verify']
|
|
|
|
)
|
2019-07-10 17:15:26 +01:00
|
|
|
|
|
|
|
const keys = await exportKey(pair)
|
|
|
|
|
|
|
|
return {
|
|
|
|
privateKey: keys[0],
|
|
|
|
publicKey: keys[1]
|
|
|
|
}
|
2016-11-29 17:54:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Takes a jwk key
|
2019-07-10 17:15:26 +01:00
|
|
|
exports.unmarshalPrivateKey = async function (key) {
|
2019-07-17 11:17:18 +01:00
|
|
|
const privateKey = await webcrypto.get().subtle.importKey(
|
2016-11-29 17:54:41 +01:00
|
|
|
'jwk',
|
|
|
|
key,
|
|
|
|
{
|
|
|
|
name: 'RSASSA-PKCS1-v1_5',
|
2019-01-03 08:13:07 -08:00
|
|
|
hash: { name: 'SHA-256' }
|
2016-11-29 17:54:41 +01:00
|
|
|
},
|
|
|
|
true,
|
|
|
|
['sign']
|
|
|
|
)
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
const pair = [
|
2016-11-29 17:54:41 +01:00
|
|
|
privateKey,
|
2019-07-10 17:15:26 +01:00
|
|
|
await derivePublicFromPrivate(key)
|
|
|
|
]
|
|
|
|
|
|
|
|
const keys = await exportKey({
|
|
|
|
privateKey: pair[0],
|
|
|
|
publicKey: pair[1]
|
|
|
|
})
|
|
|
|
|
|
|
|
return {
|
2016-11-29 17:54:41 +01:00
|
|
|
privateKey: keys[0],
|
|
|
|
publicKey: keys[1]
|
2019-07-10 17:15:26 +01:00
|
|
|
}
|
2016-11-29 17:54:41 +01:00
|
|
|
}
|
|
|
|
|
2019-01-08 18:37:03 +00:00
|
|
|
exports.getRandomValues = randomBytes
|
2016-11-29 17:54:41 +01:00
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
exports.hashAndSign = async function (key, msg) {
|
2019-07-17 11:17:18 +01:00
|
|
|
const privateKey = await webcrypto.get().subtle.importKey(
|
2016-11-29 17:54:41 +01:00
|
|
|
'jwk',
|
|
|
|
key,
|
|
|
|
{
|
|
|
|
name: 'RSASSA-PKCS1-v1_5',
|
2019-01-03 08:13:07 -08:00
|
|
|
hash: { name: 'SHA-256' }
|
2016-11-29 17:54:41 +01:00
|
|
|
},
|
|
|
|
false,
|
|
|
|
['sign']
|
2019-07-10 17:15:26 +01:00
|
|
|
)
|
|
|
|
|
2019-07-17 11:17:18 +01:00
|
|
|
const sig = await webcrypto.get().subtle.sign(
|
2019-07-10 17:15:26 +01:00
|
|
|
{ name: 'RSASSA-PKCS1-v1_5' },
|
|
|
|
privateKey,
|
|
|
|
Uint8Array.from(msg)
|
|
|
|
)
|
|
|
|
|
|
|
|
return Buffer.from(sig)
|
2016-11-29 17:54:41 +01:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
exports.hashAndVerify = async function (key, sig, msg) {
|
2019-07-17 11:17:18 +01:00
|
|
|
const publicKey = await webcrypto.get().subtle.importKey(
|
2016-11-29 17:54:41 +01:00
|
|
|
'jwk',
|
|
|
|
key,
|
|
|
|
{
|
|
|
|
name: 'RSASSA-PKCS1-v1_5',
|
2019-01-03 08:13:07 -08:00
|
|
|
hash: { name: 'SHA-256' }
|
2016-11-29 17:54:41 +01:00
|
|
|
},
|
|
|
|
false,
|
|
|
|
['verify']
|
2019-07-10 17:15:26 +01:00
|
|
|
)
|
|
|
|
|
2019-07-17 11:17:18 +01:00
|
|
|
return webcrypto.get().subtle.verify(
|
2019-07-10 17:15:26 +01:00
|
|
|
{ name: 'RSASSA-PKCS1-v1_5' },
|
|
|
|
publicKey,
|
|
|
|
sig,
|
|
|
|
msg
|
|
|
|
)
|
2016-11-29 17:54:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function exportKey (pair) {
|
|
|
|
return Promise.all([
|
2019-07-17 11:17:18 +01:00
|
|
|
webcrypto.get().subtle.exportKey('jwk', pair.privateKey),
|
|
|
|
webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
|
2016-11-29 17:54:41 +01:00
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
function derivePublicFromPrivate (jwKey) {
|
2019-07-17 11:17:18 +01:00
|
|
|
return webcrypto.get().subtle.importKey(
|
2016-11-29 17:54:41 +01:00
|
|
|
'jwk',
|
|
|
|
{
|
|
|
|
kty: jwKey.kty,
|
|
|
|
n: jwKey.n,
|
2017-12-20 21:11:47 +13:00
|
|
|
e: jwKey.e
|
2016-11-29 17:54:41 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'RSASSA-PKCS1-v1_5',
|
2019-01-03 08:13:07 -08:00
|
|
|
hash: { name: 'SHA-256' }
|
2016-11-29 17:54:41 +01:00
|
|
|
},
|
|
|
|
true,
|
|
|
|
['verify']
|
|
|
|
)
|
|
|
|
}
|
2019-07-12 18:53:56 +02:00
|
|
|
|
2019-10-23 12:55:43 +02:00
|
|
|
/*
|
|
|
|
|
2019-10-23 12:57:23 +02:00
|
|
|
RSA encryption/decryption for the browser with webcrypto workarround
|
|
|
|
"bloody dark magic. webcrypto's why."
|
|
|
|
|
2019-10-23 12:55:43 +02:00
|
|
|
Explanation:
|
|
|
|
- Convert JWK to PEM
|
|
|
|
- Load PEM with nodeForge
|
2019-10-23 13:33:48 +02:00
|
|
|
- Convert msg buffer to nodeForge buffer: it's already uint8array, so do nothing
|
|
|
|
- Convert resulting nodeForge buffer to buffer: it returns a binary string, turn that into a uint8array
|
2019-10-23 12:55:43 +02:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const forge = require('node-forge')
|
|
|
|
const pki = forge.pki
|
|
|
|
const jwkToPem = require('pem-jwk').jwk2pem
|
|
|
|
function convertKey (key, pub, msg, handle) {
|
|
|
|
const pem = jwkToPem(key)
|
|
|
|
const fkey = pki[pub ? 'publicKeyFromPem' : 'privateKeyFromPem'](pem)
|
2019-10-23 13:33:48 +02:00
|
|
|
const fomsg = handle(Buffer.from(msg), fkey)
|
|
|
|
return Buffer.from(fomsg, 'binary')
|
2019-10-23 12:55:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-23 12:57:23 +02:00
|
|
|
exports.encrypt = function (key, msg) {
|
2019-10-23 12:55:43 +02:00
|
|
|
return convertKey(key, true, msg, (msg, key) => key.encrypt(msg))
|
2019-07-12 18:53:56 +02:00
|
|
|
}
|
|
|
|
|
2019-10-23 12:57:23 +02:00
|
|
|
exports.decrypt = function (key, msg) {
|
2019-10-23 12:55:43 +02:00
|
|
|
return convertKey(key, false, msg, (msg, key) => key.decrypt(msg))
|
2019-07-12 18:53:56 +02:00
|
|
|
}
|