|
|
@ -16,6 +16,9 @@ ACME.splitPemChain = function splitPemChain(str) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
|
|
|
|
// dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
|
|
|
|
ACME.challengePrefixes = { |
|
|
|
'http-01': '/.well-known/acme-challenge' |
|
|
|
, 'dns-01': '_acme-challenge' |
|
|
@ -255,6 +258,37 @@ ACME._wait = function wait(ms) { |
|
|
|
setTimeout(resolve, (ms || 1100)); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
ACME._testChallenges = function (me, options) { |
|
|
|
if (me.skipChallengeTest) { |
|
|
|
return Promise.resolve(); |
|
|
|
} |
|
|
|
|
|
|
|
return Promise.all(options.domains.map(function (identifierValue) { |
|
|
|
// TODO we really only need one to pass, not all to pass
|
|
|
|
return Promise.all(options.challengeTypes.map(function (chType) { |
|
|
|
var chToken = require('crypto').randomBytes(16).toString('hex'); |
|
|
|
var thumbprint = me.RSA.thumbprint(options.accountKeypair); |
|
|
|
var keyAuthorization = chToken + '.' + thumbprint; |
|
|
|
var auth = { |
|
|
|
identifier: { type: "dns", value: identifierValue } |
|
|
|
, hostname: identifierValue |
|
|
|
, type: chType |
|
|
|
, token: chToken |
|
|
|
, thumbprint: thumbprint |
|
|
|
, keyAuthorization: keyAuthorization |
|
|
|
, dnsAuthorization: me.RSA.utils.toWebsafeBase64( |
|
|
|
require('crypto').createHash('sha256').update(keyAuthorization).digest('base64') |
|
|
|
) |
|
|
|
}; |
|
|
|
|
|
|
|
return ACME._setChallenge(me, options, auth).then(function () { |
|
|
|
return ACME.challengeTests[chType](me, auth); |
|
|
|
}); |
|
|
|
})); |
|
|
|
})); |
|
|
|
}; |
|
|
|
|
|
|
|
// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
|
|
|
|
ACME._postChallenge = function (me, options, identifier, ch) { |
|
|
|
var RETRY_INTERVAL = me.retryInterval || 1000; |
|
|
@ -279,7 +313,6 @@ ACME._postChallenge = function (me, options, identifier, ch) { |
|
|
|
) |
|
|
|
}; |
|
|
|
|
|
|
|
return new Promise(function (resolve, reject) { |
|
|
|
/* |
|
|
|
POST /acme/authz/1234 HTTP/1.1 |
|
|
|
Host: example.com |
|
|
@ -333,7 +366,6 @@ ACME._postChallenge = function (me, options, identifier, ch) { |
|
|
|
|
|
|
|
if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); } |
|
|
|
return me._request({ method: 'GET', url: ch.url, json: true }).then(function (resp) { |
|
|
|
|
|
|
|
if ('processing' === resp.body.status) { |
|
|
|
if (me.debug) { console.debug('poll: again'); } |
|
|
|
return ACME._wait(RETRY_INTERVAL).then(pollStatus); |
|
|
@ -342,10 +374,10 @@ ACME._postChallenge = function (me, options, identifier, ch) { |
|
|
|
// This state should never occur
|
|
|
|
if ('pending' === resp.body.status) { |
|
|
|
if (count >= MAX_PEND) { |
|
|
|
return ACME._wait(RETRY_INTERVAL).then(deactivate).then(testChallenge); |
|
|
|
return ACME._wait(RETRY_INTERVAL).then(deactivate).then(respondToChallenge); |
|
|
|
} |
|
|
|
if (me.debug) { console.debug('poll: again'); } |
|
|
|
return ACME._wait(RETRY_INTERVAL).then(testChallenge); |
|
|
|
return ACME._wait(RETRY_INTERVAL).then(respondToChallenge); |
|
|
|
} |
|
|
|
|
|
|
|
if ('valid' === resp.body.status) { |
|
|
@ -403,48 +435,35 @@ ACME._postChallenge = function (me, options, identifier, ch) { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function testChallenge() { |
|
|
|
// TODO put check dns / http checks here?
|
|
|
|
// http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
|
|
|
|
// dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
|
|
|
|
|
|
|
|
if (me.debug) {console.debug('\n[DEBUG] postChallenge\n'); } |
|
|
|
//if (me.debug) console.debug('\n[DEBUG] stop to fix things\n'); return;
|
|
|
|
|
|
|
|
return ACME._wait(RETRY_INTERVAL).then(function () { |
|
|
|
if (!me.skipChallengeTest) { |
|
|
|
return ACME.challengeTests[ch.type](me, auth); |
|
|
|
} |
|
|
|
}).then(respondToChallenge); |
|
|
|
} |
|
|
|
|
|
|
|
return ACME._setChallenge(me, options, auth).then(respondToChallenge); |
|
|
|
}; |
|
|
|
ACME._setChallenge = function (me, options, auth) { |
|
|
|
return new Promise(function (resolve, reject) { |
|
|
|
try { |
|
|
|
if (1 === options.setChallenge.length) { |
|
|
|
options.setChallenge(auth).then(testChallenge).then(resolve, reject); |
|
|
|
options.setChallenge(auth).then(resolve).catch(reject); |
|
|
|
} else if (2 === options.setChallenge.length) { |
|
|
|
options.setChallenge(auth, function (err) { |
|
|
|
if(err) { |
|
|
|
reject(err); |
|
|
|
} else { |
|
|
|
testChallenge().then(resolve, reject); |
|
|
|
} |
|
|
|
if(err) { reject(err); } else { resolve(); } |
|
|
|
}); |
|
|
|
} else { |
|
|
|
var challengeCb = function(err) { |
|
|
|
if(err) { |
|
|
|
reject(err); |
|
|
|
} else { |
|
|
|
testChallenge().then(resolve, reject); |
|
|
|
} |
|
|
|
if(err) { reject(err); } else { resolve(); } |
|
|
|
}; |
|
|
|
// for backwards compat adding extra keys without changing params length
|
|
|
|
Object.keys(auth).forEach(function (key) { |
|
|
|
challengeCb[key] = auth[key]; |
|
|
|
}); |
|
|
|
options.setChallenge(identifier.value, ch.token, keyAuthorization, challengeCb); |
|
|
|
options.setChallenge(auth.identifier.value, auth.token, auth.keyAuthorization, challengeCb); |
|
|
|
} |
|
|
|
} catch(e) { |
|
|
|
reject(e); |
|
|
|
} |
|
|
|
}).then(function () { |
|
|
|
// TODO: Do we still need this delay? Or shall we leave it to plugins to account for themselves?
|
|
|
|
var DELAY = me.setChallengeWait || 500; |
|
|
|
if (me.debug) { console.debug('\n[DEBUG] waitChallengeDelay %s\n', DELAY); } |
|
|
|
return ACME._wait(DELAY); |
|
|
|
}); |
|
|
|
}; |
|
|
|
ACME._finalizeOrder = function (me, options, validatedDomains) { |
|
|
@ -548,6 +567,7 @@ ACME._getCertificate = function (me, options) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return ACME._testChallenges(me, options).then(function () { |
|
|
|
if (me.debug) { console.debug('[acme-v2] certificates.create'); } |
|
|
|
return ACME._getNonce(me).then(function () { |
|
|
|
var body = { |
|
|
@ -650,6 +670,7 @@ ACME._getCertificate = function (me, options) { |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
ACME.create = function create(me) { |
|
|
|