v1.2.5: fix multiple bugs with conversion

This commit is contained in:
AJ ONeal 2019-03-05 12:23:50 -07:00
parent a5236f6c2f
commit ad312edfa6
2 changed files with 69 additions and 43 deletions

View File

@ -13,7 +13,7 @@ var Keypairs = require('../');
var pkg = require('../package.json'); var pkg = require('../package.json');
var args = process.argv.slice(2); var args = process.argv.slice(2);
var opts = { jwks: [], jwts: [], jwss: [], payloads: [], names: [], filenames: [], files: [], pems: [] }; var opts = { keys: [], jwts: [], jwss: [], payloads: [], names: [], filenames: [], files: [] };
var conflicts = { var conflicts = {
'namedCurve': 'modulusLength' 'namedCurve': 'modulusLength'
, 'public': 'private' , 'public': 'private'
@ -187,13 +187,7 @@ args.forEach(function (arg) {
} }
// Possibly the output file // Possibly the output file
if (!opts.extra1) { if (opts.names.length < 3) {
opts.extra1 = arg;
opts.names.push({ taken: false, name: arg });
return;
}
if (!opts.extra2) {
opts.extra2 = arg;
opts.names.push({ taken: false, name: arg }); opts.names.push({ taken: false, name: arg });
return; return;
} }
@ -220,7 +214,7 @@ function guess(txt, filename) {
try { try {
var json = JSON.parse(txt); var json = JSON.parse(txt);
if (-1 !== [ 'RSA', 'EC' ].indexOf(json.kty)) { if (-1 !== [ 'RSA', 'EC' ].indexOf(json.kty)) {
opts.jwks.push({ jwk: json, filename: filename }); opts.keys.push({ raw: txt, jwk: json, filename: filename });
return true; return true;
} else if (json.signature && json.payload && (json.header || json.protected)) { } else if (json.signature && json.payload && (json.header || json.protected)) {
opts.jwss.push(json); opts.jwss.push(json);
@ -231,15 +225,15 @@ function guess(txt, filename) {
} }
} catch(e) { } catch(e) {
try { try {
var pem = Eckles.importSync({ pem: txt }); var jwk = Eckles.importSync({ pem: txt });
// pem._string = txt; // pem._string = txt;
opts.pems.push(pem); opts.keys.push({ jwk: jwk, pem: true, raw: txt });
return true; return true;
} catch(e) { } catch(e) {
try { try {
var pem = Rasha.importSync({ pem: txt }); var jwk = Rasha.importSync({ pem: txt });
// pem._string = txt; // pem._string = txt;
opts.pems.push(pem); opts.keys.push({ jwk: jwk, pem: true, raw: txt });
return true; return true;
} catch(e) { } catch(e) {
// ignore // ignore
@ -348,8 +342,8 @@ if ('sign' === opts.action) {
function readKeypair() { function readKeypair() {
// note that the jwk may be a string // note that the jwk may be a string
var jwkopts = opts.jwks.shift(); var keyopts = opts.keys.shift();
var jwk = jwkopts && jwkopts.jwk; var jwk = keyopts && keyopts.jwk;
if (!jwk) { if (!jwk) {
console.error("no keys could be parsed from the given arguments"); console.error("no keys could be parsed from the given arguments");
console.error(opts.names.map(function (t) { return t.name; })); console.error(opts.names.map(function (t) { return t.name; }));
@ -358,13 +352,13 @@ function readKeypair() {
} }
// omit the primary private key from the list of actual (or soon-to-be) files // omit the primary private key from the list of actual (or soon-to-be) files
if (jwkopts.filename) { if (keyopts.filename) {
opts.names = opts.names.filter(function (name) { opts.names = opts.names.filter(function (name) {
return name.name !== jwkopts.filename; return name.name !== keyopts.filename;
}); });
} }
var pair = { private: null, public: null }; var pair = { private: null, public: null, pem: keyopts.pem, raw: keyopts.raw };
if (jwk.d) { if (jwk.d) {
pair.private = jwk; pair.private = jwk;
} }
@ -372,69 +366,91 @@ function readKeypair() {
return pair; return pair;
} }
// Note: some of the conditions can be factored out
// this was all built in high-speed iterative during the 3ams+
function convertKeypair(pair) { function convertKeypair(pair) {
//var pair = readKeypair(); //var pair = readKeypair();
var ps = []; var ps = [];
if (pair.private && !opts.public) { // if it's private only, or if it's not public-only, produce the private key
if ((!opts.privEncoding || 'json' === opts.privEncoding) && (!opts.privFormat || 'jwk' === opts.privFormat)) { if (pair.private || !opts.public) {
// if it came from pem (or is explicitly json), it should go to jwk
// otherwise, if it came from jwk, it should go to pem
if (((!opts.privEncoding && pair.pem) || 'json' === opts.privEncoding)
&& ((!opts.privFormat && pair.pem) || 'jwk' === opts.privFormat)) {
ps.push(Promise.resolve(pair.private)); ps.push(Promise.resolve(pair.private));
} else { } else {
ps.push(Keypairs.export({ jwk: pair.private, format: opts.privFormat, encoding: opts.privEncoding })); ps.push(Keypairs.export({ jwk: pair.private, format: opts.privFormat, encoding: opts.privEncoding }));
} }
} }
// if it's not private key only, we want to produce the public key
if (!opts.private) { if (!opts.private) {
if (opts.public) { if (opts.public) {
// if it's public-only the ambigious options will fall to the private key
// so we need to fix that
if (!opts.pubFormat) { opts.pubFormat = opts.privFormat; } if (!opts.pubFormat) { opts.pubFormat = opts.privFormat; }
if (!opts.pubEncoding) { opts.pubEncoding = opts.privEncoding; } if (!opts.pubEncoding) { opts.pubEncoding = opts.privEncoding; }
} }
if ((!opts.pubEncoding || 'json' === opts.pubEncoding) && (!opts.pubFormat || 'jwk' === opts.pubFormat)) { // same as above - swap formats by default
if (((!opts.pubEncoding && pair.pem) || 'json' === opts.pubEncoding)
&& ((!opts.pubFormat && pair.pem) || 'jwk' === opts.pubFormat)) {
ps.push(Promise.resolve(pair.public)); ps.push(Promise.resolve(pair.public));
} else { } else {
ps.push(Keypairs.export({ jwk: pair.public, format: opts.pubFormat, encoding: opts.pubEncoding, public: true })); ps.push(Keypairs.export({ jwk: pair.public, format: opts.pubFormat, encoding: opts.pubEncoding, public: true }));
} }
} }
return Promise.all(ps).then(function (arr) {
// only use the first key return Promise.all(ps).then(function (exported) {
var key = convert(0, opts.public); // start with the first key, annotating if it should be public
var index = 0;
var key = stringifyIfJson(index, opts.public);
// re: opts.names
// if we're only doing the public key we can end early
// (if the source key was from a file and was in opts.names,
// we're safe here because we already removed it earlier)
if (opts.public) { if (opts.public) {
// end early
if (opts.names.length) { if (opts.names.length) {
writeFile(opts.names[0].name, key, !opts.public); // todo make pub/priv param consistent, not flip-flop writeFile(opts.names[index].name, key, !opts.public);
} else { } else {
console.warn(key + "\n"); // output public keys to stderr
printPublic(key);
} }
// end <-- we're not outputting other keys
return; return;
} }
// private key stuff // private key stuff
if (opts.names.length) { if (opts.names.length >= 1) {
writeFile(opts.names[0].name, key, true); writeFile(opts.names[index].name, key, true);
} else { } else {
console.info(key + "\n"); printPrivate(key);
} }
// pub key stuff // pub key stuff
if (!opts.private) { // we have to output the private key,
if (opts.names.length >= 2) { // but the public key can be derived at any time
writeFile(opts.names[1].name, key, false); // so we don't need to put the same noise to the screen
} else { if (!opts.private && opts.names.length >= 2) {
console.warn(key + "\n"); index = 1;
} key = stringifyIfJson(index, false);
writeFile(opts.names[index].name, key, false);
} }
return pair; return pair;
function convert(i, pub) { function stringifyIfJson(i, pub) {
if (arr[i].kty) { if (exported[i].kty) {
if (pub) { if (pub) {
if (opts.expiresAt) { arr[i].exp = opts.expiresAt; } if (opts.expiresAt) { exported[i].exp = opts.expiresAt; }
arr[i].use = "sig"; exported[i].use = "sig";
} }
arr[i] = JSON.stringify(arr[i]); exported[i] = JSON.stringify(exported[i]);
} }
return arr[i]; return exported[i];
} }
}); });
} }
@ -445,6 +461,7 @@ function genKeypair() {
, modulusLength: opts.modulusLength , modulusLength: opts.modulusLength
, namedCurve: opts.namedCurve , namedCurve: opts.namedCurve
}).then(function (pair) { }).then(function (pair) {
// always generate as jwk by default
var ps = []; var ps = [];
if ((!opts.privEncoding || 'json' === opts.privEncoding) && (!opts.privFormat || 'jwk' === opts.privFormat)) { if ((!opts.privEncoding || 'json' === opts.privEncoding) && (!opts.privFormat || 'jwk' === opts.privFormat)) {
ps.push(Promise.resolve(pair.private)); ps.push(Promise.resolve(pair.private));
@ -488,8 +505,10 @@ function writeFile(name, key, priv) {
overwrite = opts.overwrite; overwrite = opts.overwrite;
if (!opts.overwrite) { if (!opts.overwrite) {
if (priv) { if (priv) {
// output private keys to stdout
console.info(key + "\n"); console.info(key + "\n");
} else { } else {
// output public keys to stderr
console.warn(key + "\n"); console.warn(key + "\n");
} }
console.error("'" + name + "' exists! force overwrite with 'overwrite'"); console.error("'" + name + "' exists! force overwrite with 'overwrite'");
@ -583,3 +602,10 @@ function decodeJwt(jwt) {
, signature: parts[2] //Buffer.from(parts[2], 'base64') , signature: parts[2] //Buffer.from(parts[2], 'base64')
}; };
} }
function printPrivate(key) {
console.info(key + "\n");
}
function printPublic(key) {
console.warn(key + "\n");
}

View File

@ -1,6 +1,6 @@
{ {
"name": "keypairs", "name": "keypairs",
"version": "1.2.2", "version": "1.2.5",
"description": "Lightweight RSA/ECDSA keypair generation and JWK <-> PEM", "description": "Lightweight RSA/ECDSA keypair generation and JWK <-> PEM",
"main": "keypairs.js", "main": "keypairs.js",
"files": [ "files": [