AJ ONeal преди 5 години
родител
ревизия
510a367135
  1. 13
      index.html
  2. 2
      install.sh
  3. 81
      js/app.js
  4. 67
      js/bacme.js

13
index.html

@ -120,19 +120,22 @@
<button type="submit">Next</button>
</form>
<!-- Step 4 Process Challanges -->
<!-- Step 5 Get Certs -->
<form class="js-acme-form js-acme-form-download">
<label>privkey.pem</label>
<textarea>-</textarea>
<textarea class="js-privkey">-</textarea>
<label>fullchain.pem</label>
<textarea>-</textarea>
<textarea class="js-fullchain">-</textarea>
<!--
TODO
<label>cert.pem</label>
<textarea>-</textarea>
<textarea class="js-cert">-</textarea>
<label>chain.pem</label>
<textarea>-</textarea>
<textarea class="js-chain">-</textarea>
-->
<button type="button">Download SSL Certificates</button>

2
install.sh

@ -10,5 +10,5 @@ popd
mkdir -p js/browser-csr/v1.0.0-alpha/
pushd js/browser-csr/v1.0.0-alpha/
wget -c https://git.coolaj86.com/coolaj86/browser-csr.js/raw/commit/c513a862a4e016794da800f0c2eec858b80837ab/csr.js
wget -c https://git.coolaj86.com/coolaj86/browser-csr.js/raw/commit/01cdc0e91b5bf03f12e1b25b4129e3cde927987c/csr.js
popd

81
js/app.js

