|
@@ -13,7 +13,7 @@ var Keypairs = require('../');
|
13
|
13
|
var pkg = require('../package.json');
|
14
|
14
|
|
15
|
15
|
var args = process.argv.slice(2);
|
16
|
|
-var opts = { jwks: [], jwts: [], jwss: [], payloads: [], names: [], filenames: [], files: [], pems: [] };
|
|
16
|
+var opts = { keys: [], jwts: [], jwss: [], payloads: [], names: [], filenames: [], files: [] };
|
17
|
17
|
var conflicts = {
|
18
|
18
|
'namedCurve': 'modulusLength'
|
19
|
19
|
, 'public': 'private'
|
|
@@ -187,13 +187,7 @@ args.forEach(function (arg) {
|
187
|
187
|
}
|
188
|
188
|
|
189
|
189
|
// Possibly the output file
|
190
|
|
- if (!opts.extra1) {
|
191
|
|
- opts.extra1 = arg;
|
192
|
|
- opts.names.push({ taken: false, name: arg });
|
193
|
|
- return;
|
194
|
|
- }
|
195
|
|
- if (!opts.extra2) {
|
196
|
|
- opts.extra2 = arg;
|
|
190
|
+ if (opts.names.length < 3) {
|
197
|
191
|
opts.names.push({ taken: false, name: arg });
|
198
|
192
|
return;
|
199
|
193
|
}
|
|
@@ -220,7 +214,7 @@ function guess(txt, filename) {
|
220
|
214
|
try {
|
221
|
215
|
var json = JSON.parse(txt);
|
222
|
216
|
if (-1 !== [ 'RSA', 'EC' ].indexOf(json.kty)) {
|
223
|
|
- opts.jwks.push({ jwk: json, filename: filename });
|
|
217
|
+ opts.keys.push({ raw: txt, jwk: json, filename: filename });
|
224
|
218
|
return true;
|
225
|
219
|
} else if (json.signature && json.payload && (json.header || json.protected)) {
|
226
|
220
|
opts.jwss.push(json);
|
|
@@ -231,15 +225,15 @@ function guess(txt, filename) {
|
231
|
225
|
}
|
232
|
226
|
} catch(e) {
|
233
|
227
|
try {
|
234
|
|
- var pem = Eckles.importSync({ pem: txt });
|
|
228
|
+ var jwk = Eckles.importSync({ pem: txt });
|
235
|
229
|
// pem._string = txt;
|
236
|
|
- opts.pems.push(pem);
|
|
230
|
+ opts.keys.push({ jwk: jwk, pem: true, raw: txt });
|
237
|
231
|
return true;
|
238
|
232
|
} catch(e) {
|
239
|
233
|
try {
|
240
|
|
- var pem = Rasha.importSync({ pem: txt });
|
|
234
|
+ var jwk = Rasha.importSync({ pem: txt });
|
241
|
235
|
// pem._string = txt;
|
242
|
|
- opts.pems.push(pem);
|
|
236
|
+ opts.keys.push({ jwk: jwk, pem: true, raw: txt });
|
243
|
237
|
return true;
|
244
|
238
|
} catch(e) {
|
245
|
239
|
// ignore
|
|
@@ -348,8 +342,8 @@ if ('sign' === opts.action) {
|
348
|
342
|
|
349
|
343
|
function readKeypair() {
|
350
|
344
|
// note that the jwk may be a string
|
351
|
|
- var jwkopts = opts.jwks.shift();
|
352
|
|
- var jwk = jwkopts && jwkopts.jwk;
|
|
345
|
+ var keyopts = opts.keys.shift();
|
|
346
|
+ var jwk = keyopts && keyopts.jwk;
|
353
|
347
|
if (!jwk) {
|
354
|
348
|
console.error("no keys could be parsed from the given arguments");
|
355
|
349
|
console.error(opts.names.map(function (t) { return t.name; }));
|
|
@@ -358,13 +352,13 @@ function readKeypair() {
|
358
|
352
|
}
|
359
|
353
|
|
360
|
354
|
// omit the primary private key from the list of actual (or soon-to-be) files
|
361
|
|
- if (jwkopts.filename) {
|
|
355
|
+ if (keyopts.filename) {
|
362
|
356
|
opts.names = opts.names.filter(function (name) {
|
363
|
|
- return name.name !== jwkopts.filename;
|
|
357
|
+ return name.name !== keyopts.filename;
|
364
|
358
|
});
|
365
|
359
|
}
|
366
|
360
|
|
367
|
|
- var pair = { private: null, public: null };
|
|
361
|
+ var pair = { private: null, public: null, pem: keyopts.pem, raw: keyopts.raw };
|
368
|
362
|
if (jwk.d) {
|
369
|
363
|
pair.private = jwk;
|
370
|
364
|
}
|
|
@@ -372,69 +366,91 @@ function readKeypair() {
|
372
|
366
|
return pair;
|
373
|
367
|
}
|
374
|
368
|
|
|
369
|
+// Note: some of the conditions can be factored out
|
|
370
|
+// this was all built in high-speed iterative during the 3ams+
|
375
|
371
|
function convertKeypair(pair) {
|
376
|
372
|
//var pair = readKeypair();
|
377
|
373
|
|
378
|
374
|
var ps = [];
|
379
|
|
- if (pair.private && !opts.public) {
|
380
|
|
- if ((!opts.privEncoding || 'json' === opts.privEncoding) && (!opts.privFormat || 'jwk' === opts.privFormat)) {
|
|
375
|
+ // if it's private only, or if it's not public-only, produce the private key
|
|
376
|
+ if (pair.private || !opts.public) {
|
|
377
|
+ // if it came from pem (or is explicitly json), it should go to jwk
|
|
378
|
+ // otherwise, if it came from jwk, it should go to pem
|
|
379
|
+ if (((!opts.privEncoding && pair.pem) || 'json' === opts.privEncoding)
|
|
380
|
+ && ((!opts.privFormat && pair.pem) || 'jwk' === opts.privFormat)) {
|
381
|
381
|
ps.push(Promise.resolve(pair.private));
|
382
|
382
|
} else {
|
383
|
383
|
ps.push(Keypairs.export({ jwk: pair.private, format: opts.privFormat, encoding: opts.privEncoding }));
|
384
|
384
|
}
|
385
|
385
|
}
|
|
386
|
+
|
|
387
|
+ // if it's not private key only, we want to produce the public key
|
386
|
388
|
if (!opts.private) {
|
387
|
389
|
if (opts.public) {
|
|
390
|
+ // if it's public-only the ambigious options will fall to the private key
|
|
391
|
+ // so we need to fix that
|
388
|
392
|
if (!opts.pubFormat) { opts.pubFormat = opts.privFormat; }
|
389
|
393
|
if (!opts.pubEncoding) { opts.pubEncoding = opts.privEncoding; }
|
390
|
394
|
}
|
391
|
395
|
|
392
|
|
- if ((!opts.pubEncoding || 'json' === opts.pubEncoding) && (!opts.pubFormat || 'jwk' === opts.pubFormat)) {
|
|
396
|
+ // same as above - swap formats by default
|
|
397
|
+ if (((!opts.pubEncoding && pair.pem) || 'json' === opts.pubEncoding)
|
|
398
|
+ && ((!opts.pubFormat && pair.pem) || 'jwk' === opts.pubFormat)) {
|
393
|
399
|
ps.push(Promise.resolve(pair.public));
|
394
|
400
|
} else {
|
395
|
401
|
ps.push(Keypairs.export({ jwk: pair.public, format: opts.pubFormat, encoding: opts.pubEncoding, public: true }));
|
396
|
402
|
}
|
397
|
403
|
}
|
398
|
|
- return Promise.all(ps).then(function (arr) {
|
399
|
|
- // only use the first key
|
400
|
|
- var key = convert(0, opts.public);
|
|
404
|
+
|
|
405
|
+ return Promise.all(ps).then(function (exported) {
|
|
406
|
+ // start with the first key, annotating if it should be public
|
|
407
|
+ var index = 0;
|
|
408
|
+ var key = stringifyIfJson(index, opts.public);
|
|
409
|
+
|
|
410
|
+ // re: opts.names
|
|
411
|
+ // if we're only doing the public key we can end early
|
|
412
|
+ // (if the source key was from a file and was in opts.names,
|
|
413
|
+ // we're safe here because we already removed it earlier)
|
|
414
|
+
|
401
|
415
|
if (opts.public) {
|
402
|
|
- // end early
|
403
|
416
|
if (opts.names.length) {
|
404
|
|
- writeFile(opts.names[0].name, key, !opts.public); // todo make pub/priv param consistent, not flip-flop
|
|
417
|
+ writeFile(opts.names[index].name, key, !opts.public);
|
405
|
418
|
} else {
|
406
|
|
- console.warn(key + "\n");
|
|
419
|
+ // output public keys to stderr
|
|
420
|
+ printPublic(key);
|
407
|
421
|
}
|
|
422
|
+ // end <-- we're not outputting other keys
|
408
|
423
|
return;
|
409
|
424
|
}
|
410
|
425
|
|
411
|
426
|
// private key stuff
|
412
|
|
- if (opts.names.length) {
|
413
|
|
- writeFile(opts.names[0].name, key, true);
|
|
427
|
+ if (opts.names.length >= 1) {
|
|
428
|
+ writeFile(opts.names[index].name, key, true);
|
414
|
429
|
} else {
|
415
|
|
- console.info(key + "\n");
|
|
430
|
+ printPrivate(key);
|
416
|
431
|
}
|
417
|
432
|
|
418
|
433
|
// pub key stuff
|
419
|
|
- if (!opts.private) {
|
420
|
|
- if (opts.names.length >= 2) {
|
421
|
|
- writeFile(opts.names[1].name, key, false);
|
422
|
|
- } else {
|
423
|
|
- console.warn(key + "\n");
|
424
|
|
- }
|
|
434
|
+ // we have to output the private key,
|
|
435
|
+ // but the public key can be derived at any time
|
|
436
|
+ // so we don't need to put the same noise to the screen
|
|
437
|
+ if (!opts.private && opts.names.length >= 2) {
|
|
438
|
+ index = 1;
|
|
439
|
+ key = stringifyIfJson(index, false);
|
|
440
|
+ writeFile(opts.names[index].name, key, false);
|
425
|
441
|
}
|
426
|
442
|
|
427
|
443
|
return pair;
|
428
|
444
|
|
429
|
|
- function convert(i, pub) {
|
430
|
|
- if (arr[i].kty) {
|
|
445
|
+ function stringifyIfJson(i, pub) {
|
|
446
|
+ if (exported[i].kty) {
|
431
|
447
|
if (pub) {
|
432
|
|
- if (opts.expiresAt) { arr[i].exp = opts.expiresAt; }
|
433
|
|
- arr[i].use = "sig";
|
|
448
|
+ if (opts.expiresAt) { exported[i].exp = opts.expiresAt; }
|
|
449
|
+ exported[i].use = "sig";
|
434
|
450
|
}
|
435
|
|
- arr[i] = JSON.stringify(arr[i]);
|
|
451
|
+ exported[i] = JSON.stringify(exported[i]);
|
436
|
452
|
}
|
437
|
|
- return arr[i];
|
|
453
|
+ return exported[i];
|
438
|
454
|
}
|
439
|
455
|
});
|
440
|
456
|
}
|
|
@@ -445,6 +461,7 @@ function genKeypair() {
|
445
|
461
|
, modulusLength: opts.modulusLength
|
446
|
462
|
, namedCurve: opts.namedCurve
|
447
|
463
|
}).then(function (pair) {
|
|
464
|
+ // always generate as jwk by default
|
448
|
465
|
var ps = [];
|
449
|
466
|
if ((!opts.privEncoding || 'json' === opts.privEncoding) && (!opts.privFormat || 'jwk' === opts.privFormat)) {
|
450
|
467
|
ps.push(Promise.resolve(pair.private));
|
|
@@ -488,8 +505,10 @@ function writeFile(name, key, priv) {
|
488
|
505
|
overwrite = opts.overwrite;
|
489
|
506
|
if (!opts.overwrite) {
|
490
|
507
|
if (priv) {
|
|
508
|
+ // output private keys to stdout
|
491
|
509
|
console.info(key + "\n");
|
492
|
510
|
} else {
|
|
511
|
+ // output public keys to stderr
|
493
|
512
|
console.warn(key + "\n");
|
494
|
513
|
}
|
495
|
514
|
console.error("'" + name + "' exists! force overwrite with 'overwrite'");
|
|
@@ -583,3 +602,10 @@ function decodeJwt(jwt) {
|
583
|
602
|
, signature: parts[2] //Buffer.from(parts[2], 'base64')
|
584
|
603
|
};
|
585
|
604
|
}
|
|
605
|
+
|
|
606
|
+function printPrivate(key) {
|
|
607
|
+ console.info(key + "\n");
|
|
608
|
+}
|
|
609
|
+function printPublic(key) {
|
|
610
|
+ console.warn(key + "\n");
|
|
611
|
+}
|