Compare commits
No commits in common. "14c24e3aeab5249d977ce7a0d540c3f47536aca2" and "f1e11f1be778df9f7a088bb1fa6de10b873c8418" have entirely different histories.
14c24e3aea
...
f1e11f1be7
11
app.js
11
app.js
|
@ -1,4 +1,3 @@
|
|||
/*global Promise*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
|
@ -6,9 +5,6 @@
|
|||
var Rasha = window.Rasha;
|
||||
var Eckles = window.Eckles;
|
||||
var x509 = window.x509;
|
||||
var CSR = window.CSR;
|
||||
var ACME = window.ACME;
|
||||
var accountStuff = {};
|
||||
|
||||
function $(sel) {
|
||||
return document.querySelector(sel);
|
||||
|
@ -53,10 +49,8 @@
|
|||
, namedCurve: $('input[name="ec-crv"]:checked').value
|
||||
, modulusLength: $('input[name="rsa-len"]:checked').value
|
||||
};
|
||||
var then = Date.now();
|
||||
console.log('opts', opts);
|
||||
Keypairs.generate(opts).then(function (results) {
|
||||
console.log("Key generation time:", (Date.now() - then) + "ms");
|
||||
var pubDer;
|
||||
var privDer;
|
||||
if (/EC/i.test(opts.kty)) {
|
||||
|
@ -105,13 +99,12 @@
|
|||
$$('input').map(function ($el) { $el.disabled = false; });
|
||||
$$('button').map(function ($el) { $el.disabled = false; });
|
||||
$('.js-toc-jwk').hidden = false;
|
||||
|
||||
$('.js-create-account').hidden = false;
|
||||
$('.js-create-csr').hidden = false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('.js-generate').hidden = false;
|
||||
$('.js-create-account').hidden = false;
|
||||
}
|
||||
|
||||
window.addEventListener('load', run);
|
||||
|
|
31
index.html
31
index.html
|
@ -34,21 +34,27 @@
|
|||
</div>
|
||||
<div class="js-ec-opts">
|
||||
<p>EC Options:</p>
|
||||
<label for="-crv2"><input type="radio" id="-crv2"
|
||||
name="ec-crv" value="P-256" checked>P-256</label>
|
||||
<label for="-crv3"><input type="radio" id="-crv3"
|
||||
name="ec-crv" value="P-384">P-384</label>
|
||||
<!-- label for="-crv5"><input type="radio" id="-crv5"
|
||||
name="ec-crv" value="P-521">P-521</label -->
|
||||
<input type="radio" id="-crv2"
|
||||
name="ec-crv" value="P-256" checked>
|
||||
<label for="-crv2">P-256</label>
|
||||
<input type="radio" id="-crv3"
|
||||
name="ec-crv" value="P-384">
|
||||
<label for="-crv3">P-384</label>
|
||||
<!-- input type="radio" id="-crv5"
|
||||
name="ec-crv" value="P-521">
|
||||
<label for="-crv5">P-521</label -->
|
||||
</div>
|
||||
<div class="js-rsa-opts" hidden>
|
||||
<p>RSA Options:</p>
|
||||
<label for="-modlen2"><input type="radio" id="-modlen2"
|
||||
name="rsa-len" value="2048" checked>2048</label>
|
||||
<label for="-modlen3"><input type="radio" id="-modlen3"
|
||||
name="rsa-len" value="3072">3072</label>
|
||||
<label for="-modlen5"><input type="radio" id="-modlen5"
|
||||
name="rsa-len" value="4096">4096</label>
|
||||
<input type="radio" id="-modlen2"
|
||||
name="rsa-len" value="2048" checked>
|
||||
<label for="-modlen2">2048</label>
|
||||
<input type="radio" id="-modlen3"
|
||||
name="rsa-len" value="3072">
|
||||
<label for="-modlen3">3072</label>
|
||||
<input type="radio" id="-modlen5"
|
||||
name="rsa-len" value="4096">
|
||||
<label for="-modlen5">4096</label>
|
||||
</div>
|
||||
<button class="js-generate" hidden>Generate</button>
|
||||
</form>
|
||||
|
@ -94,6 +100,7 @@
|
|||
<script src="./lib/ecdsa.js"></script>
|
||||
<script src="./lib/rsa.js"></script>
|
||||
<script src="./lib/keypairs.js"></script>
|
||||
<script src="./lib/acme.js"></script>
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -125,7 +125,7 @@ PEM.parseBlock = PEM.parseBlock || function (str) {
|
|||
var der = str.split(/\n/).filter(function (line) {
|
||||
return !/-----/.test(line);
|
||||
}).join('');
|
||||
return { bytes: Enc.base64ToBuf(der) };
|
||||
return { der: Enc.base64ToBuf(der) };
|
||||
};
|
||||
|
||||
Enc.base64ToBuf = function (b64) {
|
||||
|
|
|
@ -66,11 +66,8 @@ Enc.numToHex = function (d) {
|
|||
};
|
||||
|
||||
Enc.bufToUrlBase64 = function (u8) {
|
||||
return Enc.base64ToUrlBase64(Enc.bufToBase64(u8));
|
||||
};
|
||||
|
||||
Enc.base64ToUrlBase64 = function (str) {
|
||||
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||
return Enc.bufToBase64(u8)
|
||||
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||
};
|
||||
|
||||
Enc.bufToBase64 = function (u8) {
|
||||
|
@ -113,8 +110,6 @@ Enc.binToHex = function (bin) {
|
|||
return h;
|
||||
}).join('');
|
||||
};
|
||||
// TODO are there any nuance differences here?
|
||||
Enc.utf8ToHex = Enc.binToHex;
|
||||
|
||||
Enc.hexToBase64 = function (hex) {
|
||||
return btoa(Enc.hexToBin(hex));
|
||||
|
|
|
@ -180,12 +180,24 @@ Keypairs.signJws = function (opts) {
|
|||
var msg = protected64 + '.' + payload64;
|
||||
|
||||
return Keypairs._sign(opts, msg).then(function (buf) {
|
||||
/*
|
||||
* This will come back into play for CSRs, but not for JOSE
|
||||
if ('EC' === opts.jwk.kty) {
|
||||
// ECDSA JWT signatures differ from "normal" ECDSA signatures
|
||||
// https://tools.ietf.org/html/rfc7518#section-3.4
|
||||
binsig = convertIfEcdsa(binsig);
|
||||
}
|
||||
*/
|
||||
var signedMsg = {
|
||||
protected: protected64
|
||||
, payload: payload64
|
||||
, signature: Enc.bufToUrlBase64(buf)
|
||||
};
|
||||
|
||||
console.log('Signed Base64 Msg:');
|
||||
console.log(JSON.stringify(signedMsg, null, 2));
|
||||
|
||||
console.log('msg:', msg);
|
||||
return signedMsg;
|
||||
});
|
||||
}
|
||||
|
@ -200,6 +212,40 @@ Keypairs.signJws = function (opts) {
|
|||
}
|
||||
});
|
||||
};
|
||||
Keypairs._convertIfEcdsa = function (binsig) {
|
||||
// should have asn1 sequence header of 0x30
|
||||
if (0x30 !== binsig[0]) { throw new Error("Impossible EC SHA head marker"); }
|
||||
var index = 2; // first ecdsa "R" header byte
|
||||
var len = binsig[1];
|
||||
var lenlen = 0;
|
||||
// Seek length of length if length is greater than 127 (i.e. two 512-bit / 64-byte R and S values)
|
||||
if (0x80 & len) {
|
||||
lenlen = len - 0x80; // should be exactly 1
|
||||
len = binsig[2]; // should be <= 130 (two 64-bit SHA-512s, plus padding)
|
||||
index += lenlen;
|
||||
}
|
||||
// should be of BigInt type
|
||||
if (0x02 !== binsig[index]) { throw new Error("Impossible EC SHA R marker"); }
|
||||
index += 1;
|
||||
|
||||
var rlen = binsig[index];
|
||||
var bits = 32;
|
||||
if (rlen > 49) {
|
||||
bits = 64;
|
||||
} else if (rlen > 33) {
|
||||
bits = 48;
|
||||
}
|
||||
var r = binsig.slice(index + 1, index + 1 + rlen).toString('hex');
|
||||
var slen = binsig[index + 1 + rlen + 1]; // skip header and read length
|
||||
var s = binsig.slice(index + 1 + rlen + 1 + 1).toString('hex');
|
||||
if (2 *slen !== s.length) { throw new Error("Impossible EC SHA S length"); }
|
||||
// There may be one byte of padding on either
|
||||
while (r.length < 2*bits) { r = '00' + r; }
|
||||
while (s.length < 2*bits) { s = '00' + s; }
|
||||
if (2*(bits+1) === r.length) { r = r.slice(2); }
|
||||
if (2*(bits+1) === s.length) { s = s.slice(2); }
|
||||
return Enc.hexToBuf(r + s);
|
||||
};
|
||||
|
||||
Keypairs._sign = function (opts, payload) {
|
||||
return Keypairs._import(opts).then(function (privkey) {
|
||||
|
@ -213,14 +259,9 @@ Keypairs._sign = function (opts, payload) {
|
|||
, privkey
|
||||
, payload
|
||||
).then(function (signature) {
|
||||
signature = new Uint8Array(signature); // ArrayBuffer -> u8
|
||||
// This will come back into play for CSRs, but not for JOSE
|
||||
if ('EC' === opts.jwk.kty && /x509|asn1/i.test(opts.format)) {
|
||||
return Keypairs._ecdsaJoseSigToAsn1Sig(signature);
|
||||
} else {
|
||||
// jose/jws/jwt
|
||||
return signature;
|
||||
}
|
||||
// convert buffer to urlsafe base64
|
||||
//return Enc.bufToUrlBase64(new Uint8Array(signature));
|
||||
return new Uint8Array(signature);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -246,6 +287,7 @@ Keypairs._getName = function (opts) {
|
|||
return 'RSASSA-PKCS1-v1_5';
|
||||
}
|
||||
};
|
||||
|
||||
Keypairs._import = function (opts) {
|
||||
return Promise.resolve().then(function () {
|
||||
var ops;
|
||||
|
@ -259,6 +301,7 @@ Keypairs._import = function (opts) {
|
|||
opts.jwk.ext = true;
|
||||
opts.jwk.key_ops = ops;
|
||||
|
||||
console.log('jwk', opts.jwk);
|
||||
return window.crypto.subtle.importKey(
|
||||
"jwk"
|
||||
, opts.jwk
|
||||
|
@ -273,30 +316,6 @@ Keypairs._import = function (opts) {
|
|||
});
|
||||
});
|
||||
};
|
||||
// ECDSA JOSE / JWS / JWT signatures differ from "normal" ASN1/X509 ECDSA signatures
|
||||
// https://tools.ietf.org/html/rfc7518#section-3.4
|
||||
Keypairs._ecdsaJoseSigToAsn1Sig = function (bufsig) {
|
||||
// it's easier to do the manipulation in the browser with an array
|
||||
bufsig = Array.from(bufsig);
|
||||
var hlen = bufsig.length / 2; // should be even
|
||||
var r = bufsig.slice(0, hlen);
|
||||
var s = bufsig.slice(hlen);
|
||||
// unpad positive ints less than 32 bytes wide
|
||||
while (!r[0]) { r = r.slice(1); }
|
||||
while (!s[0]) { s = s.slice(1); }
|
||||
// pad (or re-pad) ambiguously non-negative BigInts, up to 33 bytes wide
|
||||
if (0x80 & r[0]) { r.unshift(0); }
|
||||
if (0x80 & s[0]) { s.unshift(0); }
|
||||
|
||||
var len = 2 + r.length + 2 + s.length;
|
||||
var head = [0x30];
|
||||
// hard code 0x80 + 1 because it won't be longer than
|
||||
// two SHA512 plus two pad bytes (130 bytes <= 256)
|
||||
if (len >= 0x80) { head.push(0x81); }
|
||||
head.push(len);
|
||||
|
||||
return Uint8Array.from(head.concat([0x02, r.length], r, [0x02, s.length], s));
|
||||
};
|
||||
|
||||
function setTime(time) {
|
||||
if ('number' === typeof time) { return time; }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
(function (exports) {
|
||||
'use strict';
|
||||
|
||||
var x509 = exports.x509 = {};
|
||||
var ASN1 = exports.ASN1;
|
||||
var Enc = exports.Enc;
|
||||
|
|
Loading…
Reference in New Issue