appears to work
This commit is contained in:
parent
6c0aed0491
commit
510a367135
13
index.html
13
index.html
|
@ -120,19 +120,22 @@
|
||||||
<button type="submit">Next</button>
|
<button type="submit">Next</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Step 4 Process Challanges -->
|
<!-- Step 5 Get Certs -->
|
||||||
<form class="js-acme-form js-acme-form-download">
|
<form class="js-acme-form js-acme-form-download">
|
||||||
<label>privkey.pem</label>
|
<label>privkey.pem</label>
|
||||||
<textarea>-</textarea>
|
<textarea class="js-privkey">-</textarea>
|
||||||
|
|
||||||
<label>fullchain.pem</label>
|
<label>fullchain.pem</label>
|
||||||
<textarea>-</textarea>
|
<textarea class="js-fullchain">-</textarea>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TODO
|
||||||
<label>cert.pem</label>
|
<label>cert.pem</label>
|
||||||
<textarea>-</textarea>
|
<textarea class="js-cert">-</textarea>
|
||||||
|
|
||||||
<label>chain.pem</label>
|
<label>chain.pem</label>
|
||||||
<textarea>-</textarea>
|
<textarea class="js-chain">-</textarea>
|
||||||
|
-->
|
||||||
|
|
||||||
<button type="button">Download SSL Certificates</button>
|
<button type="button">Download SSL Certificates</button>
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,5 @@ popd
|
||||||
|
|
||||||
mkdir -p js/browser-csr/v1.0.0-alpha/
|
mkdir -p js/browser-csr/v1.0.0-alpha/
|
||||||
pushd 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
|
popd
|
||||||
|
|
81
js/app.js
81
js/app.js
|
@ -30,7 +30,9 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
function updateChallengeType() {
|
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);
|
console.log('ch type radio:', input.value);
|
||||||
$qs('.js-acme-table-wildcard').hidden = true;
|
$qs('.js-acme-table-wildcard').hidden = true;
|
||||||
$qs('.js-acme-table-http-01').hidden = true;
|
$qs('.js-acme-table-http-01').hidden = true;
|
||||||
|
@ -200,7 +202,6 @@
|
||||||
, dnsAnswer: dnsAuth.answer
|
, dnsAnswer: dnsAuth.answer
|
||||||
};
|
};
|
||||||
|
|
||||||
obj[c.type].push(data);
|
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('CHALLENGE');
|
console.log('CHALLENGE');
|
||||||
console.log(claim);
|
console.log(claim);
|
||||||
|
@ -258,9 +259,6 @@
|
||||||
$qs('.js-acme-form-challenges').hidden = false;
|
$qs('.js-acme-form-challenges').hidden = false;
|
||||||
};
|
};
|
||||||
steps[3].submit = function () {
|
steps[3].submit = function () {
|
||||||
// for now just show the next page immediately (its a spinner)
|
|
||||||
console.log("MAGIC STEP NUMBER is:", i);
|
|
||||||
|
|
||||||
var chType;
|
var chType;
|
||||||
Array.prototype.some.call($qsa('.js-acme-challenge-type'), function ($el) {
|
Array.prototype.some.call($qsa('.js-acme-challenge-type'), function ($el) {
|
||||||
if ($el.checked) {
|
if ($el.checked) {
|
||||||
|
@ -283,6 +281,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
console.log("INFO.challenges !!!!!", info.challenges);
|
||||||
|
|
||||||
var results = [];
|
var results = [];
|
||||||
function nextChallenge() {
|
function nextChallenge() {
|
||||||
|
@ -294,6 +293,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for now just show the next page immediately (its a spinner)
|
||||||
steps[i]();
|
steps[i]();
|
||||||
return nextChallenge().then(function (results) {
|
return nextChallenge().then(function (results) {
|
||||||
console.log('challenge status:', results);
|
console.log('challenge status:', results);
|
||||||
|
@ -366,6 +366,7 @@
|
||||||
|
|
||||||
return p.then(function (_serverJwk) {
|
return p.then(function (_serverJwk) {
|
||||||
serverJwk = _serverJwk;
|
serverJwk = _serverJwk;
|
||||||
|
info.serverJwk = serverJwk;
|
||||||
// { serverJwk, domains }
|
// { serverJwk, domains }
|
||||||
return BACME.orders.generateCsr({
|
return BACME.orders.generateCsr({
|
||||||
serverJwk: serverJwk
|
serverJwk: serverJwk
|
||||||
|
@ -373,7 +374,7 @@
|
||||||
return ident.value;
|
return ident.value;
|
||||||
})
|
})
|
||||||
}).then(function (csrweb64) {
|
}).then(function (csrweb64) {
|
||||||
return BACME.order.finalize({
|
return BACME.orders.finalize({
|
||||||
csr: csrweb64
|
csr: csrweb64
|
||||||
, jwk: info.jwk
|
, jwk: info.jwk
|
||||||
, finalizeUrl: info.finalizeUrl
|
, finalizeUrl: info.finalizeUrl
|
||||||
|
@ -384,7 +385,7 @@
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
setTimeout(resolve, 1000);
|
setTimeout(resolve, 1000);
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
return BACME.order.check({ orderUrl: info.orderUrl });
|
return BACME.orders.check({ orderUrl: info.orderUrl });
|
||||||
}).then(function (reply) {
|
}).then(function (reply) {
|
||||||
if ('processing' === reply) {
|
if ('processing' === reply) {
|
||||||
return checkCert();
|
return checkCert();
|
||||||
|
@ -395,10 +396,74 @@
|
||||||
|
|
||||||
return checkCert();
|
return checkCert();
|
||||||
}).then(function (reply) {
|
}).then(function (reply) {
|
||||||
return BACME.order.receive({ certificateUrl: reply.certificate });
|
return BACME.orders.receive({ certificateUrl: reply.certificate });
|
||||||
}).then(function (certs) {
|
}).then(function (certs) {
|
||||||
console.log('WINNING!');
|
console.log('WINNING!');
|
||||||
console.log(certs);
|
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
67
js/bacme.js
|
@ -129,13 +129,24 @@ var textEncoder = new TextEncoder();
|
||||||
BACME._importKey = function (jwk) {
|
BACME._importKey = function (jwk) {
|
||||||
var alg; // I think the 256 refers to the hash
|
var alg; // I think the 256 refers to the hash
|
||||||
var wcOpts = {};
|
var wcOpts = {};
|
||||||
var extractable = false;
|
var extractable = true; // TODO make optionally false?
|
||||||
|
var priv = jwk;
|
||||||
|
var pub;
|
||||||
|
|
||||||
// ECDSA
|
// ECDSA
|
||||||
if (/^EC/i.test(jwk.kty)) {
|
if (/^EC/i.test(jwk.kty)) {
|
||||||
wcOpts.name = 'ECDSA';
|
wcOpts.name = 'ECDSA';
|
||||||
wcOpts.namedCurve = jwk.crv;
|
wcOpts.namedCurve = jwk.crv;
|
||||||
alg = 'ES256';
|
alg = 'ES256';
|
||||||
|
pub = {
|
||||||
|
crv: priv.crv
|
||||||
|
, kty: priv.kty
|
||||||
|
, x: priv.x
|
||||||
|
, y: priv.y
|
||||||
|
};
|
||||||
|
if (!priv.d) {
|
||||||
|
priv = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSA
|
// RSA
|
||||||
|
@ -143,28 +154,50 @@ BACME._importKey = function (jwk) {
|
||||||
wcOpts.name = 'RSASSA-PKCS1-v1_5';
|
wcOpts.name = 'RSASSA-PKCS1-v1_5';
|
||||||
wcOpts.hash = { name: "SHA-256" };
|
wcOpts.hash = { name: "SHA-256" };
|
||||||
alg = 'RS256';
|
alg = 'RS256';
|
||||||
|
pub = {
|
||||||
|
e: priv.e
|
||||||
|
, kty: priv.kty
|
||||||
|
, n: priv.n
|
||||||
|
}
|
||||||
|
if (!priv.p) {
|
||||||
|
priv = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.crypto.subtle.importKey(
|
return window.crypto.subtle.importKey(
|
||||||
"jwk"
|
"jwk"
|
||||||
, jwk
|
, pub
|
||||||
, wcOpts
|
, wcOpts
|
||||||
, extractable
|
, extractable
|
||||||
, [ "sign"/*, "verify"*/ ]
|
, [ "verify" ]
|
||||||
).then(function (keypair) {
|
).then(function (publicKey) {
|
||||||
return {
|
function give(privateKey) {
|
||||||
wcKey: keypair
|
return {
|
||||||
, meta: {
|
wcPub: publicKey
|
||||||
alg: alg
|
, wcKey: privateKey
|
||||||
, name: wcOpts.name
|
, wcKeypair: { publicKey: publicKey, privateKey: privateKey }
|
||||||
, hash: wcOpts.hash
|
, meta: {
|
||||||
}
|
alg: alg
|
||||||
, jwk: jwk
|
, 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) {
|
BACME._sign = function (opts) {
|
||||||
var wcPrivKey = opts.abstractKey.wcKey;
|
var wcPrivKey = opts.abstractKey.wcKeypair.privateKey;
|
||||||
var wcOpts = opts.abstractKey.meta;
|
var wcOpts = opts.abstractKey.meta;
|
||||||
var alg = opts.abstractKey.meta.alg; // I think the 256 refers to the hash
|
var alg = opts.abstractKey.meta.alg; // I think the 256 refers to the hash
|
||||||
var signHash;
|
var signHash;
|
||||||
|
@ -508,6 +541,7 @@ BACME.challenges.accept = function (opts) {
|
||||||
).then(function (resp) {
|
).then(function (resp) {
|
||||||
BACME._logHeaders(resp);
|
BACME._logHeaders(resp);
|
||||||
nonce = resp.headers.get('replay-nonce');
|
nonce = resp.headers.get('replay-nonce');
|
||||||
|
console.log("ACCEPT NONCE:", nonce);
|
||||||
|
|
||||||
return resp.json().then(function (reply) {
|
return resp.json().then(function (reply) {
|
||||||
challengePollUrl = reply.url;
|
challengePollUrl = reply.url;
|
||||||
|
@ -523,7 +557,6 @@ BACME.challenges.accept = function (opts) {
|
||||||
BACME.challenges.check = function (opts) {
|
BACME.challenges.check = function (opts) {
|
||||||
return window.fetch(opts.challengePollUrl, { mode: 'cors' }).then(function (resp) {
|
return window.fetch(opts.challengePollUrl, { mode: 'cors' }).then(function (resp) {
|
||||||
BACME._logHeaders(resp);
|
BACME._logHeaders(resp);
|
||||||
nonce = resp.headers.get('replay-nonce');
|
|
||||||
|
|
||||||
return resp.json().then(function (reply) {
|
return resp.json().then(function (reply) {
|
||||||
challengePollUrl = reply.url;
|
challengePollUrl = reply.url;
|
||||||
|
@ -566,7 +599,7 @@ BACME.domains.generateKeypair = function () {
|
||||||
// { serverJwk, domains }
|
// { serverJwk, domains }
|
||||||
BACME.orders.generateCsr = function (opts) {
|
BACME.orders.generateCsr = function (opts) {
|
||||||
return BACME._importKey(opts.serverJwk).then(function (abstractKey) {
|
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);
|
BACME._logHeaders(resp);
|
||||||
nonce = resp.headers.get('replay-nonce');
|
nonce = resp.headers.get('replay-nonce');
|
||||||
|
|
||||||
return resp.json().then(function (reply) {
|
return resp.text().then(function (reply) {
|
||||||
BACME._logBody(reply);
|
BACME._logBody(reply);
|
||||||
|
|
||||||
return reply;
|
return reply;
|
||||||
|
|
Loading…
Reference in New Issue