display all ASN.1/x509 formats
This commit is contained in:
parent
b2174e3923
commit
76621560cb
69
app.js
69
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;
|
||||
|
|
26
index.html
26
index.html
|
@ -80,13 +80,25 @@
|
|||
<summary>DER Public Binary</summary>
|
||||
<pre><code class="js-der-public"> </code></pre>
|
||||
</details>
|
||||
<details class="js-toc-pem-private" hidden>
|
||||
<summary>PEM Private (base64-encoded DER)</summary>
|
||||
<pre><code class="js-input-pem-private" ></code></pre>
|
||||
<details class="js-toc-pem js-toc-pem-pkcs1-private" hidden>
|
||||
<summary>PEM Private (base64-encoded PKCS1 DER)</summary>
|
||||
<pre><code class="js-input-pem-pkcs1-private" ></code></pre>
|
||||
</details>
|
||||
<details class="js-toc-pem-public" hidden>
|
||||
<summary>PEM Public (base64-encoded DER)</summary>
|
||||
<pre><code class="js-input-pem-public" ></code></pre>
|
||||
<details class="js-toc-pem js-toc-pem-sec1-private" hidden>
|
||||
<summary>PEM Private (base64-encoded SEC1 DER)</summary>
|
||||
<pre><code class="js-input-pem-sec1-private" ></code></pre>
|
||||
</details>
|
||||
<details class="js-toc-pem js-toc-pem-pkcs8-private" hidden>
|
||||
<summary>PEM Private (base64-encoded PKCS8 DER)</summary>
|
||||
<pre><code class="js-input-pem-pkcs8-private" ></code></pre>
|
||||
</details>
|
||||
<details class="js-toc-pem js-toc-pem-pkcs1-public" hidden>
|
||||
<summary>PEM Public (base64-encoded PKCS1 DER)</summary>
|
||||
<pre><code class="js-input-pem-pkcs1-public" ></code></pre>
|
||||
</details>
|
||||
<details class="js-toc-pem js-toc-pem-spki-public" hidden>
|
||||
<summary>PEM Public (base64-encoded SPKI/PKIX DER)</summary>
|
||||
<pre><code class="js-input-pem-spki-public" ></code></pre>
|
||||
</details>
|
||||
<details class="js-toc-acme-account-request" hidden>
|
||||
<summary>ACME Account Request</summary>
|
||||
|
@ -97,9 +109,9 @@
|
|||
<pre><code class="js-acme-account-response"> </code></pre>
|
||||
</details>
|
||||
<script src="./lib/bluecrypt-encoding.js"></script>
|
||||
<script src="./lib/ecdsa.js"></script>
|
||||
<script src="./lib/asn1-packer.js"></script>
|
||||
<script src="./lib/x509.js"></script>
|
||||
<script src="./lib/ecdsa.js"></script>
|
||||
<script src="./lib/rsa.js"></script>
|
||||
<script src="./lib/keypairs.js"></script>
|
||||
<script src="./lib/acme.js"></script>
|
||||
|
|
87
lib/ecdsa.js
87
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();
|
||||
|
||||
|
@ -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 () {
|
||||
|
|
92
lib/rsa.js
92
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
|
||||
|
|
45
lib/x509.js
45
lib/x509.js
|
@ -162,7 +162,7 @@
|
|||
* @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;
|
||||
|
|
Loading…
Reference in New Issue