From 6cd0c28b862524cb3b14e457e023c68a5b65659d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 24 Nov 2018 01:33:12 -0700 Subject: [PATCH] generate RSA keys with node 10.12+ --- README.md | 38 ++++++++++++++++++++++------ bin/rasha.js | 16 ++++++++++++ lib/rasha.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- test.sh | 13 +++++++++- 5 files changed, 131 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0213c43..dba6aac 100644 --- a/README.md +++ b/README.md @@ -5,27 +5,51 @@ Sponsored by [Root](https://therootcompany.com). Built for [ACME.js](https://git.coolaj86.com/coolaj86/acme.js) and [Greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js) -RSA tools. Lightweight. Zero Dependencies. Universal compatibility. - | ~550 lines of code | 3kb gzipped | 10kb minified | 18kb with comments | +RSA tools. Lightweight. Zero Dependencies. Universal compatibility. + +* [x] Fast and Easy RSA Key Generation * [x] PEM-to-JWK * [x] JWK-to-PEM * [x] SSH "pub" format +* [ ] ECDSA + * **Need EC or ECDSA tools?** Check out [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js) + +## Generate RSA Key + +Achieves the *fastest possible key generation* using node's native RSA bindings to OpenSSL, +then converts to JWK for ease-of-use. -This project is fully functional and tested (and the code is pretty clean). +``` +Rasha.generate({ format: 'jwk' }).then(function (keypair) { + console.log(keypair.private); + console.log(keypair.public); +}); +``` -It is considered to be complete, but if you find a bug please open an issue. +**options** -## Looking for ECDSA as well? +* `format` defaults to `'jwk'` + * `'pkcs1'` (traditional) + * `'pkcs8'` +* `modulusLength` defaults to 2048 (must not be lower) + * generally you shouldn't pick a larger key size - they're slow + * **2048** is more than sufficient + * 3072 is way, way overkill and takes a few seconds to generate + * 4096 can take about a minute to generate and is just plain wasteful -Check out [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js) +**advanced options** + +These options are provided for debugging and should not be used. + +* `publicExponent` defaults to 65537 (`0x10001`) ## PEM-to-JWK * [x] PKCS#1 (traditional) * [x] PKCS#8, SPKI/PKIX -* [x] 2048-bit, 4096-bit (and ostensibily all others) +* [x] 2048-bit, 3072-bit, 4096-bit (and ostensibily all others) * [x] SSH (RFC4716), (RFC 4716/SSH2) ```js diff --git a/bin/rasha.js b/bin/rasha.js index 0084031..de49619 100755 --- a/bin/rasha.js +++ b/bin/rasha.js @@ -9,6 +9,22 @@ var ASN1 = require('../lib/asn1.js'); var infile = process.argv[2]; var format = process.argv[3]; +if (!infile) { + infile = 'jwk'; +} + +if (-1 !== [ 'jwk', 'pem', 'json', 'der', 'pkcs1', 'pkcs8', 'spki' ].indexOf(infile)) { + console.log("Generating new key..."); + Rasha.generate({ + format: infile + , modulusLength: parseInt(format, 10) || 2048 + , encoding: parseInt(format, 10) ? null : format + }).then(function (key) { + console.log(key.private); + console.log(key.public); + }); + return; +} var key = fs.readFileSync(infile, 'ascii'); try { diff --git a/lib/rasha.js b/lib/rasha.js index 83dda7b..e37d3e3 100644 --- a/lib/rasha.js +++ b/lib/rasha.js @@ -7,6 +7,77 @@ var x509 = require('./x509.js'); var ASN1 = require('./asn1.js'); /*global Promise*/ +RSA.generate = function (opts) { + return Promise.resolve().then(function () { + var typ = 'rsa'; + var format = opts.format; + var encoding = opts.encoding; + var priv; + var pub; + + if (!format) { + format = 'jwk'; + } + if ('spki' === format || 'pkcs8' === format) { + format = 'pkcs8'; + pub = 'spki'; + } + + if ('pem' === format) { + format = 'pkcs1'; + encoding = 'pem'; + } else if ('der' === format) { + format = 'pkcs1'; + encoding = 'der'; + } + + if ('jwk' === format || 'json' === format) { + format = 'jwk'; + encoding = 'json'; + } else { + priv = format; + pub = pub || format; + } + + if (!encoding) { + encoding = 'pem'; + } + + if (priv) { + priv = { type: priv, format: encoding }; + pub = { type: pub, format: encoding }; + } else { + // jwk + priv = { type: 'pkcs1', format: 'pem' }; + pub = { type: 'pkcs1', format: 'pem' }; + } + + return new Promise(function (resolve, reject) { + return require('crypto').generateKeyPair(typ, { + modulusLength: opts.modulusLength || 2048 + , publicExponent: opts.publicExponent || 0x10001 + , privateKeyEncoding: priv + , publicKeyEncoding: pub + }, function (err, pubkey, privkey) { + if (err) { reject(err); } + resolve({ + private: privkey + , public: pubkey + }); + }); + }).then(function (keypair) { + if ('jwk' !== format) { + return keypair; + } + + return { + private: RSA.importSync({ pem: keypair.private, format: priv.format }) + , public: RSA.importSync({ pem: keypair.public, format: pub.format, public: true }) + }; + }); + }); +}; + RSA.importSync = function (opts) { if (!opts || !opts.pem || 'string' !== typeof opts.pem) { throw new Error("must pass { pem: pem } as a string"); diff --git a/package.json b/package.json index 07ddd0d..21a17c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rasha", - "version": "1.0.5", + "version": "1.1.0", "description": "💯 PEM-to-JWK and JWK-to-PEM for RSA keys in a lightweight, zero-dependency library focused on perfect universal compatibility.", "homepage": "https://git.coolaj86.com/coolaj86/rasha.js", "main": "index.js", diff --git a/test.sh b/test.sh index 5c0b072..c616058 100755 --- a/test.sh +++ b/test.sh @@ -121,6 +121,18 @@ rndkey() { pemtojwk "" jwktopem "" +echo "" +echo "testing node key generation" +node bin/rasha.js > /dev/null +node bin/rasha.js jwk > /dev/null +node bin/rasha.js json 2048 > /dev/null +node bin/rasha.js der > /dev/null +node bin/rasha.js pkcs8 der > /dev/null +node bin/rasha.js pem > /dev/null +node bin/rasha.js pkcs1 pem > /dev/null +node bin/rasha.js spki > /dev/null +echo "PASS" + echo "" echo "" echo "Re-running tests with random keys of varying sizes" @@ -140,7 +152,6 @@ echo "" echo "Note:" echo "Keys larger than 2048 have been tested and work, but are omitted from automated tests to save time." - rm fixtures/*.1.* echo ""