diff --git a/app.js b/app.js index 1738a2c..097d710 100644 --- a/app.js +++ b/app.js @@ -44,15 +44,19 @@ function run() { console.log('opts', opts); Keypairs.generate(opts).then(function (results) { var der = x509.packPkcs8(results.private); - console.log(der) - // Pem.encode(x509.packPkcs8(privateJwk)) + var pem = Eckles.export({jwk:results.private}) + $('.js-jwk').innerText = JSON.stringify(results, null, 2); + $('.js-der').innerText = JSON.stringify(der, null, 2); + $('.js-input-pem').innerText = pem; // $('.js-loading').hidden = true; $('.js-jwk').hidden = false; $$('input').map(function ($el) { $el.disabled = false; }); $$('button').map(function ($el) { $el.disabled = false; }); $('.js-toc-jwk').hidden = false; + $('.js-toc-der').hidden = false; + $('.js-toc-pem').hidden = false; }); }); diff --git a/index.html b/index.html index 0d35e9a..d4fec55 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,12 @@
+
+
diff --git a/lib/ecdsa.js b/lib/ecdsa.js
index dedc4fb..deb2c86 100644
--- a/lib/ecdsa.js
+++ b/lib/ecdsa.js
@@ -32,7 +32,7 @@ EC.generate = function (opts) {
+ " Please choose either 'P-256' or 'P-384'. "
+ EC._stance));
}
-
+
var extractable = true;
return window.crypto.subtle.generateKey(
wcOpts
@@ -51,6 +51,59 @@ EC.generate = function (opts) {
});
};
+EC.export = function (opts) {
+ if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
+ throw new Error("must pass { jwk: jwk } as a JSON object");
+ }
+ var jwk = JSON.parse(JSON.stringify(opts.jwk));
+ var format = opts.format;
+ if (opts.public || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
+ jwk.d = null;
+ }
+ if ('EC' !== jwk.kty) {
+ throw new Error("options.jwk.kty must be 'EC' for EC keys");
+ }
+ if (!jwk.d) {
+ if (!format || -1 !== [ 'spki', 'pkix' ].indexOf(format)) {
+ format = 'spki';
+ } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
+ format = 'ssh';
+ } else {
+ throw new Error("options.format must be 'spki' or 'ssh' for public EC keys, not ("
+ + typeof format + ") " + format);
+ }
+ } else {
+ if (!format || 'sec1' === format) {
+ format = 'sec1';
+ } else if ('pkcs8' !== format) {
+ throw new Error("options.format must be 'sec1' or 'pkcs8' for private EC keys, not '" + format + "'");
+ }
+ }
+ if (-1 === [ 'P-256', 'P-384' ].indexOf(jwk.crv)) {
+ throw new Error("options.jwk.crv must be either P-256 or P-384 for EC keys, not '" + jwk.crv + "'");
+ }
+ if (!jwk.y) {
+ throw new Error("options.jwk.y must be a urlsafe base64-encoded either P-256 or P-384");
+ }
+
+ if ('sec1' === format) {
+ return PEM.packBlock({ type: "EC PRIVATE KEY", bytes: x509.packSec1(jwk) });
+ } else if ('pkcs8' === format) {
+ return PEM.packBlock({ type: "PRIVATE KEY", bytes: x509.packPkcs8(jwk) });
+ } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
+ return PEM.packBlock({ type: "PUBLIC KEY", bytes: x509.packSpki(jwk) });
+ } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
+ return SSH.packSsh(jwk);
+ } else {
+ throw new Error("Sanity Error: reached unreachable code block with format: " + format);
+ }
+};
+EC.pack = function (opts) {
+ return Promise.resolve().then(function () {
+ return EC.exportSync(opts);
+ });
+};
+
// Chopping off the private parts is now part of the public API.
// I thought it sounded a little too crude at first, but it really is the best name in every possible way.
EC.neuter = function (opts) {