create acocunt and order, and view challenges
This commit is contained in:
parent
5d4f71ba8e
commit
ec9a2606f6
100
js/app.js
100
js/app.js
|
@ -5,6 +5,8 @@
|
||||||
var $qsa = function (s) { return window.document.querySelectorAll(s); };
|
var $qsa = function (s) { return window.document.querySelectorAll(s); };
|
||||||
var info = {};
|
var info = {};
|
||||||
var steps = {};
|
var steps = {};
|
||||||
|
var nonce;
|
||||||
|
var kid;
|
||||||
var i = 1;
|
var i = 1;
|
||||||
|
|
||||||
//$qs('.js-acme-directory-url').value = 'https://acme-v02.api.letsencrypt.org/directory';
|
//$qs('.js-acme-directory-url').value = 'https://acme-v02.api.letsencrypt.org/directory';
|
||||||
|
@ -16,11 +18,13 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$qs('.js-acme-form-domains').addEventListener('submit', function (ev) {
|
$qsa('.js-acme-form').forEach(function ($el) {
|
||||||
|
$el.addEventListener('submit', function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
steps[i].submit(ev);
|
steps[i].submit(ev);
|
||||||
i += 1;
|
i += 1;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
steps[1] = function () {
|
steps[1] = function () {
|
||||||
hideForms();
|
hideForms();
|
||||||
|
@ -33,8 +37,12 @@
|
||||||
|
|
||||||
return BACME.directory($qs('.js-acme-directory-url').value).then(function (directory) {
|
return BACME.directory($qs('.js-acme-directory-url').value).then(function (directory) {
|
||||||
$qs('.js-acme-tos-url').href = directory.meta.termsOfService;
|
$qs('.js-acme-tos-url').href = directory.meta.termsOfService;
|
||||||
|
return BACME.nonce().then(function (_nonce) {
|
||||||
|
nonce = _nonce;
|
||||||
|
|
||||||
steps[i]();
|
steps[i]();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
steps[2] = function () {
|
steps[2] = function () {
|
||||||
|
@ -42,16 +50,94 @@
|
||||||
$qs('.js-acme-form-account').hidden = false;
|
$qs('.js-acme-form-account').hidden = false;
|
||||||
};
|
};
|
||||||
steps[2].submit = function () {
|
steps[2].submit = function () {
|
||||||
info.contact = [ 'mailto:' + $qs('.js-acme-account-email').value ];
|
var email = $qs('.js-acme-account-email').value.toLowerCase().trim();
|
||||||
|
|
||||||
|
info.contact = [ 'mailto:' + email ];
|
||||||
info.agree = $qs('.js-acme-account-tos').checked;
|
info.agree = $qs('.js-acme-account-tos').checked;
|
||||||
info.greenlockAgree = $qs('.js-gl-tos').checked;
|
info.greenlockAgree = $qs('.js-gl-tos').checked;
|
||||||
// TODO
|
// TODO
|
||||||
// create account key
|
// options for
|
||||||
// create account
|
// * regenerate key
|
||||||
// capture email
|
// * ECDSA / RSA / bitlength
|
||||||
// submit challenges
|
|
||||||
// populate challenges in table
|
// TODO ping with version and account creation
|
||||||
|
|
||||||
|
var jwk = JSON.parse(localStorage.getItem('account:' + email) || 'null');
|
||||||
|
var p;
|
||||||
|
|
||||||
|
function createKeypair() {
|
||||||
|
return BACME.accounts.generateKeypair({
|
||||||
|
type: 'ECDSA'
|
||||||
|
, bitlength: '256'
|
||||||
|
}).then(function (jwk) {
|
||||||
|
localStorage.setItem('account:' + email, JSON.stringify(jwk));
|
||||||
|
return jwk;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jwk) {
|
||||||
|
p = Promise.resolve(jwk);
|
||||||
|
} else {
|
||||||
|
p = createKeypair();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAccount(jwk) {
|
||||||
|
console.log('account jwk:');
|
||||||
|
console.log(jwk);
|
||||||
|
delete jwk.key_ops;
|
||||||
|
return BACME.accounts.sign({
|
||||||
|
jwk: jwk
|
||||||
|
, contacts: [ 'mailto:' + email ]
|
||||||
|
, agree: info.agree
|
||||||
|
, nonce: nonce
|
||||||
|
, kid: kid
|
||||||
|
}).then(function (signedAccount) {
|
||||||
|
return BACME.accounts.set({
|
||||||
|
signedAccount: signedAccount
|
||||||
|
}).then(function (account) {
|
||||||
|
console.log('account:');
|
||||||
|
console.log(account);
|
||||||
|
kid = account.kid;
|
||||||
|
return kid;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.then(function (_jwk) {
|
||||||
|
jwk = _jwk;
|
||||||
|
kid = JSON.parse(localStorage.getItem('account-kid:' + email) || 'null');
|
||||||
|
var p2
|
||||||
|
|
||||||
|
// TODO save account id rather than always retrieving it
|
||||||
|
if (kid) {
|
||||||
|
p2 = Promise.resolve(kid);
|
||||||
|
} else {
|
||||||
|
p2 = createAccount(jwk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p2.then(function (_kid) {
|
||||||
|
kid = _kid;
|
||||||
|
return BACME.orders.sign({
|
||||||
|
jwk: jwk
|
||||||
|
, identifiers: info.identifiers
|
||||||
|
, kid: kid
|
||||||
|
}).then(function (signedOrder) {
|
||||||
|
return BACME.orders.create({
|
||||||
|
signedOrder: signedOrder
|
||||||
|
}).then(function (/*challengeIndexes*/) {
|
||||||
|
return BACME.challenges.all().then(function (challenges) {
|
||||||
|
console.log('challenges:');
|
||||||
|
console.log(challenges);
|
||||||
|
// TODO populate challenges in table
|
||||||
steps[i]();
|
steps[i]();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.error('Step \'' + i + '\' Error:');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
steps[3] = function () {
|
steps[3] = function () {
|
||||||
|
|
284
js/bacme.js
284
js/bacme.js
|
@ -58,11 +58,36 @@ BACME.nonce = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
BACME.accounts = {};
|
BACME.accounts = {};
|
||||||
BACME.accounts.generateKeypair = function () {
|
|
||||||
|
// type = ECDSA
|
||||||
|
// bitlength = 256
|
||||||
|
BACME.accounts.generateKeypair = function (opts) {
|
||||||
|
var wcOpts = {};
|
||||||
|
|
||||||
|
// ECDSA has only the P curves and an associated bitlength
|
||||||
|
if (/^EC/i.test(opts.type)) {
|
||||||
|
wcOpts.name = 'ECDSA';
|
||||||
|
if (/256/.test(opts.bitlength)) {
|
||||||
|
wcOpts.namedCurve = 'P-256';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RSA-PSS is another option, but I don't think it's used for Let's Encrypt
|
||||||
|
// I think the hash is only necessary for signing, not generation or import
|
||||||
|
if (/^RS/i.test(opts.type)) {
|
||||||
|
wcOpts.name = 'RSASSA-PKCS1-v1_5';
|
||||||
|
wcOpts.modulusLength = opts.bitlength;
|
||||||
|
if (opts.bitlength < 2048) {
|
||||||
|
wcOpts.modulusLength = opts.bitlength * 8;
|
||||||
|
}
|
||||||
|
wcOpts.publicExponent = new Uint8Array([0x01, 0x00, 0x01]);
|
||||||
|
wcOpts.hash = { name: "SHA-256" };
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
|
// https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
|
||||||
var extractable = true;
|
var extractable = true;
|
||||||
return webCrypto.subtle.generateKey(
|
return webCrypto.subtle.generateKey(
|
||||||
{ name: "ECDSA", namedCurve: "P-256" }
|
wcOpts
|
||||||
, extractable
|
, extractable
|
||||||
, [ 'sign', 'verify' ]
|
, [ 'sign', 'verify' ]
|
||||||
).then(function (result) {
|
).then(function (result) {
|
||||||
|
@ -71,11 +96,11 @@ BACME.accounts.generateKeypair = function () {
|
||||||
return webCrypto.subtle.exportKey(
|
return webCrypto.subtle.exportKey(
|
||||||
"jwk"
|
"jwk"
|
||||||
, result.privateKey
|
, result.privateKey
|
||||||
).then(function (jwk) {
|
).then(function (privJwk) {
|
||||||
|
|
||||||
accountJwk = jwk;
|
accountJwk = privJwk;
|
||||||
console.log('private jwk:');
|
console.log('private jwk:');
|
||||||
console.log(JSON.stringify(jwk, null, 2));
|
console.log(JSON.stringify(privJwk, null, 2));
|
||||||
|
|
||||||
return webCrypto.subtle.exportKey(
|
return webCrypto.subtle.exportKey(
|
||||||
"pkcs8"
|
"pkcs8"
|
||||||
|
@ -84,7 +109,8 @@ BACME.accounts.generateKeypair = function () {
|
||||||
console.log('pkcs8:');
|
console.log('pkcs8:');
|
||||||
console.log(Array.from(new Uint8Array(keydata)));
|
console.log(Array.from(new Uint8Array(keydata)));
|
||||||
|
|
||||||
return accountKeypair;
|
return privJwk;
|
||||||
|
//return accountKeypair;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -97,63 +123,137 @@ BACME._jsto64 = function (json) {
|
||||||
|
|
||||||
var textEncoder = new TextEncoder();
|
var textEncoder = new TextEncoder();
|
||||||
|
|
||||||
// email = john.doe@gmail.com
|
BACME._importKey = function (jwk) {
|
||||||
BACME.accounts.sign = function (email) {
|
var alg; // I think the 256 refers to the hash
|
||||||
var payload64 = BACME._jsto64(
|
var wcOpts = {};
|
||||||
{ termsOfServiceAgreed: true
|
var extractable = false;
|
||||||
, onlyReturnExisting: false
|
|
||||||
, contact: [ 'mailto:' + email ]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
var protected64 = BACME._jsto64(
|
// ECDSA
|
||||||
{ nonce: nonce
|
if (/^EC/i.test(jwk.kty)) {
|
||||||
, url: accountUrl
|
wcOpts.name = 'ECDSA';
|
||||||
, alg: 'ES256'
|
wcOpts.namedCurve = jwk.crv;
|
||||||
, jwk: {
|
alg = 'ES256';
|
||||||
kty: accountJwk.kty
|
|
||||||
, crv: accountJwk.crv
|
|
||||||
, x: accountJwk.x
|
|
||||||
, y: accountJwk.y
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note: this function hashes before signing so send data, not the hash
|
// RSA
|
||||||
|
if (/^RS/i.test(jwk.kty)) {
|
||||||
|
wcOpts.name = 'RSASSA-PKCS1-v1_5';
|
||||||
|
wcOpts.hash = { name: "SHA-256" };
|
||||||
|
alg = 'RS256';
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.crypto.subtle.importKey(
|
||||||
|
"jwk"
|
||||||
|
, jwk
|
||||||
|
, wcOpts
|
||||||
|
, extractable
|
||||||
|
, [ "sign"/*, "verify"*/ ]
|
||||||
|
).then(function (keypair) {
|
||||||
|
return {
|
||||||
|
wcKey: keypair
|
||||||
|
, meta: {
|
||||||
|
alg: alg
|
||||||
|
, name: wcOpts.name
|
||||||
|
, hash: wcOpts.hash
|
||||||
|
}
|
||||||
|
, jwk: jwk
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
BACME._sign = function (opts) {
|
||||||
|
var wcPrivKey = opts.abstractKey.wcKey;
|
||||||
|
var wcOpts = opts.abstractKey.meta;
|
||||||
|
var alg = opts.abstractKey.meta.alg; // I think the 256 refers to the hash
|
||||||
|
var signHash;
|
||||||
|
|
||||||
|
console.log('kty', opts.abstractKey.jwk.kty);
|
||||||
|
signHash = { name: "SHA-" + alg.replace(/[a-z]+/ig, '') };
|
||||||
|
|
||||||
|
var msg = textEncoder.encode(opts.protected64 + '.' + opts.payload64);
|
||||||
|
console.log('msg:', msg);
|
||||||
return window.crypto.subtle.sign(
|
return window.crypto.subtle.sign(
|
||||||
{ name: "ECDSA", hash: { name: "SHA-256" } }
|
{ name: wcOpts.name, hash: signHash }
|
||||||
, accountKeypair.privateKey
|
, wcPrivKey
|
||||||
, textEncoder.encode(protected64 + '.' + payload64)
|
, msg
|
||||||
).then(function (signature) {
|
).then(function (signature) {
|
||||||
|
//console.log('sig1:', signature);
|
||||||
|
//console.log('sig2:', new Uint8Array(signature));
|
||||||
|
//console.log('sig3:', Array.prototype.slice.call(new Uint8Array(signature)));
|
||||||
// convert buffer to urlsafe base64
|
// convert buffer to urlsafe base64
|
||||||
var sig64 = btoa(Array.prototype.map.call(new Uint8Array(signature), function (ch) {
|
var sig64 = btoa(Array.prototype.map.call(new Uint8Array(signature), function (ch) {
|
||||||
return String.fromCharCode(ch);
|
return String.fromCharCode(ch);
|
||||||
}).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
}).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
||||||
|
|
||||||
console.log('URL-safe Base64 Signature:');
|
console.log('[1] URL-safe Base64 Signature:');
|
||||||
console.log(sig64);
|
console.log(sig64);
|
||||||
|
|
||||||
signedAccount = {
|
var signedMsg = {
|
||||||
protected: protected64
|
protected: opts.protected64
|
||||||
, payload: payload64
|
, payload: opts.payload64
|
||||||
, signature: sig64
|
, signature: sig64
|
||||||
};
|
};
|
||||||
console.log('Signed Base64 Account:');
|
|
||||||
console.log(JSON.stringify(signedAccount, null, 2));
|
console.log('Signed Base64 Msg:');
|
||||||
|
console.log(JSON.stringify(signedMsg, null, 2));
|
||||||
|
|
||||||
|
return signedMsg;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// email = john.doe@gmail.com
|
||||||
|
// jwk = { ... }
|
||||||
|
// agree = true
|
||||||
|
BACME.accounts.sign = function (opts) {
|
||||||
|
|
||||||
|
return BACME._importKey(opts.jwk).then(function (abstractKey) {
|
||||||
|
|
||||||
|
var payloadJson =
|
||||||
|
{ termsOfServiceAgreed: opts.agree
|
||||||
|
, onlyReturnExisting: false
|
||||||
|
, contact: opts.contacts || [ 'mailto:' + opts.email ]
|
||||||
|
};
|
||||||
|
console.log('payload:');
|
||||||
|
console.log(payloadJson);
|
||||||
|
var payload64 = BACME._jsto64(
|
||||||
|
payloadJson
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO RSA
|
||||||
|
var protectedJson =
|
||||||
|
{ nonce: opts.nonce
|
||||||
|
, url: accountUrl
|
||||||
|
, alg: abstractKey.meta.alg
|
||||||
|
, jwk: {
|
||||||
|
kty: opts.jwk.kty
|
||||||
|
, crv: opts.jwk.crv
|
||||||
|
, x: opts.jwk.x
|
||||||
|
, y: opts.jwk.y
|
||||||
|
}
|
||||||
|
};
|
||||||
|
console.log('protected:');
|
||||||
|
console.log(protectedJson);
|
||||||
|
var protected64 = BACME._jsto64(
|
||||||
|
protectedJson
|
||||||
|
);
|
||||||
|
|
||||||
|
// Note: this function hashes before signing so send data, not the hash
|
||||||
|
return BACME._sign({
|
||||||
|
abstractKey: abstractKey
|
||||||
|
, payload64: payload64
|
||||||
|
, protected64: protected64
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var account;
|
var account;
|
||||||
var accountId;
|
var accountId;
|
||||||
|
|
||||||
BACME.accounts.set = function () {
|
BACME.accounts.set = function (opts) {
|
||||||
nonce = null;
|
nonce = null;
|
||||||
return window.fetch(accountUrl, {
|
return window.fetch(accountUrl, {
|
||||||
mode: 'cors'
|
mode: 'cors'
|
||||||
, method: 'POST'
|
, method: 'POST'
|
||||||
, headers: { 'Content-Type': 'application/jose+json' }
|
, headers: { 'Content-Type': 'application/jose+json' }
|
||||||
, body: JSON.stringify(signedAccount)
|
, body: JSON.stringify(opts.signedAccount)
|
||||||
}).then(function (resp) {
|
}).then(function (resp) {
|
||||||
BACME._logHeaders(resp);
|
BACME._logHeaders(resp);
|
||||||
nonce = resp.headers.get('replay-nonce');
|
nonce = resp.headers.get('replay-nonce');
|
||||||
|
@ -163,11 +263,18 @@ BACME.accounts.set = function () {
|
||||||
|
|
||||||
if (!resp.headers.get('content-type')) {
|
if (!resp.headers.get('content-type')) {
|
||||||
console.log('Body: <none>');
|
console.log('Body: <none>');
|
||||||
return;
|
|
||||||
|
return { kid: accountId };
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.json().then(function (result) {
|
return resp.json().then(function (result) {
|
||||||
|
if (/^Error/i.test(result.detail)) {
|
||||||
|
return Promise.reject(new Error(result.detail));
|
||||||
|
}
|
||||||
|
result.kid = accountId;
|
||||||
BACME._logBody(result);
|
BACME._logBody(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -178,36 +285,28 @@ var signedOrder;
|
||||||
BACME.orders = {};
|
BACME.orders = {};
|
||||||
|
|
||||||
// identifiers = [ { type: 'dns', value: 'example.com' }, { type: 'dns', value: '*.example.com' } ]
|
// identifiers = [ { type: 'dns', value: 'example.com' }, { type: 'dns', value: '*.example.com' } ]
|
||||||
BACME.orders.sign = function (identifiers) {
|
// signedAccount
|
||||||
var payload64 = jsto64({ identifiers: identifiers });
|
BACME.orders.sign = function (opts) {
|
||||||
|
var payload64 = BACME._jsto64({ identifiers: opts.identifiers });
|
||||||
|
|
||||||
var protected64 = jsto64(
|
var protected64 = BACME._jsto64(
|
||||||
{ nonce: nonce, alg: 'ES256', url: orderUrl, kid: accountId }
|
{ nonce: nonce, alg: 'ES256', url: orderUrl, kid: opts.kid }
|
||||||
);
|
);
|
||||||
|
|
||||||
return window.crypto.subtle.sign(
|
return BACME._importKey(opts.jwk).then(function (abstractKey) {
|
||||||
{ name: "ECDSA", hash: { name: "SHA-256" } }
|
console.log('abstractKey:');
|
||||||
, accountKeypair.privateKey
|
console.log(abstractKey);
|
||||||
, textEncoder.encode(protected64 + '.' + payload64)
|
return BACME._sign({
|
||||||
).then(function (signature) {
|
abstractKey: abstractKey
|
||||||
|
, payload64: payload64
|
||||||
// convert buffer to urlsafe base64
|
, protected64: protected64
|
||||||
var sig64 = btoa(Array.prototype.map.call(new Uint8Array(signature), function (ch) {
|
}).then(function (sig) {
|
||||||
return String.fromCharCode(ch);
|
if (!sig) {
|
||||||
}).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
throw new Error('sig is undefined... nonsense!');
|
||||||
|
}
|
||||||
console.log('URL-safe Base64 Signature:');
|
console.log('newsig', sig);
|
||||||
console.log(sig64);
|
return sig;
|
||||||
|
});
|
||||||
signedOrder = {
|
|
||||||
protected: protected64
|
|
||||||
, payload: payload64
|
|
||||||
, signature: sig64
|
|
||||||
};
|
|
||||||
console.log('Signed Base64 Order:');
|
|
||||||
console.log(JSON.stringify(signedAccount, null, 2));
|
|
||||||
|
|
||||||
return signedOrder;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,25 +315,26 @@ var currentOrderUrl;
|
||||||
var authorizationUrls;
|
var authorizationUrls;
|
||||||
var finalizeUrl;
|
var finalizeUrl;
|
||||||
|
|
||||||
BACME.orders.create = function () {
|
BACME.orders.create = function (opts) {
|
||||||
nonce = null;
|
nonce = null;
|
||||||
return window.fetch(orderUrl, {
|
return window.fetch(orderUrl, {
|
||||||
mode: 'cors'
|
mode: 'cors'
|
||||||
, method: 'POST'
|
, method: 'POST'
|
||||||
, headers: { 'Content-Type': 'application/jose+json' }
|
, headers: { 'Content-Type': 'application/jose+json' }
|
||||||
, body: JSON.stringify(signedOrder)
|
, body: JSON.stringify(opts.signedOrder)
|
||||||
}).then(function (resp) {
|
}).then(function (resp) {
|
||||||
console.log('Headers:');
|
BACME._logHeaders(resp);
|
||||||
Array.from(resp.headers.entries()).forEach(function (h) { console.log(h[0] + ': ' + h[1]); });
|
|
||||||
currentOrderUrl = resp.headers.get('location');
|
currentOrderUrl = resp.headers.get('location');
|
||||||
nonce = resp.headers.get('replay-nonce');
|
nonce = resp.headers.get('replay-nonce');
|
||||||
console.log('Next nonce:', nonce);
|
console.log('Next nonce:', nonce);
|
||||||
|
|
||||||
return resp.json().then(function (result) {
|
return resp.json().then(function (result) {
|
||||||
|
if (/^Error/i.test(result.detail)) {
|
||||||
|
return Promise.reject(new Error(result.detail));
|
||||||
|
}
|
||||||
authorizationUrls = result.authorizations;
|
authorizationUrls = result.authorizations;
|
||||||
finalizeUrl = result.finalize;
|
finalizeUrl = result.finalize;
|
||||||
console.log('Body:');
|
BACME._logBody(result);
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
@ -242,6 +342,22 @@ BACME.orders.create = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
BACME.challenges = {};
|
BACME.challenges = {};
|
||||||
|
BACME.challenges.all = function () {
|
||||||
|
var challenges = [];
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (!authorizationUrls.length) {
|
||||||
|
return challenges;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BACME.challenges.view().then(function (challenge) {
|
||||||
|
challenges.push(challenge);
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
BACME.challenges.view = function () {
|
BACME.challenges.view = function () {
|
||||||
var authzUrl = authorizationUrls.pop();
|
var authzUrl = authorizationUrls.pop();
|
||||||
var token;
|
var token;
|
||||||
|
@ -273,10 +389,19 @@ var httpPath;
|
||||||
var dnsAuth;
|
var dnsAuth;
|
||||||
var dnsRecord;
|
var dnsRecord;
|
||||||
|
|
||||||
BACME.thumbprint = function () {
|
BACME.thumbprint = function (opts) {
|
||||||
// https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
|
// https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
|
||||||
|
|
||||||
var accountPublicStr = '{' + ['crv', 'kty', 'x', 'y'].map(function (key) {
|
var accountJwk = opts.jwk;
|
||||||
|
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' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var accountPublicStr = '{' + keys.map(function (key) {
|
||||||
return '"' + key + '":"' + accountJwk[key] + '"';
|
return '"' + key + '":"' + accountJwk[key] + '"';
|
||||||
}).join(',') + '}';
|
}).join(',') + '}';
|
||||||
|
|
||||||
|
@ -338,11 +463,11 @@ BACME.challenges['dns-01'] = function () {
|
||||||
var challengePollUrl;
|
var challengePollUrl;
|
||||||
|
|
||||||
BACME.challenges.accept = function () {
|
BACME.challenges.accept = function () {
|
||||||
var payload64 = jsto64(
|
var payload64 = BACME._jsto64(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
var protected64 = jsto64(
|
var protected64 = BACME._jsto64(
|
||||||
{ nonce: nonce, alg: 'ES256', url: challengeUrl, kid: accountId }
|
{ nonce: nonce, alg: 'ES256', url: challengeUrl, kid: accountId }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -371,8 +496,7 @@ BACME.challenges.accept = function () {
|
||||||
, body: JSON.stringify(body)
|
, body: JSON.stringify(body)
|
||||||
}
|
}
|
||||||
).then(function (resp) {
|
).then(function (resp) {
|
||||||
console.log('Headers:');
|
BACME._logHeaders(resp);
|
||||||
Array.from(resp.headers.entries()).forEach(function (h) { console.log(h[0] + ': ' + h[1]); });
|
|
||||||
nonce = resp.headers.get('replay-nonce');
|
nonce = resp.headers.get('replay-nonce');
|
||||||
|
|
||||||
return resp.json().then(function (reply) {
|
return resp.json().then(function (reply) {
|
||||||
|
@ -435,11 +559,11 @@ BACME.orders.generateCsr = function (keypair, domains) {
|
||||||
var certificateUrl;
|
var certificateUrl;
|
||||||
|
|
||||||
BACME.orders.finalize = function () {
|
BACME.orders.finalize = function () {
|
||||||
var payload64 = jsto64(
|
var payload64 = BACME._jsto64(
|
||||||
{ csr: csr }
|
{ csr: csr }
|
||||||
);
|
);
|
||||||
|
|
||||||
var protected64 = jsto64(
|
var protected64 = BACME._jsto64(
|
||||||
{ nonce: nonce, alg: 'ES256', url: finalizeUrl, kid: accountId }
|
{ nonce: nonce, alg: 'ES256', url: finalizeUrl, kid: accountId }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue