diff --git a/app.js b/app.js
index f2066dd..22e293a 100644
--- a/app.js
+++ b/app.js
@@ -2,6 +2,9 @@
'use strict';
var Keypairs = window.Keypairs;
+ var Rasha = window.Rasha;
+ var Eckles = window.Eckles;
+ var x509 = window.x509;
function $(sel) {
return document.querySelector(sel);
@@ -35,9 +38,10 @@
$('.js-loading').hidden = false;
$('.js-jwk').hidden = true;
$('.js-toc-der-public').hidden = true;
- $('.js-toc-pem-public').hidden = true;
$('.js-toc-der-private').hidden = true;
- $('.js-toc-pem-private').hidden = true;
+ $$('.js-toc-pem').forEach(function ($el) {
+ $el.hidden = true;
+ });
$$('input').map(function ($el) { $el.disabled = true; });
$$('button').map(function ($el) { $el.disabled = true; });
var opts = {
@@ -47,32 +51,47 @@
};
console.log('opts', opts);
Keypairs.generate(opts).then(function (results) {
- var der_public, der_private;
- if (opts.kty == 'EC') {
- der_public = x509.packSpki(results.public);
- der_private = x509.packPkcs8(results.private);
- var pem_private = Eckles.export({ jwk: results.private })
- var pem_public = Eckles.export({ jwk: results.public, public: true })
- $('.js-input-pem-public').innerText = pem_public;
- $('.js-toc-pem-public').hidden = false;
- $('.js-input-pem-private').innerText = pem_private;
- $('.js-toc-pem-private').hidden = false;
+ var pubDer;
+ var privDer;
+ if (/EC/i.test(opts.kty)) {
+ privDer = x509.packPkcs8(results.private);
+ pubDer = x509.packSpki(results.public);
+ Eckles.export({ jwk: results.private, format: 'sec1' }).then(function (pem) {
+ $('.js-input-pem-sec1-private').innerText = pem;
+ $('.js-toc-pem-sec1-private').hidden = false;
+ });
+ Eckles.export({ jwk: results.private, format: 'pkcs8' }).then(function (pem) {
+ $('.js-input-pem-pkcs8-private').innerText = pem;
+ $('.js-toc-pem-pkcs8-private').hidden = false;
+ });
+ Eckles.export({ jwk: results.public, public: true }).then(function (pem) {
+ $('.js-input-pem-spki-public').innerText = pem;
+ $('.js-toc-pem-spki-public').hidden = false;
+ });
} else {
- der_private = x509.packPkcs8(results.private);
- der_public = x509.packPkcs8(results.public);
- Rasha.pack({ jwk: results.private }).then(function (pem) {
- $('.js-input-pem-private').innerText = pem;
- $('.js-toc-pem-private').hidden = false;
- })
- Rasha.pack({ jwk: results.public }).then(function (pem) {
- $('.js-input-pem-public').innerText = pem;
- $('.js-toc-pem-public').hidden = false;
- })
+ privDer = x509.packPkcs8(results.private);
+ pubDer = x509.packSpki(results.public);
+ Rasha.export({ jwk: results.private, format: 'pkcs1' }).then(function (pem) {
+ $('.js-input-pem-pkcs1-private').innerText = pem;
+ $('.js-toc-pem-pkcs1-private').hidden = false;
+ });
+ Rasha.export({ jwk: results.private, format: 'pkcs8' }).then(function (pem) {
+ $('.js-input-pem-pkcs8-private').innerText = pem;
+ $('.js-toc-pem-pkcs8-private').hidden = false;
+ });
+ Rasha.export({ jwk: results.public, format: 'pkcs1' }).then(function (pem) {
+ $('.js-input-pem-pkcs1-public').innerText = pem;
+ $('.js-toc-pem-pkcs1-public').hidden = false;
+ });
+ Rasha.export({ jwk: results.public, format: 'spki' }).then(function (pem) {
+ $('.js-input-pem-spki-public').innerText = pem;
+ $('.js-toc-pem-spki-public').hidden = false;
+ });
}
- $('.js-der-public').innerText = der_public;
+ $('.js-der-public').innerText = pubDer;
$('.js-toc-der-public').hidden = false;
- $('.js-der-private').innerText = der_private;
+ $('.js-der-private').innerText = privDer;
$('.js-toc-der-private').hidden = false;
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
$('.js-loading').hidden = true;
@@ -87,7 +106,7 @@
ev.preventDefault();
ev.stopPropagation();
$('.js-loading').hidden = false;
- ACME.accounts.create
+ //ACME.accounts.create
});
$('.js-generate').hidden = false;
diff --git a/index.html b/index.html
index 047936b..013e3b4 100644
--- a/index.html
+++ b/index.html
@@ -8,10 +8,10 @@
}
/* need to word wrap the binary no space der */
.js-der-public, .js-der-private{
- white-space: pre-wrap; /* CSS3 */
- white-space: -moz-pre-wrap; /* Firefox */
- white-space: -pre-wrap; /* Opera <7 */
- white-space: -o-pre-wrap; /* Opera 7 */
+ white-space: pre-wrap; /* CSS3 */
+ white-space: -moz-pre-wrap; /* Firefox */
+ white-space: -pre-wrap; /* Opera <7 */
+ white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* IE */
}
@@ -80,13 +80,25 @@
DER Public Binary
-
- PEM Private (base64-encoded DER)
-
+
+ PEM Private (base64-encoded PKCS1 DER)
+
-
- PEM Public (base64-encoded DER)
-
+
+ PEM Private (base64-encoded SEC1 DER)
+
+
+
+ PEM Private (base64-encoded PKCS8 DER)
+
+
+
+ PEM Public (base64-encoded PKCS1 DER)
+
+
+
+ PEM Public (base64-encoded SPKI/PKIX DER)
+
ACME Account Request
@@ -97,9 +109,9 @@
-
+
diff --git a/lib/ecdsa.js b/lib/ecdsa.js
index deb2c86..eff9794 100644
--- a/lib/ecdsa.js
+++ b/lib/ecdsa.js
@@ -3,7 +3,10 @@
'use strict';
var EC = exports.Eckles = {};
+var x509 = exports.x509;
if ('undefined' !== typeof module) { module.exports = EC; }
+var PEM = exports.PEM;
+var SSH = exports.SSH;
var Enc = {};
var textEncoder = new TextEncoder();
@@ -32,7 +35,7 @@ EC.generate = function (opts) {
+ " Please choose either 'P-256' or 'P-384'. "
+ EC._stance));
}
-
+
var extractable = true;
return window.crypto.subtle.generateKey(
wcOpts
@@ -52,51 +55,53 @@ 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';
+ return Promise.resolve().then(function () {
+ 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 {
- throw new Error("options.format must be 'spki' or 'ssh' for public EC keys, not ("
- + typeof format + ") " + format);
+ 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 + "'");
+ }
}
- } 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 (-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);
- }
+ 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 () {
diff --git a/lib/rsa.js b/lib/rsa.js
index 17ceccb..5d3ce26 100644
--- a/lib/rsa.js
+++ b/lib/rsa.js
@@ -5,6 +5,8 @@
var RSA = exports.Rasha = {};
var x509 = exports.x509;
if ('undefined' !== typeof module) { module.exports = RSA; }
+var PEM = exports.PEM;
+var SSH = exports.SSH;
var Enc = {};
var textEncoder = new TextEncoder();
@@ -108,55 +110,57 @@ RSA.thumbprint = function (opts) {
};
RSA.export = function (opts) {
- if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
- throw new Error("must pass { jwk: jwk }");
- }
- var jwk = JSON.parse(JSON.stringify(opts.jwk));
- var format = opts.format;
- var pub = opts.public;
- if (pub || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
- jwk = RSA.nueter(jwk);
- }
- if ('RSA' !== jwk.kty) {
- throw new Error("options.jwk.kty must be 'RSA' for RSA keys");
- }
- if (!jwk.p) {
- // TODO test for n and e
- pub = true;
- if (!format || 'pkcs1' === format) {
- format = 'pkcs1';
- } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
- format = 'spki';
- } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
- format = 'ssh';
+ return Promise.resolve().then(function () {
+ if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
+ throw new Error("must pass { jwk: jwk }");
+ }
+ var jwk = JSON.parse(JSON.stringify(opts.jwk));
+ var format = opts.format;
+ var pub = opts.public;
+ if (pub || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
+ jwk = RSA.neuter({ jwk: jwk });
+ }
+ if ('RSA' !== jwk.kty) {
+ throw new Error("options.jwk.kty must be 'RSA' for RSA keys");
+ }
+ if (!jwk.p) {
+ // TODO test for n and e
+ pub = true;
+ if (!format || 'pkcs1' === format) {
+ format = 'pkcs1';
+ } else if (-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', 'pkcs1', or 'ssh' for public RSA keys, not ("
+ + typeof format + ") " + format);
+ }
} else {
- throw new Error("options.format must be 'spki', 'pkcs1', or 'ssh' for public RSA keys, not ("
- + typeof format + ") " + format);
+ // TODO test for all necessary keys (d, p, q ...)
+ if (!format || 'pkcs1' === format) {
+ format = 'pkcs1';
+ } else if ('pkcs8' !== format) {
+ throw new Error("options.format must be 'pkcs1' or 'pkcs8' for private RSA keys");
+ }
}
- } else {
- // TODO test for all necessary keys (d, p, q ...)
- if (!format || 'pkcs1' === format) {
- format = 'pkcs1';
- } else if ('pkcs8' !== format) {
- throw new Error("options.format must be 'pkcs1' or 'pkcs8' for private RSA keys");
- }
- }
- if ('pkcs1' === format) {
- if (jwk.d) {
- return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) });
+ if ('pkcs1' === format) {
+ if (jwk.d) {
+ return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) });
+ } else {
+ return PEM.packBlock({ type: "RSA PUBLIC KEY", bytes: x509.packPkcs1(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.pack({ jwk: jwk, comment: opts.comment });
} else {
- return PEM.packBlock({ type: "RSA PUBLIC KEY", bytes: x509.packPkcs1(jwk) });
+ throw new Error("Sanity Error: reached unreachable code block with format: " + format);
}
- } 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.pack({ jwk: jwk, comment: opts.comment });
- } else {
- throw new Error("Sanity Error: reached unreachable code block with format: " + format);
- }
+ });
};
RSA.pack = function (opts) {
// wrapped in a promise for API compatibility
diff --git a/lib/x509.js b/lib/x509.js
index f1ac559..901bb36 100644
--- a/lib/x509.js
+++ b/lib/x509.js
@@ -58,11 +58,11 @@
x509.packPkcs1 = function (jwk) {
var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
-
+
if (!jwk.d) {
return Enc.hexToBuf(ASN1('30', n, e));
}
-
+
return Enc.hexToBuf(ASN1('30'
, ASN1.UInt('00')
, n
@@ -159,10 +159,10 @@
};
/**
* take a private jwk and creates a der from it
- * @param {*} jwk
+ * @param {*} jwk
*/
x509.packPkcs8 = function (jwk) {
- if (jwk.kty == 'RSA') {
+ if ('RSA' === jwk.kty) {
if (!jwk.d) {
// Public RSA
return Enc.hexToBuf(ASN1('30'
@@ -219,6 +219,49 @@
);
};
x509.packSpki = function (jwk) {
+ if (/EC/i.test(jwk.kty)) {
+ return x509.packSpkiEc(jwk);
+ }
+ return x509.packSpkiRsa(jwk);
+ };
+ x509.packSpkiRsa = function (jwk) {
+ if (!jwk.d) {
+ // Public RSA
+ return Enc.hexToBuf(ASN1('30'
+ , ASN1('30'
+ , ASN1('06', '2a864886f70d010101')
+ , ASN1('05')
+ )
+ , ASN1.BitStr(ASN1('30'
+ , ASN1.UInt(Enc.base64ToHex(jwk.n))
+ , ASN1.UInt(Enc.base64ToHex(jwk.e))
+ ))
+ ));
+ }
+
+ // Private RSA
+ return Enc.hexToBuf(ASN1('30'
+ , ASN1.UInt('00')
+ , ASN1('30'
+ , ASN1('06', '2a864886f70d010101')
+ , ASN1('05')
+ )
+ , ASN1('04'
+ , ASN1('30'
+ , ASN1.UInt('00')
+ , ASN1.UInt(Enc.base64ToHex(jwk.n))
+ , ASN1.UInt(Enc.base64ToHex(jwk.e))
+ , ASN1.UInt(Enc.base64ToHex(jwk.d))
+ , ASN1.UInt(Enc.base64ToHex(jwk.p))
+ , ASN1.UInt(Enc.base64ToHex(jwk.q))
+ , ASN1.UInt(Enc.base64ToHex(jwk.dp))
+ , ASN1.UInt(Enc.base64ToHex(jwk.dq))
+ , ASN1.UInt(Enc.base64ToHex(jwk.qi))
+ )
+ )
+ ));
+};
+ x509.packSpkiEc = function (jwk) {
var x = Enc.base64ToHex(jwk.x);
var y = Enc.base64ToHex(jwk.y);
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;