forked from coolaj86/bluecrypt-keypairs.js
Merge branch 'master' of lastlink/bluecrypt-keypairs.js into master
This commit is contained in:
commit
b2174e3923
142
app.js
142
app.js
|
@ -1,68 +1,98 @@
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Keypairs = window.Keypairs;
|
var Keypairs = window.Keypairs;
|
||||||
|
|
||||||
function $(sel) {
|
function $(sel) {
|
||||||
return document.querySelector(sel);
|
return document.querySelector(sel);
|
||||||
}
|
}
|
||||||
function $$(sel) {
|
function $$(sel) {
|
||||||
return Array.prototype.slice.call(document.querySelectorAll(sel));
|
return Array.prototype.slice.call(document.querySelectorAll(sel));
|
||||||
}
|
}
|
||||||
|
|
||||||
function run() {
|
function run() {
|
||||||
console.log('hello');
|
console.log('hello');
|
||||||
|
|
||||||
// Show different options for ECDSA vs RSA
|
// Show different options for ECDSA vs RSA
|
||||||
$$('input[name="kty"]').forEach(function ($el) {
|
$$('input[name="kty"]').forEach(function ($el) {
|
||||||
$el.addEventListener('change', function (ev) {
|
$el.addEventListener('change', function (ev) {
|
||||||
console.log(this);
|
console.log(this);
|
||||||
console.log(ev);
|
console.log(ev);
|
||||||
if ("RSA" === ev.target.value) {
|
if ("RSA" === ev.target.value) {
|
||||||
$('.js-rsa-opts').hidden = false;
|
$('.js-rsa-opts').hidden = false;
|
||||||
$('.js-ec-opts').hidden = true;
|
$('.js-ec-opts').hidden = true;
|
||||||
} else {
|
} else {
|
||||||
$('.js-rsa-opts').hidden = true;
|
$('.js-rsa-opts').hidden = true;
|
||||||
$('.js-ec-opts').hidden = false;
|
$('.js-ec-opts').hidden = false;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Generate a key on submit
|
// Generate a key on submit
|
||||||
$('form.js-keygen').addEventListener('submit', function (ev) {
|
$('form.js-keygen').addEventListener('submit', function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
$('.js-loading').hidden = false;
|
$('.js-loading').hidden = false;
|
||||||
$('.js-jwk').hidden = true;
|
$('.js-jwk').hidden = true;
|
||||||
$$('input').map(function ($el) { $el.disabled = true; });
|
$('.js-toc-der-public').hidden = true;
|
||||||
$$('button').map(function ($el) { $el.disabled = true; });
|
$('.js-toc-pem-public').hidden = true;
|
||||||
var opts = {
|
$('.js-toc-der-private').hidden = true;
|
||||||
kty: $('input[name="kty"]:checked').value
|
$('.js-toc-pem-private').hidden = true;
|
||||||
, namedCurve: $('input[name="ec-crv"]:checked').value
|
$$('input').map(function ($el) { $el.disabled = true; });
|
||||||
, modulusLength: $('input[name="rsa-len"]:checked').value
|
$$('button').map(function ($el) { $el.disabled = true; });
|
||||||
};
|
var opts = {
|
||||||
console.log('opts', opts);
|
kty: $('input[name="kty"]:checked').value
|
||||||
Keypairs.generate(opts).then(function (results) {
|
, namedCurve: $('input[name="ec-crv"]:checked').value
|
||||||
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
|
, modulusLength: $('input[name="rsa-len"]:checked').value
|
||||||
//
|
};
|
||||||
$('.js-loading').hidden = true;
|
console.log('opts', opts);
|
||||||
$('.js-jwk').hidden = false;
|
Keypairs.generate(opts).then(function (results) {
|
||||||
$$('input').map(function ($el) { $el.disabled = false; });
|
var der_public, der_private;
|
||||||
$$('button').map(function ($el) { $el.disabled = false; });
|
if (opts.kty == 'EC') {
|
||||||
$('.js-toc-jwk').hidden = false;
|
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;
|
||||||
|
} 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;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.js-der-public').innerText = der_public;
|
||||||
|
$('.js-toc-der-public').hidden = false;
|
||||||
|
$('.js-der-private').innerText = der_private;
|
||||||
|
$('.js-toc-der-private').hidden = false;
|
||||||
|
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
|
||||||
|
$('.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;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
$('form.js-acme-account').addEventListener('submit', function (ev) {
|
$('form.js-acme-account').addEventListener('submit', function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
$('.js-loading').hidden = false;
|
$('.js-loading').hidden = false;
|
||||||
ACME.accounts.create
|
ACME.accounts.create
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.js-generate').hidden = false;
|
$('.js-generate').hidden = false;
|
||||||
$('.js-create-account').hidden = false;
|
$('.js-create-account').hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', run);
|
window.addEventListener('load', run);
|
||||||
}());
|
}());
|
||||||
|
|
34
index.html
34
index.html
|
@ -1,6 +1,20 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>BlueCrypt</title>
|
<title>BlueCrypt</title>
|
||||||
|
<style>
|
||||||
|
textarea {
|
||||||
|
width: 42em;
|
||||||
|
height: 10em;
|
||||||
|
}
|
||||||
|
/* 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 */
|
||||||
|
word-wrap: break-word; /* IE */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BlueCrypt for the Browser</h1>
|
<h1>BlueCrypt for the Browser</h1>
|
||||||
|
@ -58,6 +72,22 @@
|
||||||
<summary>JWK Keypair</summary>
|
<summary>JWK Keypair</summary>
|
||||||
<pre><code class="js-jwk"> </code></pre>
|
<pre><code class="js-jwk"> </code></pre>
|
||||||
</details>
|
</details>
|
||||||
|
<details class="js-toc-der-private" hidden>
|
||||||
|
<summary>DER Private Binary</summary>
|
||||||
|
<pre><code class="js-der-private"> </code></pre>
|
||||||
|
</details>
|
||||||
|
<details class="js-toc-der-public" hidden>
|
||||||
|
<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>
|
||||||
|
<details class="js-toc-pem-public" hidden>
|
||||||
|
<summary>PEM Public (base64-encoded DER)</summary>
|
||||||
|
<pre><code class="js-input-pem-public" ></code></pre>
|
||||||
|
</details>
|
||||||
<details class="js-toc-acme-account-request" hidden>
|
<details class="js-toc-acme-account-request" hidden>
|
||||||
<summary>ACME Account Request</summary>
|
<summary>ACME Account Request</summary>
|
||||||
<pre><code class="js-acme-account-request"> </code></pre>
|
<pre><code class="js-acme-account-request"> </code></pre>
|
||||||
|
@ -66,8 +96,10 @@
|
||||||
<summary>ACME Account Response</summary>
|
<summary>ACME Account Response</summary>
|
||||||
<pre><code class="js-acme-account-response"> </code></pre>
|
<pre><code class="js-acme-account-response"> </code></pre>
|
||||||
</details>
|
</details>
|
||||||
|
<script src="./lib/bluecrypt-encoding.js"></script>
|
||||||
<script src="./lib/ecdsa.js"></script>
|
<script src="./lib/ecdsa.js"></script>
|
||||||
|
<script src="./lib/asn1-packer.js"></script>
|
||||||
|
<script src="./lib/x509.js"></script>
|
||||||
<script src="./lib/rsa.js"></script>
|
<script src="./lib/rsa.js"></script>
|
||||||
<script src="./lib/keypairs.js"></script>
|
<script src="./lib/keypairs.js"></script>
|
||||||
<script src="./lib/acme.js"></script>
|
<script src="./lib/acme.js"></script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function (exports) {
|
(function (exports) {
|
||||||
|
|
||||||
var Enc = exports.BluecryptEncoding = {};
|
var Enc = exports.Enc = {};
|
||||||
|
|
||||||
Enc.bufToBin = function (buf) {
|
Enc.bufToBin = function (buf) {
|
||||||
var bin = '';
|
var bin = '';
|
||||||
|
|
53
lib/ecdsa.js
53
lib/ecdsa.js
|
@ -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.
|
// 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.
|
// 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) {
|
EC.neuter = function (opts) {
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Keypairs = exports.Keypairs = {};
|
var Keypairs = exports.Keypairs = {};
|
||||||
var Rasha = exports.Rasha || require('rasha');
|
var Rasha = exports.Rasha;
|
||||||
var Eckles = exports.Eckles || require('eckles');
|
var Eckles = exports.Eckles;
|
||||||
var Enc = exports.Enc || {};
|
var Enc = exports.Enc || {};
|
||||||
|
|
||||||
Keypairs._stance = "We take the stance that if you're knowledgeable enough to"
|
Keypairs._stance = "We take the stance that if you're knowledgeable enough to"
|
||||||
|
@ -34,10 +34,12 @@ Keypairs.generate = function (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.
|
* 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.
|
||||||
|
*/
|
||||||
Keypairs.neuter = Keypairs._neuter = function (opts) {
|
Keypairs.neuter = Keypairs._neuter = function (opts) {
|
||||||
// trying to find the best balance of an immutable copy with custom attributes
|
/** trying to find the best balance of an immutable copy with custom attributes */
|
||||||
var jwk = {};
|
var jwk = {};
|
||||||
Object.keys(opts.jwk).forEach(function (k) {
|
Object.keys(opts.jwk).forEach(function (k) {
|
||||||
if ('undefined' === typeof opts.jwk[k]) { return; }
|
if ('undefined' === typeof opts.jwk[k]) { return; }
|
||||||
|
@ -61,7 +63,7 @@ Keypairs.thumbprint = function (opts) {
|
||||||
Keypairs.publish = function (opts) {
|
Keypairs.publish = function (opts) {
|
||||||
if ('object' !== typeof opts.jwk || !opts.jwk.kty) { throw new Error("invalid jwk: " + JSON.stringify(opts.jwk)); }
|
if ('object' !== typeof opts.jwk || !opts.jwk.kty) { throw new Error("invalid jwk: " + JSON.stringify(opts.jwk)); }
|
||||||
|
|
||||||
// returns a copy
|
/** returns a copy */
|
||||||
var jwk = Keypairs.neuter(opts);
|
var jwk = Keypairs.neuter(opts);
|
||||||
|
|
||||||
if (jwk.exp) {
|
if (jwk.exp) {
|
||||||
|
|
61
lib/rsa.js
61
lib/rsa.js
|
@ -3,6 +3,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var RSA = exports.Rasha = {};
|
var RSA = exports.Rasha = {};
|
||||||
|
var x509 = exports.x509;
|
||||||
if ('undefined' !== typeof module) { module.exports = RSA; }
|
if ('undefined' !== typeof module) { module.exports = RSA; }
|
||||||
var Enc = {};
|
var Enc = {};
|
||||||
var textEncoder = new TextEncoder();
|
var textEncoder = new TextEncoder();
|
||||||
|
@ -106,6 +107,66 @@ 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';
|
||||||
|
} else {
|
||||||
|
throw new Error("options.format must be 'spki', 'pkcs1', or 'ssh' for public RSA keys, not ("
|
||||||
|
+ typeof format + ") " + format);
|
||||||
|
}
|
||||||
|
} 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) });
|
||||||
|
} 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 {
|
||||||
|
throw new Error("Sanity Error: reached unreachable code block with format: " + format);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RSA.pack = function (opts) {
|
||||||
|
// wrapped in a promise for API compatibility
|
||||||
|
// with the forthcoming browser version
|
||||||
|
// (and potential future native node capability)
|
||||||
|
return Promise.resolve().then(function () {
|
||||||
|
return RSA.export(opts);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Enc.bufToUrlBase64 = function (u8) {
|
Enc.bufToUrlBase64 = function (u8) {
|
||||||
return Enc.bufToBase64(u8)
|
return Enc.bufToBase64(u8)
|
||||||
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||||
|
|
69
lib/x509.js
69
lib/x509.js
|
@ -55,6 +55,27 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
, 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.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
|
x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
|
||||||
var index = 24 + (OBJ_ID_EC.length / 2);
|
var index = 24 + (OBJ_ID_EC.length / 2);
|
||||||
var len = 32;
|
var len = 32;
|
||||||
|
@ -128,7 +149,7 @@
|
||||||
var x = Enc.base64ToHex(jwk.x);
|
var x = Enc.base64ToHex(jwk.x);
|
||||||
var y = Enc.base64ToHex(jwk.y);
|
var y = Enc.base64ToHex(jwk.y);
|
||||||
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
|
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
|
||||||
return Enc.hexToUint8(
|
return Enc.hexToBuf(
|
||||||
ASN1('30'
|
ASN1('30'
|
||||||
, ASN1.UInt('01')
|
, ASN1.UInt('01')
|
||||||
, ASN1('04', d)
|
, ASN1('04', d)
|
||||||
|
@ -136,12 +157,54 @@
|
||||||
, ASN1('A1', ASN1.BitStr('04' + x + y)))
|
, ASN1('A1', ASN1.BitStr('04' + x + y)))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* take a private jwk and creates a der from it
|
||||||
|
* @param {*} jwk
|
||||||
|
*/
|
||||||
x509.packPkcs8 = function (jwk) {
|
x509.packPkcs8 = function (jwk) {
|
||||||
|
if (jwk.kty == 'RSA') {
|
||||||
|
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))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
var d = Enc.base64ToHex(jwk.d);
|
var d = Enc.base64ToHex(jwk.d);
|
||||||
var x = Enc.base64ToHex(jwk.x);
|
var x = Enc.base64ToHex(jwk.x);
|
||||||
var y = Enc.base64ToHex(jwk.y);
|
var y = Enc.base64ToHex(jwk.y);
|
||||||
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
|
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
|
||||||
return Enc.hexToUint8(
|
return Enc.hexToBuf(
|
||||||
ASN1('30'
|
ASN1('30'
|
||||||
, ASN1.UInt('00')
|
, ASN1.UInt('00')
|
||||||
, ASN1('30'
|
, ASN1('30'
|
||||||
|
@ -159,7 +222,7 @@
|
||||||
var x = Enc.base64ToHex(jwk.x);
|
var x = Enc.base64ToHex(jwk.x);
|
||||||
var y = Enc.base64ToHex(jwk.y);
|
var y = Enc.base64ToHex(jwk.y);
|
||||||
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
|
var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
|
||||||
return Enc.hexToUint8(
|
return Enc.hexToBuf(
|
||||||
ASN1('30'
|
ASN1('30'
|
||||||
, ASN1('30'
|
, ASN1('30'
|
||||||
, OBJ_ID_EC_PUB
|
, OBJ_ID_EC_PUB
|
||||||
|
|
Loading…
Reference in New Issue