From d27060e508c004960b689536fccfe6f26aadc25a Mon Sep 17 00:00:00 2001 From: Tom J Date: Fri, 16 Aug 2019 11:11:04 +0000 Subject: [PATCH] Use native Node crypto for RSA key generation (#947) --- src/crypto/public_key/rsa.js | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 5ddbe1d2..5b704808 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -46,6 +46,22 @@ function promisifyIE11Op(keyObj, err) { return keyObj; } +/* eslint-disable no-invalid-this */ +const RSAPrivateKey = util.detectNode() ? require('asn1.js').define('RSAPrivateKey', function () { + this.seq().obj( // used for native NodeJS keygen + this.key('version').int(), // 0 + this.key('modulus').int(), // n + this.key('publicExponent').int(), // e + this.key('privateExponent').int(), // d + this.key('prime1').int(), // p + this.key('prime2').int(), // q + this.key('exponent1').int(), // dp + this.key('exponent2').int(), // dq + this.key('coefficient').int() // u + ); +}) : undefined; +/* eslint-enable no-invalid-this */ + export default { /** Create signature * @param {BN} m message @@ -142,7 +158,7 @@ export default { /** * Generate a new random private key B bits long with public exponent E. * - * When possible, webCrypto is used. Otherwise, primes are generated using + * When possible, webCrypto or nodeCrypto is used. Otherwise, primes are generated using * 40 rounds of the Miller-Rabin probabilistic random prime generation algorithm. * @see module:crypto/public_key/prime * @param {Integer} B RSA bit length @@ -156,6 +172,7 @@ export default { let key; E = new BN(E, 16); const webCrypto = util.getWebCryptoAll(); + const nodeCrypto = util.getNodeCrypto(); // Native RSA keygen using Web Crypto if (webCrypto) { @@ -207,6 +224,31 @@ export default { key.q = new BN(util.b64_to_Uint8Array(jwk.q)); key.u = key.p.invm(key.q); return key; + } else if (nodeCrypto && nodeCrypto.generateKeyPair && RSAPrivateKey) { + const opts = { + modulusLength: Number(B.toString(10)), + publicExponent: Number(E.toString(10)), + publicKeyEncoding: { type: 'pkcs1', format: 'der' }, + privateKeyEncoding: { type: 'pkcs1', format: 'der' } + }; + const prv = await new Promise((resolve, reject) => nodeCrypto.generateKeyPair('rsa', opts, (err, _, der) => { + if (err) { + reject(err); + } else { + resolve(RSAPrivateKey.decode(der, 'der')); + } + })); + return { + n: prv.modulus, + e: prv.publicExponent, + d: prv.privateExponent, + p: prv.prime1, + q: prv.prime2, + dp: prv.exponent1, + dq: prv.exponent2, + // re-compute `u` because PGP spec differs from DER spec, DER: `(inverse of q) mod p`, PGP: `(inverse of p) mod q` + u: prv.prime1.invm(prv.prime2) // PGP type of u + }; } // RSA keygen fallback using 40 iterations of the Miller-Rabin test