@ -30,7 +30,9 @@
});
});
function updateChallengeType() {
var input = this || $qs('.js-acme-challenge-type');
var input = this || Array.prototype.filter.call(
$qsa('.js-acme-challenge-type'), function ($el) { return $el.checked; }
)[0];
console.log('ch type radio:', input.value);
$qs('.js-acme-table-wildcard').hidden = true;
$qs('.js-acme-table-http-01').hidden = true;
@ -200,7 +202,6 @@
, dnsAnswer: dnsAuth.answer
};
obj[c.type].push(data);
console.log('');
console.log('CHALLENGE');
console.log(claim);
@ -258,9 +259,6 @@
$qs('.js-acme-form-challenges').hidden = false;
};
steps[3].submit = function () {
// for now just show the next page immediately (its a spinner)
console.log("MAGIC STEP NUMBER is:", i);
var chType;
Array.prototype.some.call($qsa('.js-acme-challenge-type'), function ($el) {
if ($el.checked) {
@ -283,6 +281,7 @@
});
});
});
console.log("INFO.challenges !!!!!", info.challenges);
var results = [];
function nextChallenge() {
@ -294,6 +293,7 @@
});
}
// for now just show the next page immediately (its a spinner)
steps[i]();
return nextChallenge().then(function (results) {
console.log('challenge status:', results);
@ -366,6 +366,7 @@
return p.then(function (_serverJwk) {
serverJwk = _serverJwk;
info.serverJwk = serverJwk;
// { serverJwk, domains }
return BACME.orders.generateCsr({
serverJwk: serverJwk
@ -373,7 +374,7 @@
return ident.value;
})
}).then(function (csrweb64) {
return BACME.order.finalize({
return BACME.orders.finalize({
csr: csrweb64
, jwk: info.jwk
, finalizeUrl: info.finalizeUrl
@ -384,7 +385,7 @@
return new Promise(function (resolve) {
setTimeout(resolve, 1000);
}).then(function () {
return BACME.order.check({ orderUrl: info.orderUrl });
return BACME.orders.check({ orderUrl: info.orderUrl });
}).then(function (reply) {
if ('processing' === reply) {
return checkCert();
@ -395,10 +396,74 @@
return checkCert();
}).then(function (reply) {
return BACME.order.receive({ certificateUrl: reply.certificate });
return BACME.orders.receive({ certificateUrl: reply.certificate });
}).then(function (certs) {
console.log('WINNING!');
console.log(certs);
$qs('.js-fullchain').value = certs;
// https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
function spkiToPEM(keydata){
var keydataS = arrayBufferToString(keydata);
var keydataB64 = window.btoa(keydataS);
var keydataB64Pem = formatAsPem(keydataB64);
return keydataB64Pem;
}
function arrayBufferToString( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return binary;
}
function formatAsPem(str) {
var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n';
while(str.length > 0) {
finalString += str.substring(0, 64) + '\n';
str = str.substring(64);
}
finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----';
return finalString;
}
var wcOpts;
var pemName;
if (/^R/.test(info.serverJwk.kty)) {
pemName = 'RSA';
wcOpts = {
name: "RSASSA-PKCS1-v1_5"
, hash: { name: "SHA-256" }
};
} else {
pemName = 'EC';
wcOpts = {
name: "ECDSA"
, namedCurve: "P-256"
}
}
return crypto.subtle.importKey(
"jwk"
, info.serverJwk
, wcOpts
, true
, ["sign"]
).then(function (privateKey) {
return window.crypto.subtle.exportKey("pkcs8", privateKey);
}).then (function (keydata) {
var pem = spkiToPEM(keydata);
$qs('.js-privkey').value = pem;
steps[i]();
}).catch(function(err){
console.error(err);
});
});
});
};

67
js/bacme.js

@ -129,13 +129,24 @@ var textEncoder = new TextEncoder();
BACME._importKey = function (jwk) {
var alg; // I think the 256 refers to the hash
var wcOpts = {};
var extractable = false;
var extractable = true; // TODO make optionally false?
var priv = jwk;
var pub;
// ECDSA
if (/^EC/i.test(jwk.kty)) {
wcOpts.name = 'ECDSA';
wcOpts.namedCurve = jwk.crv;
alg = 'ES256';
pub = {
crv: priv.crv
, kty: priv.kty
, x: priv.x
, y: priv.y
};
if (!priv.d) {
priv = null;
}
}
// RSA
@ -143,28 +154,50 @@ BACME._importKey = function (jwk) {
wcOpts.name = 'RSASSA-PKCS1-v1_5';
wcOpts.hash = { name: "SHA-256" };
alg = 'RS256';
pub = {
e: priv.e
, kty: priv.kty
, n: priv.n
}
if (!priv.p) {
priv = null;
}
}
return window.crypto.subtle.importKey(
"jwk"
, jwk
, pub
, wcOpts
, extractable
, [ "sign"/*, "verify"*/ ]
).then(function (keypair) {
return {
wcKey: keypair
, meta: {
alg: alg
, name: wcOpts.name
, hash: wcOpts.hash
}
, jwk: jwk
};
, [ "verify" ]
).then(function (publicKey) {
function give(privateKey) {
return {
wcPub: publicKey
, wcKey: privateKey
, wcKeypair: { publicKey: publicKey, privateKey: privateKey }
, meta: {
alg: alg
, name: wcOpts.name
, hash: wcOpts.hash
}
, jwk: jwk
};
}
if (!priv) {
return give();
}
return window.crypto.subtle.importKey(
"jwk"
, priv
, wcOpts
, extractable
, [ "sign"/*, "verify"*/ ]
).then(give);
});
};
BACME._sign = function (opts) {
var wcPrivKey = opts.abstractKey.wcKey;
var wcPrivKey = opts.abstractKey.wcKeypair.privateKey;
var wcOpts = opts.abstractKey.meta;
var alg = opts.abstractKey.meta.alg; // I think the 256 refers to the hash
var signHash;
@ -508,6 +541,7 @@ BACME.challenges.accept = function (opts) {
).then(function (resp) {
BACME._logHeaders(resp);
nonce = resp.headers.get('replay-nonce');
console.log("ACCEPT NONCE:", nonce);
return resp.json().then(function (reply) {
challengePollUrl = reply.url;
@ -523,7 +557,6 @@ BACME.challenges.accept = function (opts) {
BACME.challenges.check = function (opts) {
return window.fetch(opts.challengePollUrl, { mode: 'cors' }).then(function (resp) {
BACME._logHeaders(resp);
nonce = resp.headers.get('replay-nonce');
return resp.json().then(function (reply) {
challengePollUrl = reply.url;
@ -566,7 +599,7 @@ BACME.domains.generateKeypair = function () {
// { serverJwk, domains }
BACME.orders.generateCsr = function (opts) {
return BACME._importKey(opts.serverJwk).then(function (abstractKey) {
return Promise.resolve(CSR.generate({ keypair: abstractKey.wcKey, domains: opts.domains }));
return Promise.resolve(CSR.generate({ keypair: abstractKey.wcKeypair, domains: opts.domains }));
});
};
@ -621,7 +654,7 @@ BACME.orders.receive = function (opts) {
BACME._logHeaders(resp);
nonce = resp.headers.get('replay-nonce');
return resp.json().then(function (reply) {
return resp.text().then(function (reply) {
BACME._logBody(reply);
return reply;

Зареждане…
Отказ
Запис