AJ ONeal преди 6 години
родител
ревизия
6c0aed0491
  1. 108
      js/app.js
  2. 68
      js/bacme.js

108
js/app.js

@ -19,8 +19,9 @@
}
function submitForm(ev) {
steps[i].submit(ev);
var j = i;
i += 1;
steps[j].submit(ev);
}
$qsa('.js-acme-form').forEach(function ($el) {
$el.addEventListener('submit', function (ev) {
@ -51,7 +52,12 @@
};
steps[1].submit = function () {
info.identifiers = $qs('.js-acme-domains').value.split(/\s*,\s*/g).map(function (hostname) {
return { type: 'dns', value: hostname.trim() };
return { type: 'dns', value: hostname.toLowerCase().trim() };
});
info.identifiers.sort(function (a, b) {
if (a === b) { return 0; }
if (a < b) { return 1; }
if (a > b) { return -1; }
});
return BACME.directory($qs('.js-acme-directory-url').value).then(function (directory) {
@ -59,6 +65,7 @@
return BACME.nonce().then(function (_nonce) {
nonce = _nonce;
console.log("MAGIC STEP NUMBER in 1 is:", i);
steps[i]();
});
});
@ -147,6 +154,7 @@
signedOrder: signedOrder
}).then(function (order) {
info.finalizeUrl = order.finalize;
info.orderUrl = order.url; // from header Location ???
return BACME.thumbprint({ jwk: jwk }).then(function (thumbprint) {
return BACME.challenges.all().then(function (claims) {
console.log('claims:');
@ -176,7 +184,7 @@
, challengeDomain: hostname
});
return BACME.challenges['dns-01']({
keyAuth: keyAuth
keyAuth: keyAuth.value
, challengeDomain: hostname
}).then(function (dnsAuth) {
var data = {
@ -230,6 +238,7 @@
updateChallengeType();
console.log("MAGIC STEP NUMBER in 2 is:", i);
steps[i]();
});
@ -249,23 +258,44 @@
$qs('.js-acme-form-challenges').hidden = false;
};
steps[3].submit = function () {
var chType = $qs('.js-acme-challenge-type').value;
var ps = [];
// 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) {
chType = $el.value;
return true;
}
});
console.log('chType is:', chType);
var chs = [];
// do each wildcard, if any
// do each challenge, by selected type only
[ 'wildcard', chType].forEach(function (typ) {
info.challenges[typ].forEach(function (ch) {
// { jwk, challengeUrl, accountId (kid) }
ps.push(BACME.challenges.accept({
chs.push({
jwk: info.jwk
, challengeUrl: ch.url
, accountId: info.kid
}));
});
});
});
return Promise.all(ps).then(function (results) {
var results = [];
function nextChallenge() {
var ch = chs.pop();
if (!ch) { return results; }
return BACME.challenges.accept(ch).then(function (result) {
results.push(result);
return nextChallenge();
});
}
steps[i]();
return nextChallenge().then(function (results) {
console.log('challenge status:', results);
var polls = results.slice(0);
var allsWell = true;
@ -276,7 +306,9 @@
}).then(function () {
return Promise.all(polls.map(function (poll) {
return BACME.challenges.check({ challengePollUrl: poll.url });
})).then(function () {
})).then(function (polls) {
console.log(polls);
polls = polls.filter(function (poll) {
//return 'valid' !== poll.status && 'invalid' !== poll.status;
if ('pending' === poll.status) {
@ -312,7 +344,63 @@
}
steps[4].submit = function () {
console.log('Congrats! Auto advancing...');
return BACME.order
var key = info.identifiers.map(function (ident) { return ident.value; }).join(',');
var serverJwk = JSON.parse(localStorage.getItem('server:' + key) || 'null');
var p;
function createKeypair() {
return BACME.accounts.generateKeypair({
type: 'ECDSA'
, bitlength: '256'
}).then(function (serverJwk) {
localStorage.setItem('server:' + key, JSON.stringify(serverJwk));
return serverJwk;
})
}
if (serverJwk) {
p = Promise.resolve(serverJwk);
} else {
p = createKeypair();
}
return p.then(function (_serverJwk) {
serverJwk = _serverJwk;
// { serverJwk, domains }
return BACME.orders.generateCsr({
serverJwk: serverJwk
, domains: info.identifiers.map(function (ident) {
return ident.value;
})
}).then(function (csrweb64) {
return BACME.order.finalize({
csr: csrweb64
, jwk: info.jwk
, finalizeUrl: info.finalizeUrl
, accountId: info.kid
});
}).then(function () {
function checkCert() {
return new Promise(function (resolve) {
setTimeout(resolve, 1000);
}).then(function () {
return BACME.order.check({ orderUrl: info.orderUrl });
}).then(function (reply) {
if ('processing' === reply) {
return checkCert();
}
return reply;
});
}
return checkCert();
}).then(function (reply) {
return BACME.order.receive({ certificateUrl: reply.certificate });
}).then(function (certs) {
console.log('WINNING!');
console.log(certs);
});
});
};
steps[5] = function () {

68
js/bacme.js

@ -102,6 +102,8 @@ BACME.accounts.generateKeypair = function (opts) {
console.log('private jwk:');
console.log(JSON.stringify(privJwk, null, 2));
return privJwk;
/*
return webCrypto.subtle.exportKey(
"pkcs8"
, result.privateKey
@ -112,6 +114,7 @@ BACME.accounts.generateKeypair = function (opts) {
return privJwk;
//return accountKeypair;
});
*/
})
});
};
@ -335,6 +338,7 @@ BACME.orders.create = function (opts) {
finalizeUrl = result.finalize;
BACME._logBody(result);
result.url = currentOrderUrl;
return result;
});
});
@ -404,9 +408,9 @@ BACME.thumbprint = function (opts) {
var keys;
if (/^EC/i.test(opts.jwk.kty)) {
keys = [ 'e', 'kty', 'n' ];
} else if (/^RS/i.test(opts.jwk.kty)) {
keys = [ 'crv', 'kty', 'x', 'y' ];
} else if (/^RS/i.test(opts.jwk.kty)) {
keys = [ 'e', 'kty', 'n' ];
}
var accountPublicStr = '{' + keys.map(function (key) {
@ -416,12 +420,14 @@ BACME.thumbprint = function (opts) {
return window.crypto.subtle.digest(
{ name: "SHA-256" } // SHA-256 is spec'd, non-optional
, textEncoder.encode(accountPublicStr)
).then(function(hash){
).then(function (hash) {
thumbprint = btoa(Array.prototype.map.call(new Uint8Array(hash), function (ch) {
return String.fromCharCode(ch);
}).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
console.log('Thumbprint:');
console.log(opts);
console.log(accountPublicStr);
console.log(thumbprint);
return thumbprint;
@ -446,10 +452,12 @@ BACME.challenges['http-01'] = function (opts) {
// { keyAuth }
BACME.challenges['dns-01'] = function (opts) {
console.log('opts.keyAuth for DNS:');
console.log(opts.keyAuth);
return window.crypto.subtle.digest(
{ name: "SHA-256", }
, textEncoder.encode(opts.keyAuth)
).then(function(hash){
).then(function (hash) {
dnsAuth = btoa(Array.prototype.map.call(new Uint8Array(hash), function (ch) {
return String.fromCharCode(ch);
}).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
@ -478,8 +486,7 @@ BACME.challenges.accept = function (opts) {
{}
);
nonce = null;
return BACME._import(opts.jwk).then(function (abstractKey) {
return BACME._importKey(opts.jwk).then(function (abstractKey) {
var protected64 = BACME._jsto64(
{ nonce: nonce, alg: abstractKey.meta.alg/*'ES256'*/, url: opts.challengeUrl, kid: opts.accountId }
);
@ -490,6 +497,7 @@ BACME.challenges.accept = function (opts) {
});
}).then(function (signedAccept) {
nonce = null;
return window.fetch(
opts.challengeUrl
, { mode: 'cors'
@ -555,8 +563,11 @@ BACME.domains.generateKeypair = function () {
});
};
BACME.orders.generateCsr = function (keypair, domains) {
return Promise.resolve(CSR.generate(keypair, domains));
// { 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 }));
});
};
var certificateUrl;
@ -567,8 +578,7 @@ BACME.orders.finalize = function (opts) {
{ csr: opts.csr }
);
nonce = null;
return BACME._import(opts.jwk).then(function (abstractKey) {
return BACME._importKey(opts.jwk).then(function (abstractKey) {
var protected64 = BACME._jsto64(
{ nonce: nonce, alg: abstractKey.meta.alg/*'ES256'*/, url: opts.finalizeUrl, kid: opts.accountId }
);
@ -579,8 +589,9 @@ BACME.orders.finalize = function (opts) {
});
}).then(function (signedFinal) {
nonce = null;
return window.fetch(
finalizeUrl
opts.finalizeUrl
, { mode: 'cors'
, method: 'POST'
, headers: { 'Content-Type': 'application/jose+json' }
@ -600,4 +611,39 @@ BACME.orders.finalize = function (opts) {
});
};
BACME.orders.receive = function (opts) {
return window.fetch(
opts.certificateUrl
, { mode: 'cors'
, method: 'GET'
}
).then(function (resp) {
BACME._logHeaders(resp);
nonce = resp.headers.get('replay-nonce');
return resp.json().then(function (reply) {
BACME._logBody(reply);
return reply;
});
});
};
BACME.orders.check = function (opts) {
return window.fetch(
opts.orderUrl
, { mode: 'cors'
, method: 'GET'
}
).then(function (resp) {
BACME._logHeaders(resp);
return resp.json().then(function (reply) {
BACME._logBody(reply);
return reply;
});
});
};
}(window));

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