forked from root/acme.js
Compare commits
No commits in common. "master" and "v3" have entirely different histories.
12
README.md
12
README.md
|
@ -2,16 +2,10 @@
|
||||||
|
|
||||||
| Built by [Root](https://therootcompany.com) for [Hub](https://rootprojects.org/hub)
|
| Built by [Root](https://therootcompany.com) for [Hub](https://rootprojects.org/hub)
|
||||||
|
|
||||||
## Automated Certificate Management Environment
|
ACME.js is a _low-level_ client for Let's Encrypt.
|
||||||
|
|
||||||
ACME ([RFC 8555](https://tools.ietf.org/html/rfc8555)) is the protocol that powers **Let's Encrypt**.
|
|
||||||
|
|
||||||
ACME.js is a _low-level_ client that speaks RFC 8555 to get Free SSL certificates through Let's Encrypt.
|
|
||||||
|
|
||||||
Looking for an **easy**, _high-level_ client? Check out [Greenlock.js](https://git.rootprojects.org/root/greenlock.js).
|
Looking for an **easy**, _high-level_ client? Check out [Greenlock.js](https://git.rootprojects.org/root/greenlock.js).
|
||||||
|
|
||||||
# Quick Start
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var acme = ACME.create({ maintainerEmail, packageAgent, notify });
|
var acme = ACME.create({ maintainerEmail, packageAgent, notify });
|
||||||
await acme.init(directoryUrl);
|
await acme.init(directoryUrl);
|
||||||
|
@ -103,7 +97,7 @@ The public API encapsulates the three high-level steps of the ACME protocol:
|
||||||
- Challenge Presentation
|
- Challenge Presentation
|
||||||
- Certificate Redemption
|
- Certificate Redemption
|
||||||
|
|
||||||
## API Overview
|
## Overview
|
||||||
|
|
||||||
The core API can be show in just four functions:
|
The core API can be show in just four functions:
|
||||||
|
|
||||||
|
@ -183,7 +177,7 @@ These `notify` events are intended for _logging_ and debugging, NOT as a data AP
|
||||||
Note: DO NOT rely on **undocumented properties**. They are experimental and **will break**.
|
Note: DO NOT rely on **undocumented properties**. They are experimental and **will break**.
|
||||||
If you have a use case for a particular property **open an issue** - we can lock it down and document it.
|
If you have a use case for a particular property **open an issue** - we can lock it down and document it.
|
||||||
|
|
||||||
# Example (Full Walkthrough)
|
# Example
|
||||||
|
|
||||||
### See [examples/README.md](https://git.rootprojects.org/root/acme.js/src/branch/master/examples/README.md)
|
### See [examples/README.md](https://git.rootprojects.org/root/acme.js/src/branch/master/examples/README.md)
|
||||||
|
|
||||||
|
|
41
account.js
41
account.js
|
@ -5,13 +5,12 @@ var U = require('./utils.js');
|
||||||
|
|
||||||
var Keypairs = require('@root/keypairs');
|
var Keypairs = require('@root/keypairs');
|
||||||
var Enc = require('@root/encoding/bytes');
|
var Enc = require('@root/encoding/bytes');
|
||||||
var agreers = {};
|
|
||||||
|
|
||||||
A._getAccountKid = function (me, options) {
|
A._getAccountKid = function(me, options) {
|
||||||
// It's just fine if there's no account, we'll go get the key id we need via the existing key
|
// It's just fine if there's no account, we'll go get the key id we need via the existing key
|
||||||
var kid =
|
var kid =
|
||||||
options.kid ||
|
options.kid ||
|
||||||
(options.account && options.account.key && options.account.key.kid);
|
(options.account && (options.account.key && options.account.key.kid));
|
||||||
|
|
||||||
if (kid) {
|
if (kid) {
|
||||||
return Promise.resolve(kid);
|
return Promise.resolve(kid);
|
||||||
|
@ -19,7 +18,7 @@ A._getAccountKid = function (me, options) {
|
||||||
|
|
||||||
//return Promise.reject(new Error("must include KeyID"));
|
//return Promise.reject(new Error("must include KeyID"));
|
||||||
// This is an idempotent request. It'll return the same account for the same public key.
|
// This is an idempotent request. It'll return the same account for the same public key.
|
||||||
return A._registerAccount(me, options).then(function (account) {
|
return A._registerAccount(me, options).then(function(account) {
|
||||||
return account.key.kid;
|
return account.key.kid;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -44,7 +43,7 @@ A._getAccountKid = function (me, options) {
|
||||||
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
|
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
A._registerAccount = function (me, options) {
|
A._registerAccount = function(me, options) {
|
||||||
//#console.debug('[ACME.js] accounts.create');
|
//#console.debug('[ACME.js] accounts.create');
|
||||||
|
|
||||||
function agree(agreed) {
|
function agree(agreed) {
|
||||||
|
@ -58,7 +57,7 @@ A._registerAccount = function (me, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccount() {
|
function getAccount() {
|
||||||
return U._importKeypair(options.accountKey).then(function (pair) {
|
return U._importKeypair(options.accountKey).then(function(pair) {
|
||||||
var contact;
|
var contact;
|
||||||
if (options.contact) {
|
if (options.contact) {
|
||||||
contact = options.contact.slice(0);
|
contact = options.contact.slice(0);
|
||||||
|
@ -73,14 +72,14 @@ A._registerAccount = function (me, options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
var pub = pair.public;
|
var pub = pair.public;
|
||||||
return attachExtAcc(pub, accountRequest).then(function (accReq) {
|
return attachExtAcc(pub, accountRequest).then(function(accReq) {
|
||||||
var payload = JSON.stringify(accReq);
|
var payload = JSON.stringify(accReq);
|
||||||
return U._jwsRequest(me, {
|
return U._jwsRequest(me, {
|
||||||
accountKey: options.accountKey,
|
accountKey: options.accountKey,
|
||||||
url: me._directoryUrls.newAccount,
|
url: me._directoryUrls.newAccount,
|
||||||
protected: { kid: false, jwk: pair.public },
|
protected: { kid: false, jwk: pair.public },
|
||||||
payload: Enc.strToBuf(payload)
|
payload: Enc.strToBuf(payload)
|
||||||
}).then(function (resp) {
|
}).then(function(resp) {
|
||||||
var account = resp.body;
|
var account = resp.body;
|
||||||
|
|
||||||
if (resp.statusCode < 200 || resp.statusCode >= 300) {
|
if (resp.statusCode < 200 || resp.statusCode >= 300) {
|
||||||
|
@ -127,27 +126,20 @@ A._registerAccount = function (me, options) {
|
||||||
url: me._directoryUrls.newAccount
|
url: me._directoryUrls.newAccount
|
||||||
},
|
},
|
||||||
payload: Enc.strToBuf(JSON.stringify(pubkey))
|
payload: Enc.strToBuf(JSON.stringify(pubkey))
|
||||||
}).then(function (jws) {
|
}).then(function(jws) {
|
||||||
accountRequest.externalAccountBinding = jws;
|
accountRequest.externalAccountBinding = jws;
|
||||||
return accountRequest;
|
return accountRequest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(function () {
|
.then(function() {
|
||||||
//#console.debug('[ACME.js] agreeToTerms');
|
//#console.debug('[ACME.js] agreeToTerms');
|
||||||
var agreeToTerms = options.agreeToTerms;
|
var agreeToTerms = options.agreeToTerms;
|
||||||
if (!agreeToTerms) {
|
if (!agreeToTerms) {
|
||||||
agreeToTerms = function (terms) {
|
agreeToTerms = function(terms) {
|
||||||
if (agreers[options.subscriberEmail]) {
|
console.log(
|
||||||
return true;
|
'By using this software you accept this Subscriber Agreement and Terms of Service:'
|
||||||
}
|
|
||||||
agreers[options.subscriberEmail] = true;
|
|
||||||
console.info();
|
|
||||||
console.info(
|
|
||||||
'By using this software you (' +
|
|
||||||
options.subscriberEmail +
|
|
||||||
') are agreeing to the following:'
|
|
||||||
);
|
);
|
||||||
console.info(
|
console.info(
|
||||||
'ACME Subscriber Agreement:',
|
'ACME Subscriber Agreement:',
|
||||||
|
@ -155,19 +147,18 @@ A._registerAccount = function (me, options) {
|
||||||
);
|
);
|
||||||
console.info(
|
console.info(
|
||||||
'Greenlock/ACME.js Terms of Use:',
|
'Greenlock/ACME.js Terms of Use:',
|
||||||
terms.acmeJsTermsUrl
|
terms.terms.acmeJsTermsUrl
|
||||||
);
|
);
|
||||||
console.info();
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
} else if (true === agreeToTerms) {
|
} else if (true === agreeToTerms) {
|
||||||
agreeToTerms = function (terms) {
|
agreeToTerms = function(terms) {
|
||||||
return terms && true;
|
return terms && true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return agreeToTerms({
|
return agreeToTerms({
|
||||||
acmeSubscriberTermsUrl: me._tos,
|
acmeSubscriberTosUrl: me._tos,
|
||||||
acmeJsTermsUrl: 'https://rootprojects.org/legal/#terms'
|
acmeJsTosUrl: 'https://rootprojects.org/legal/#terms'
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(agree)
|
.then(agree)
|
||||||
|
|
290
acme.js
290
acme.js
|
@ -43,7 +43,7 @@ ACME.create = function create(me) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!me.dns01) {
|
if (!me.dns01) {
|
||||||
me.dns01 = function (ch) {
|
me.dns01 = function(ch) {
|
||||||
return native._dns01(me, ch);
|
return native._dns01(me, ch);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ ACME.create = function create(me) {
|
||||||
if (!me._baseUrl) {
|
if (!me._baseUrl) {
|
||||||
me._baseUrl = '';
|
me._baseUrl = '';
|
||||||
}
|
}
|
||||||
me.http01 = function (ch) {
|
me.http01 = function(ch) {
|
||||||
return native._http01(me, ch);
|
return native._http01(me, ch);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,11 @@ ACME.create = function create(me) {
|
||||||
me.__request = http.request;
|
me.__request = http.request;
|
||||||
}
|
}
|
||||||
// passed to dependencies
|
// passed to dependencies
|
||||||
me.request = function (opts) {
|
me.request = function(opts) {
|
||||||
return U._request(me, opts);
|
return U._request(me, opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
me.init = function (opts) {
|
me.init = function(opts) {
|
||||||
M.init(me);
|
M.init(me);
|
||||||
|
|
||||||
function fin(dir) {
|
function fin(dir) {
|
||||||
|
@ -90,14 +90,14 @@ ACME.create = function create(me) {
|
||||||
if (!me.skipChallengeTest) {
|
if (!me.skipChallengeTest) {
|
||||||
p = native._canCheck(me);
|
p = native._canCheck(me);
|
||||||
}
|
}
|
||||||
return p.then(function () {
|
return p.then(function() {
|
||||||
return ACME._directory(me).then(function (resp) {
|
return ACME._directory(me).then(function(resp) {
|
||||||
return fin(resp.body);
|
return fin(resp.body);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
me.accounts = {
|
me.accounts = {
|
||||||
create: function (options) {
|
create: function(options) {
|
||||||
try {
|
try {
|
||||||
return A._registerAccount(me, options);
|
return A._registerAccount(me, options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -126,8 +126,8 @@ ACME.create = function create(me) {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
me.certificates = {
|
me.certificates = {
|
||||||
create: function (options) {
|
create: function(options) {
|
||||||
return A._getAccountKid(me, options).then(function (kid) {
|
return A._getAccountKid(me, options).then(function(kid) {
|
||||||
ACME._normalizePresenters(me, options, options.challenges);
|
ACME._normalizePresenters(me, options, options.challenges);
|
||||||
return ACME._getCertificate(me, options, kid);
|
return ACME._getCertificate(me, options, kid);
|
||||||
});
|
});
|
||||||
|
@ -143,9 +143,9 @@ ACME.challengePrefixes = {
|
||||||
'dns-01': '_acme-challenge'
|
'dns-01': '_acme-challenge'
|
||||||
};
|
};
|
||||||
ACME.challengeTests = {
|
ACME.challengeTests = {
|
||||||
'http-01': function (me, auth) {
|
'http-01': function(me, auth) {
|
||||||
var ch = auth.challenge;
|
var ch = auth.challenge;
|
||||||
return me.http01(ch).then(function (keyAuth) {
|
return me.http01(ch).then(function(keyAuth) {
|
||||||
var err;
|
var err;
|
||||||
|
|
||||||
// TODO limit the number of bytes that are allowed to be downloaded
|
// TODO limit the number of bytes that are allowed to be downloaded
|
||||||
|
@ -170,14 +170,14 @@ ACME.challengeTests = {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'dns-01': function (me, auth) {
|
'dns-01': function(me, auth) {
|
||||||
// remove leading *. on wildcard domains
|
// remove leading *. on wildcard domains
|
||||||
var ch = auth.challenge;
|
var ch = auth.challenge;
|
||||||
return me.dns01(ch).then(function (ans) {
|
return me.dns01(ch).then(function(ans) {
|
||||||
var err;
|
var err;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ans.answer.some(function (txt) {
|
ans.answer.some(function(txt) {
|
||||||
return ch.dnsAuthorization === txt.data[0];
|
return ch.dnsAuthorization === txt.data[0];
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
|
@ -199,7 +199,7 @@ ACME.challengeTests = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._directory = function (me) {
|
ACME._directory = function(me) {
|
||||||
// TODO cache the directory URL
|
// TODO cache the directory URL
|
||||||
|
|
||||||
// GET-as-GET ok
|
// GET-as-GET ok
|
||||||
|
@ -210,13 +210,13 @@ ACME._directory = function (me) {
|
||||||
// postChallenge
|
// postChallenge
|
||||||
// finalizeOrder
|
// finalizeOrder
|
||||||
// getCertificate
|
// getCertificate
|
||||||
ACME._getCertificate = function (me, options, kid) {
|
ACME._getCertificate = function(me, options, kid) {
|
||||||
//#console.debug('[ACME.js] certificates.create');
|
//#console.debug('[ACME.js] certificates.create');
|
||||||
return ACME._orderCert(me, options, kid).then(function (order) {
|
return ACME._orderCert(me, options, kid).then(function(order) {
|
||||||
return ACME._finalizeOrder(me, options, kid, order);
|
return ACME._finalizeOrder(me, options, kid, order);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
ACME._normalizePresenters = function (me, options, presenters) {
|
ACME._normalizePresenters = function(me, options, presenters) {
|
||||||
// Prefer this order for efficiency:
|
// Prefer this order for efficiency:
|
||||||
// * http-01 is the fasest
|
// * http-01 is the fasest
|
||||||
// * tls-alpn-01 is for networks that don't allow plain traffic
|
// * tls-alpn-01 is for networks that don't allow plain traffic
|
||||||
|
@ -224,7 +224,7 @@ ACME._normalizePresenters = function (me, options, presenters) {
|
||||||
// but is required for private networks and wildcards
|
// but is required for private networks and wildcards
|
||||||
var presenterTypes = Object.keys(options.challenges || {});
|
var presenterTypes = Object.keys(options.challenges || {});
|
||||||
options._presenterTypes = ['http-01', 'tls-alpn-01', 'dns-01'].filter(
|
options._presenterTypes = ['http-01', 'tls-alpn-01', 'dns-01'].filter(
|
||||||
function (typ) {
|
function(typ) {
|
||||||
return -1 !== presenterTypes.indexOf(typ);
|
return -1 !== presenterTypes.indexOf(typ);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -244,7 +244,7 @@ ACME._normalizePresenters = function (me, options, presenters) {
|
||||||
ACME._propagationDelayWarning = true;
|
ACME._propagationDelayWarning = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object.keys(presenters || {}).forEach(function (k) {
|
Object.keys(presenters || {}).forEach(function(k) {
|
||||||
var ch = presenters[k];
|
var ch = presenters[k];
|
||||||
var warned = false;
|
var warned = false;
|
||||||
|
|
||||||
|
@ -280,9 +280,9 @@ ACME._normalizePresenters = function (me, options, presenters) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function promisify(fn) {
|
function promisify(fn) {
|
||||||
return function (opts) {
|
return function(opts) {
|
||||||
new Promise(function (resolve, reject) {
|
new Promise(function(resolve, reject) {
|
||||||
fn(opts, function (err, result) {
|
fn(opts, function(err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
|
@ -344,7 +344,7 @@ ACME._normalizePresenters = function (me, options, presenters) {
|
||||||
"signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
|
"signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
ACME._getAuthorization = function (me, options, kid, zonenames, authUrl) {
|
ACME._getAuthorization = function(me, options, kid, zonenames, authUrl) {
|
||||||
//#console.debug('\n[DEBUG] getAuthorization\n');
|
//#console.debug('\n[DEBUG] getAuthorization\n');
|
||||||
|
|
||||||
return U._jwsRequest(me, {
|
return U._jwsRequest(me, {
|
||||||
|
@ -352,7 +352,7 @@ ACME._getAuthorization = function (me, options, kid, zonenames, authUrl) {
|
||||||
url: authUrl,
|
url: authUrl,
|
||||||
protected: { kid: kid },
|
protected: { kid: kid },
|
||||||
payload: ''
|
payload: ''
|
||||||
}).then(function (resp) {
|
}).then(function(resp) {
|
||||||
// Pre-emptive rather than lazy for interfaces that need to show the
|
// Pre-emptive rather than lazy for interfaces that need to show the
|
||||||
// challenges to the user first
|
// challenges to the user first
|
||||||
return ACME._computeAuths(
|
return ACME._computeAuths(
|
||||||
|
@ -362,7 +362,7 @@ ACME._getAuthorization = function (me, options, kid, zonenames, authUrl) {
|
||||||
resp.body,
|
resp.body,
|
||||||
zonenames,
|
zonenames,
|
||||||
false
|
false
|
||||||
).then(function (auths) {
|
).then(function(auths) {
|
||||||
resp.body._rawChallenges = resp.body.challenges;
|
resp.body._rawChallenges = resp.body.challenges;
|
||||||
resp.body.challenges = auths;
|
resp.body.challenges = auths;
|
||||||
return resp.body;
|
return resp.body;
|
||||||
|
@ -370,7 +370,7 @@ ACME._getAuthorization = function (me, options, kid, zonenames, authUrl) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._testChallengeOptions = function () {
|
ACME._testChallengeOptions = function() {
|
||||||
// we want this to be the same for the whole group
|
// we want this to be the same for the whole group
|
||||||
var chToken = ACME._prnd(16);
|
var chToken = ACME._prnd(16);
|
||||||
return [
|
return [
|
||||||
|
@ -396,9 +396,9 @@ ACME._testChallengeOptions = function () {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._thumber = function (options, thumb) {
|
ACME._thumber = function(options, thumb) {
|
||||||
var thumbPromise;
|
var thumbPromise;
|
||||||
return function (key) {
|
return function(key) {
|
||||||
if (thumb) {
|
if (thumb) {
|
||||||
return Promise.resolve(thumb);
|
return Promise.resolve(thumb);
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,7 @@ ACME._thumber = function (options, thumb) {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
key = options.accountKey || options.accountKeypair;
|
key = options.accountKey || options.accountKeypair;
|
||||||
}
|
}
|
||||||
thumbPromise = U._importKeypair(key).then(function (pair) {
|
thumbPromise = U._importKeypair(key).then(function(pair) {
|
||||||
return Keypairs.thumbprint({
|
return Keypairs.thumbprint({
|
||||||
jwk: pair.public
|
jwk: pair.public
|
||||||
});
|
});
|
||||||
|
@ -417,9 +417,9 @@ ACME._thumber = function (options, thumb) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._dryRun = function (me, realOptions, zonenames) {
|
ACME._dryRun = function(me, realOptions, zonenames) {
|
||||||
var noopts = {};
|
var noopts = {};
|
||||||
Object.keys(realOptions).forEach(function (key) {
|
Object.keys(realOptions).forEach(function(key) {
|
||||||
noopts[key] = realOptions[key];
|
noopts[key] = realOptions[key];
|
||||||
});
|
});
|
||||||
noopts.order = {};
|
noopts.order = {};
|
||||||
|
@ -428,20 +428,20 @@ ACME._dryRun = function (me, realOptions, zonenames) {
|
||||||
var getThumbprint = ACME._thumber(noopts, '');
|
var getThumbprint = ACME._thumber(noopts, '');
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
noopts.domains.map(function (identifierValue) {
|
noopts.domains.map(function(identifierValue) {
|
||||||
// TODO we really only need one to pass, not all to pass
|
// TODO we really only need one to pass, not all to pass
|
||||||
var challenges = ACME._testChallengeOptions();
|
var challenges = ACME._testChallengeOptions();
|
||||||
var wild = '*.' === identifierValue.slice(0, 2);
|
var wild = '*.' === identifierValue.slice(0, 2);
|
||||||
if (wild) {
|
if (wild) {
|
||||||
challenges = challenges.filter(function (ch) {
|
challenges = challenges.filter(function(ch) {
|
||||||
return ch._wildcard;
|
return ch._wildcard;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
challenges = challenges.filter(function (auth) {
|
challenges = challenges.filter(function(auth) {
|
||||||
return me._canCheck[auth.type];
|
return me._canCheck[auth.type];
|
||||||
});
|
});
|
||||||
|
|
||||||
return getThumbprint().then(function (accountKeyThumb) {
|
return getThumbprint().then(function(accountKeyThumb) {
|
||||||
var resp = {
|
var resp = {
|
||||||
body: {
|
body: {
|
||||||
identifier: {
|
identifier: {
|
||||||
|
@ -464,32 +464,31 @@ ACME._dryRun = function (me, realOptions, zonenames) {
|
||||||
resp.body,
|
resp.body,
|
||||||
zonenames,
|
zonenames,
|
||||||
dryrun
|
dryrun
|
||||||
).then(function (auths) {
|
).then(function(auths) {
|
||||||
resp.body.challenges = auths;
|
resp.body.challenges = auths;
|
||||||
return resp.body;
|
return resp.body;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
).then(function (claims) {
|
).then(function(claims) {
|
||||||
var selected = [];
|
var selected = [];
|
||||||
noopts.order._claims = claims.slice(0);
|
noopts.order._claims = claims.slice(0);
|
||||||
noopts.notify = function (ev, params) {
|
noopts.notify = function(ev, params) {
|
||||||
if ('_challenge_select' === ev) {
|
if ('challenge_select' === ev) {
|
||||||
selected.push(params.challenge);
|
selected.push(params.challenge);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
selected.forEach(function (ch) {
|
selected.forEach(function(ch) {
|
||||||
ACME._notify(me, noopts, 'challenge_remove', {
|
ACME._notify(me, noopts, 'challenge_remove', {
|
||||||
altname: ch.altname,
|
altname: ch.altname,
|
||||||
type: ch.type
|
type: ch.type
|
||||||
//challenge: ch
|
//challenge: ch
|
||||||
});
|
});
|
||||||
// ignore promise return
|
|
||||||
noopts.challenges[ch.type]
|
noopts.challenges[ch.type]
|
||||||
.remove({ challenge: ch })
|
.remove({ challenge: ch })
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
err.action = 'challenge_remove';
|
err.action = 'challenge_remove';
|
||||||
err.altname = ch.altname;
|
err.altname = ch.altname;
|
||||||
err.type = ch.type;
|
err.type = ch.type;
|
||||||
|
@ -499,7 +498,7 @@ ACME._dryRun = function (me, realOptions, zonenames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ACME._setChallenges(me, noopts, noopts.order)
|
return ACME._setChallenges(me, noopts, noopts.order)
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
clear();
|
clear();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
|
@ -510,12 +509,12 @@ ACME._dryRun = function (me, realOptions, zonenames) {
|
||||||
// Get the list of challenge types we can validate,
|
// Get the list of challenge types we can validate,
|
||||||
// which is already ordered by preference.
|
// which is already ordered by preference.
|
||||||
// Select the first matching offered challenge type
|
// Select the first matching offered challenge type
|
||||||
ACME._chooseChallenge = function (options, results) {
|
ACME._chooseChallenge = function(options, results) {
|
||||||
// For each of the challenge types that we support
|
// For each of the challenge types that we support
|
||||||
var challenge;
|
var challenge;
|
||||||
options._presenterTypes.some(function (chType) {
|
options._presenterTypes.some(function(chType) {
|
||||||
// And for each of the challenge types that are allowed
|
// And for each of the challenge types that are allowed
|
||||||
return results.challenges.some(function (ch) {
|
return results.challenges.some(function(ch) {
|
||||||
// Check to see if there are any matches
|
// Check to see if there are any matches
|
||||||
if (ch.type === chType) {
|
if (ch.type === chType) {
|
||||||
challenge = ch;
|
challenge = ch;
|
||||||
|
@ -527,7 +526,7 @@ ACME._chooseChallenge = function (options, results) {
|
||||||
return challenge;
|
return challenge;
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._getZones = function (me, challenges, domains) {
|
ACME._getZones = function(me, challenges, domains) {
|
||||||
var presenter = challenges['dns-01'];
|
var presenter = challenges['dns-01'];
|
||||||
if (!presenter) {
|
if (!presenter) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
|
@ -538,7 +537,7 @@ ACME._getZones = function (me, challenges, domains) {
|
||||||
|
|
||||||
// a little bit of random to ensure that getZones()
|
// a little bit of random to ensure that getZones()
|
||||||
// actually returns the zones and not the hosts as zones
|
// actually returns the zones and not the hosts as zones
|
||||||
var dnsHosts = domains.map(function (d) {
|
var dnsHosts = domains.map(function(d) {
|
||||||
var rnd = ACME._prnd(2);
|
var rnd = ACME._prnd(2);
|
||||||
return rnd + '.' + d;
|
return rnd + '.' + d;
|
||||||
});
|
});
|
||||||
|
@ -552,7 +551,7 @@ ACME._getZones = function (me, challenges, domains) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._challengesMap = { 'http-01': 0, 'dns-01': 0, 'tls-alpn-01': 0 };
|
ACME._challengesMap = { 'http-01': 0, 'dns-01': 0, 'tls-alpn-01': 0 };
|
||||||
ACME._computeAuths = function (me, options, thumb, authz, zonenames, dryrun) {
|
ACME._computeAuths = function(me, options, thumb, authz, zonenames, dryrun) {
|
||||||
// we don't poison the dns cache with our dummy request
|
// we don't poison the dns cache with our dummy request
|
||||||
var dnsPrefix = ACME.challengePrefixes['dns-01'];
|
var dnsPrefix = ACME.challengePrefixes['dns-01'];
|
||||||
if (dryrun) {
|
if (dryrun) {
|
||||||
|
@ -565,7 +564,7 @@ ACME._computeAuths = function (me, options, thumb, authz, zonenames, dryrun) {
|
||||||
var getThumbprint = ACME._thumber(options, thumb);
|
var getThumbprint = ACME._thumber(options, thumb);
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
authz.challenges.map(function (challenge) {
|
authz.challenges.map(function(challenge) {
|
||||||
// Don't do extra work for challenges that we can't satisfy
|
// Don't do extra work for challenges that we can't satisfy
|
||||||
var _types = options._presenterTypes;
|
var _types = options._presenterTypes;
|
||||||
if (_types && !_types.includes(challenge.type)) {
|
if (_types && !_types.includes(challenge.type)) {
|
||||||
|
@ -576,14 +575,14 @@ ACME._computeAuths = function (me, options, thumb, authz, zonenames, dryrun) {
|
||||||
|
|
||||||
// straight copy from the new order response
|
// straight copy from the new order response
|
||||||
// { identifier, status, expires, challenges, wildcard }
|
// { identifier, status, expires, challenges, wildcard }
|
||||||
Object.keys(authz).forEach(function (key) {
|
Object.keys(authz).forEach(function(key) {
|
||||||
auth[key] = authz[key];
|
auth[key] = authz[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
// copy from the challenge we've chosen
|
// copy from the challenge we've chosen
|
||||||
// { type, status, url, token }
|
// { type, status, url, token }
|
||||||
// (note the duplicate status overwrites the one above, but they should be the same)
|
// (note the duplicate status overwrites the one above, but they should be the same)
|
||||||
Object.keys(challenge).forEach(function (key) {
|
Object.keys(challenge).forEach(function(key) {
|
||||||
// don't confused devs with the id url
|
// don't confused devs with the id url
|
||||||
auth[key] = challenge[key];
|
auth[key] = challenge[key];
|
||||||
});
|
});
|
||||||
|
@ -602,19 +601,19 @@ ACME._computeAuths = function (me, options, thumb, authz, zonenames, dryrun) {
|
||||||
challenge: auth,
|
challenge: auth,
|
||||||
zone: zone,
|
zone: zone,
|
||||||
dnsPrefix: dnsPrefix
|
dnsPrefix: dnsPrefix
|
||||||
}).then(function (resp) {
|
}).then(function(resp) {
|
||||||
Object.keys(resp).forEach(function (k) {
|
Object.keys(resp).forEach(function(k) {
|
||||||
auth[k] = resp[k];
|
auth[k] = resp[k];
|
||||||
});
|
});
|
||||||
return auth;
|
return auth;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
).then(function (auths) {
|
).then(function(auths) {
|
||||||
return auths.filter(Boolean);
|
return auths.filter(Boolean);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME.computeChallenge = function (opts) {
|
ACME.computeChallenge = function(opts) {
|
||||||
var auth = opts.challenge;
|
var auth = opts.challenge;
|
||||||
var hostname = auth.hostname || opts.hostname;
|
var hostname = auth.hostname || opts.hostname;
|
||||||
var zone = opts.zone;
|
var zone = opts.zone;
|
||||||
|
@ -623,7 +622,7 @@ ACME.computeChallenge = function (opts) {
|
||||||
var getThumbprint = opts._getThumbprint || ACME._thumber(opts, thumb);
|
var getThumbprint = opts._getThumbprint || ACME._thumber(opts, thumb);
|
||||||
var dnsPrefix = opts.dnsPrefix || ACME.challengePrefixes['dns-01'];
|
var dnsPrefix = opts.dnsPrefix || ACME.challengePrefixes['dns-01'];
|
||||||
|
|
||||||
return getThumbprint(accountKey).then(function (thumb) {
|
return getThumbprint(accountKey).then(function(thumb) {
|
||||||
var resp = {};
|
var resp = {};
|
||||||
resp.thumbprint = thumb;
|
resp.thumbprint = thumb;
|
||||||
// keyAuthorization = token + '.' + base64url(JWK_Thumbprint(accountKey))
|
// keyAuthorization = token + '.' + base64url(JWK_Thumbprint(accountKey))
|
||||||
|
@ -651,10 +650,10 @@ ACME.computeChallenge = function (opts) {
|
||||||
// _as part of_ the decision making process
|
// _as part of_ the decision making process
|
||||||
return sha2
|
return sha2
|
||||||
.sum(256, resp.keyAuthorization)
|
.sum(256, resp.keyAuthorization)
|
||||||
.then(function (hash) {
|
.then(function(hash) {
|
||||||
return Enc.bufToUrlBase64(Uint8Array.from(hash));
|
return Enc.bufToUrlBase64(Uint8Array.from(hash));
|
||||||
})
|
})
|
||||||
.then(function (hash64) {
|
.then(function(hash64) {
|
||||||
resp.dnsHost = dnsPrefix + '.' + hostname; // .replace('*.', '');
|
resp.dnsHost = dnsPrefix + '.' + hostname; // .replace('*.', '');
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
|
@ -674,7 +673,7 @@ ACME.computeChallenge = function (opts) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._untame = function (name, wild) {
|
ACME._untame = function(name, wild) {
|
||||||
if (wild) {
|
if (wild) {
|
||||||
name = '*.' + name.replace('*.', '');
|
name = '*.' + name.replace('*.', '');
|
||||||
}
|
}
|
||||||
|
@ -682,7 +681,7 @@ ACME._untame = function (name, wild) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
|
// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
|
||||||
ACME._postChallenge = function (me, options, kid, auth) {
|
ACME._postChallenge = function(me, options, kid, auth) {
|
||||||
var RETRY_INTERVAL = me.retryInterval || 1000;
|
var RETRY_INTERVAL = me.retryInterval || 1000;
|
||||||
var DEAUTH_INTERVAL = me.deauthWait || 10 * 1000;
|
var DEAUTH_INTERVAL = me.deauthWait || 10 * 1000;
|
||||||
var MAX_POLL = me.retryPoll || 8;
|
var MAX_POLL = me.retryPoll || 8;
|
||||||
|
@ -716,7 +715,7 @@ ACME._postChallenge = function (me, options, kid, auth) {
|
||||||
url: auth.url,
|
url: auth.url,
|
||||||
protected: { kid: kid },
|
protected: { kid: kid },
|
||||||
payload: Enc.strToBuf(JSON.stringify({ status: 'deactivated' }))
|
payload: Enc.strToBuf(JSON.stringify({ status: 'deactivated' }))
|
||||||
}).then(function (/*#resp*/) {
|
}).then(function(/*#resp*/) {
|
||||||
//#console.debug('deactivate challenge: resp.body:');
|
//#console.debug('deactivate challenge: resp.body:');
|
||||||
//#console.debug(resp.body);
|
//#console.debug(resp.body);
|
||||||
return ACME._wait(DEAUTH_INTERVAL);
|
return ACME._wait(DEAUTH_INTERVAL);
|
||||||
|
@ -756,8 +755,12 @@ ACME._postChallenge = function (me, options, kid, auth) {
|
||||||
altname: altname
|
altname: altname
|
||||||
});
|
});
|
||||||
|
|
||||||
// State can be pending while waiting ACME server to transition to
|
if ('processing' === resp.body.status) {
|
||||||
// processing
|
//#console.debug('poll: again', auth.url);
|
||||||
|
return ACME._wait(RETRY_INTERVAL).then(pollStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This state should never occur
|
||||||
if ('pending' === resp.body.status) {
|
if ('pending' === resp.body.status) {
|
||||||
if (count >= MAX_PEND) {
|
if (count >= MAX_PEND) {
|
||||||
return ACME._wait(RETRY_INTERVAL)
|
return ACME._wait(RETRY_INTERVAL)
|
||||||
|
@ -765,25 +768,13 @@ ACME._postChallenge = function (me, options, kid, auth) {
|
||||||
.then(respondToChallenge);
|
.then(respondToChallenge);
|
||||||
}
|
}
|
||||||
//#console.debug('poll: again', auth.url);
|
//#console.debug('poll: again', auth.url);
|
||||||
return ACME._wait(RETRY_INTERVAL).then(pollStatus);
|
return ACME._wait(RETRY_INTERVAL).then(respondToChallenge);
|
||||||
}
|
|
||||||
|
|
||||||
if ('processing' === resp.body.status) {
|
|
||||||
//#console.debug('poll: again', auth.url);
|
|
||||||
return ACME._wait(RETRY_INTERVAL).then(pollStatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// REMOVE DNS records as soon as the state is non-processing
|
// REMOVE DNS records as soon as the state is non-processing
|
||||||
// (valid or invalid or other)
|
// (valid or invalid or other)
|
||||||
try {
|
try {
|
||||||
options.challenges[auth.type]
|
options.challenges[auth.type].remove({ challenge: auth });
|
||||||
.remove({ challenge: auth })
|
|
||||||
.catch(function (err) {
|
|
||||||
err.action = 'challenge_remove';
|
|
||||||
err.altname = auth.altname;
|
|
||||||
err.type = auth.type;
|
|
||||||
ACME._notify(me, options, 'error', err);
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if ('valid' === resp.body.status) {
|
if ('valid' === resp.body.status) {
|
||||||
|
@ -859,7 +850,7 @@ ACME._postChallenge = function (me, options, kid, auth) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// options = { domains, claims, challenges }
|
// options = { domains, claims, challenges }
|
||||||
ACME._setChallenges = function (me, options, order) {
|
ACME._setChallenges = function(me, options, order) {
|
||||||
var claims = order._claims.slice(0);
|
var claims = order._claims.slice(0);
|
||||||
var valids = [];
|
var valids = [];
|
||||||
var auths = [];
|
var auths = [];
|
||||||
|
@ -876,11 +867,11 @@ ACME._setChallenges = function (me, options, order) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(function () {
|
.then(function() {
|
||||||
// For any challenges that are already valid,
|
// For any challenges that are already valid,
|
||||||
// add to the list and skip any checks.
|
// add to the list and skip any checks.
|
||||||
if (
|
if (
|
||||||
claim.challenges.some(function (ch) {
|
claim.challenges.some(function(ch) {
|
||||||
if ('valid' === ch.status) {
|
if ('valid' === ch.status) {
|
||||||
valids.push(ch);
|
valids.push(ch);
|
||||||
return true;
|
return true;
|
||||||
|
@ -902,15 +893,6 @@ ACME._setChallenges = function (me, options, order) {
|
||||||
placed.push(selected);
|
placed.push(selected);
|
||||||
ACME._notify(me, options, 'challenge_select', {
|
ACME._notify(me, options, 'challenge_select', {
|
||||||
// API-locked
|
// API-locked
|
||||||
altname: ACME._untame(
|
|
||||||
claim.identifier.value,
|
|
||||||
claim.wildcard
|
|
||||||
),
|
|
||||||
type: selected.type,
|
|
||||||
dnsHost: selected.dnsHost,
|
|
||||||
keyAuthorization: selected.keyAuthorization
|
|
||||||
});
|
|
||||||
ACME._notify(me, options, '_challenge_select', {
|
|
||||||
altname: ACME._untame(
|
altname: ACME._untame(
|
||||||
claim.identifier.value,
|
claim.identifier.value,
|
||||||
claim.wildcard
|
claim.wildcard
|
||||||
|
@ -960,7 +942,7 @@ ACME._setChallenges = function (me, options, order) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ACME.challengeTests[auth.type](me, { challenge: auth })
|
return ACME.challengeTests[auth.type](me, { challenge: auth })
|
||||||
.then(function () {
|
.then(function() {
|
||||||
valids.push(auth);
|
valids.push(auth);
|
||||||
})
|
})
|
||||||
.then(checkNext);
|
.then(checkNext);
|
||||||
|
@ -969,7 +951,7 @@ ACME._setChallenges = function (me, options, order) {
|
||||||
function removeAll(ch) {
|
function removeAll(ch) {
|
||||||
options.challenges[ch.type]
|
options.challenges[ch.type]
|
||||||
.remove({ challenge: ch })
|
.remove({ challenge: ch })
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
err.action = 'challenge_remove';
|
err.action = 'challenge_remove';
|
||||||
err.altname = ch.altname;
|
err.altname = ch.altname;
|
||||||
err.type = ch.type;
|
err.type = ch.type;
|
||||||
|
@ -982,7 +964,7 @@ ACME._setChallenges = function (me, options, order) {
|
||||||
return setNext()
|
return setNext()
|
||||||
.then(waitAll)
|
.then(waitAll)
|
||||||
.then(checkNext)
|
.then(checkNext)
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
if (!options.debug) {
|
if (!options.debug) {
|
||||||
placed.forEach(removeAll);
|
placed.forEach(removeAll);
|
||||||
}
|
}
|
||||||
|
@ -990,7 +972,7 @@ ACME._setChallenges = function (me, options, order) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._presentChallenges = function (me, options, kid, readyToPresent) {
|
ACME._presentChallenges = function(me, options, kid, readyToPresent) {
|
||||||
// Actually sets the challenge via ACME
|
// Actually sets the challenge via ACME
|
||||||
function challengeNext() {
|
function challengeNext() {
|
||||||
// First set, First presented
|
// First set, First presented
|
||||||
|
@ -1003,17 +985,24 @@ ACME._presentChallenges = function (me, options, kid, readyToPresent) {
|
||||||
|
|
||||||
// BTW, these are done serially rather than parallel on purpose
|
// BTW, these are done serially rather than parallel on purpose
|
||||||
// (rate limits, propagation delays, etc)
|
// (rate limits, propagation delays, etc)
|
||||||
return challengeNext().then(function () {
|
return challengeNext().then(function() {
|
||||||
return readyToPresent;
|
return readyToPresent;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._pollOrderStatus = function (me, options, kid, order, verifieds) {
|
ACME._pollOrderStatus = function(me, options, kid, order, verifieds) {
|
||||||
var csr64 = ACME._csrToUrlBase64(options.csr);
|
var csr64 = ACME._csrToUrlBase64(options.csr);
|
||||||
var body = { csr: csr64 };
|
var body = { csr: csr64 };
|
||||||
var payload = JSON.stringify(body);
|
var payload = JSON.stringify(body);
|
||||||
|
|
||||||
function processResponse(resp) {
|
function pollCert() {
|
||||||
|
//#console.debug('[ACME.js] pollCert:', order._finalizeUrl);
|
||||||
|
return U._jwsRequest(me, {
|
||||||
|
accountKey: options.accountKey,
|
||||||
|
url: order._finalizeUrl,
|
||||||
|
protected: { kid: kid },
|
||||||
|
payload: Enc.strToBuf(payload)
|
||||||
|
}).then(function(resp) {
|
||||||
ACME._notify(me, options, 'certificate_status', {
|
ACME._notify(me, options, 'certificate_status', {
|
||||||
subject: options.domains[0],
|
subject: options.domains[0],
|
||||||
status: resp.body.status
|
status: resp.body.status
|
||||||
|
@ -1029,7 +1018,7 @@ ACME._pollOrderStatus = function (me, options, kid, order, verifieds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('processing' === resp.body.status) {
|
if ('processing' === resp.body.status) {
|
||||||
return ACME._wait().then(pollStatus);
|
return ACME._wait().then(pollCert);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me.debug) {
|
if (me.debug) {
|
||||||
|
@ -1069,31 +1058,13 @@ ACME._pollOrderStatus = function (me, options, kid, order, verifieds) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
E.UNHANDLED_ORDER_STATUS(options, verifieds, resp)
|
E.UNHANDLED_ORDER_STATUS(options, verifieds, resp)
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function pollStatus() {
|
return pollCert();
|
||||||
return U._jwsRequest(me, {
|
|
||||||
accountKey: options.accountKey,
|
|
||||||
url: order._orderUrl,
|
|
||||||
protected: { kid: kid },
|
|
||||||
payload: Enc.binToBuf('')
|
|
||||||
}).then(processResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
function finalizeOrder() {
|
|
||||||
//#console.debug('[ACME.js] pollCert:', order._finalizeUrl);
|
|
||||||
return U._jwsRequest(me, {
|
|
||||||
accountKey: options.accountKey,
|
|
||||||
url: order._finalizeUrl,
|
|
||||||
protected: { kid: kid },
|
|
||||||
payload: Enc.strToBuf(payload)
|
|
||||||
}).then(processResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalizeOrder();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._redeemCert = function (me, options, kid, voucher) {
|
ACME._redeemCert = function(me, options, kid, voucher) {
|
||||||
//#console.debug('ACME.js: order was finalized');
|
//#console.debug('ACME.js: order was finalized');
|
||||||
|
|
||||||
// POST-as-GET
|
// POST-as-GET
|
||||||
|
@ -1103,7 +1074,7 @@ ACME._redeemCert = function (me, options, kid, voucher) {
|
||||||
protected: { kid: kid },
|
protected: { kid: kid },
|
||||||
payload: Enc.binToBuf(''),
|
payload: Enc.binToBuf(''),
|
||||||
json: true
|
json: true
|
||||||
}).then(function (resp) {
|
}).then(function(resp) {
|
||||||
//#console.debug('ACME.js: csr submitted and cert received:');
|
//#console.debug('ACME.js: csr submitted and cert received:');
|
||||||
|
|
||||||
// https://github.com/certbot/certbot/issues/5721
|
// https://github.com/certbot/certbot/issues/5721
|
||||||
|
@ -1122,12 +1093,12 @@ ACME._redeemCert = function (me, options, kid, voucher) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._finalizeOrder = function (me, options, kid, order) {
|
ACME._finalizeOrder = function(me, options, kid, order) {
|
||||||
//#console.debug('[ACME.js] finalizeOrder:');
|
//#console.debug('[ACME.js] finalizeOrder:');
|
||||||
var readyToPresent;
|
var readyToPresent;
|
||||||
return A._getAccountKid(me, options).then(function (kid) {
|
return A._getAccountKid(me, options).then(function(kid) {
|
||||||
return ACME._setChallenges(me, options, order)
|
return ACME._setChallenges(me, options, order)
|
||||||
.then(function (_readyToPresent) {
|
.then(function(_readyToPresent) {
|
||||||
readyToPresent = _readyToPresent;
|
readyToPresent = _readyToPresent;
|
||||||
return ACME._presentChallenges(
|
return ACME._presentChallenges(
|
||||||
me,
|
me,
|
||||||
|
@ -1136,28 +1107,28 @@ ACME._finalizeOrder = function (me, options, kid, order) {
|
||||||
readyToPresent
|
readyToPresent
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function() {
|
||||||
return ACME._pollOrderStatus(
|
return ACME._pollOrderStatus(
|
||||||
me,
|
me,
|
||||||
options,
|
options,
|
||||||
kid,
|
kid,
|
||||||
order,
|
order,
|
||||||
readyToPresent.map(function (ch) {
|
readyToPresent.map(function(ch) {
|
||||||
return ACME._untame(ch.identifier.value, ch.wildcard);
|
return ACME._untame(ch.identifier.value, ch.wildcard);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.then(function (voucher) {
|
.then(function(voucher) {
|
||||||
return ACME._redeemCert(me, options, kid, voucher);
|
return ACME._redeemCert(me, options, kid, voucher);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Order a certificate request with all domains
|
// Order a certificate request with all domains
|
||||||
ACME._orderCert = function (me, options, kid) {
|
ACME._orderCert = function(me, options, kid) {
|
||||||
var certificateRequest = {
|
var certificateRequest = {
|
||||||
// raw wildcard syntax MUST be used here
|
// raw wildcard syntax MUST be used here
|
||||||
identifiers: options.domains.map(function (hostname) {
|
identifiers: options.domains.map(function(hostname) {
|
||||||
return { type: 'dns', value: hostname };
|
return { type: 'dns', value: hostname };
|
||||||
})
|
})
|
||||||
//, "notBefore": "2016-01-01T00:00:00Z"
|
//, "notBefore": "2016-01-01T00:00:00Z"
|
||||||
|
@ -1165,10 +1136,10 @@ ACME._orderCert = function (me, options, kid) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return ACME._prepRequest(me, options)
|
return ACME._prepRequest(me, options)
|
||||||
.then(function () {
|
.then(function() {
|
||||||
return ACME._getZones(me, options.challenges, options.domains);
|
return ACME._getZones(me, options.challenges, options.domains);
|
||||||
})
|
})
|
||||||
.then(function (zonenames) {
|
.then(function(zonenames) {
|
||||||
var p;
|
var p;
|
||||||
// Do a little dry-run / self-test
|
// Do a little dry-run / self-test
|
||||||
if (!me.skipDryRun && !options.skipDryRun) {
|
if (!me.skipDryRun && !options.skipDryRun) {
|
||||||
|
@ -1177,9 +1148,9 @@ ACME._orderCert = function (me, options, kid) {
|
||||||
p = Promise.resolve(null);
|
p = Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.then(function () {
|
return p.then(function() {
|
||||||
return A._getAccountKid(me, options)
|
return A._getAccountKid(me, options)
|
||||||
.then(function (kid) {
|
.then(function(kid) {
|
||||||
ACME._notify(me, options, 'certificate_order', {
|
ACME._notify(me, options, 'certificate_order', {
|
||||||
// API-locked
|
// API-locked
|
||||||
account: { key: { kid: kid } },
|
account: { key: { kid: kid } },
|
||||||
|
@ -1197,7 +1168,7 @@ ACME._orderCert = function (me, options, kid) {
|
||||||
payload: Enc.binToBuf(payload)
|
payload: Enc.binToBuf(payload)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function (resp) {
|
.then(function(resp) {
|
||||||
var order = resp.body;
|
var order = resp.body;
|
||||||
order._orderUrl = resp.headers.location;
|
order._orderUrl = resp.headers.location;
|
||||||
order._finalizeUrl = resp.body.finalize;
|
order._finalizeUrl = resp.body.finalize;
|
||||||
|
@ -1213,14 +1184,14 @@ ACME._orderCert = function (me, options, kid) {
|
||||||
|
|
||||||
return order;
|
return order;
|
||||||
})
|
})
|
||||||
.then(function (order) {
|
.then(function(order) {
|
||||||
return ACME._getAllChallenges(
|
return ACME._getAllChallenges(
|
||||||
me,
|
me,
|
||||||
options,
|
options,
|
||||||
kid,
|
kid,
|
||||||
zonenames,
|
zonenames,
|
||||||
order
|
order
|
||||||
).then(function (claims) {
|
).then(function(claims) {
|
||||||
order._claims = claims;
|
order._claims = claims;
|
||||||
return order;
|
return order;
|
||||||
});
|
});
|
||||||
|
@ -1229,8 +1200,8 @@ ACME._orderCert = function (me, options, kid) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._prepRequest = function (me, options) {
|
ACME._prepRequest = function(me, options) {
|
||||||
return Promise.resolve().then(function () {
|
return Promise.resolve().then(function() {
|
||||||
// TODO check that all presenterTypes are represented in challenges
|
// TODO check that all presenterTypes are represented in challenges
|
||||||
if (!options._presenterTypes.length) {
|
if (!options._presenterTypes.length) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
|
@ -1248,8 +1219,14 @@ ACME._prepRequest = function (me, options) {
|
||||||
options.domains = options.domains || _csr.altnames;
|
options.domains = options.domains || _csr.altnames;
|
||||||
_csr.altnames = _csr.altnames || [];
|
_csr.altnames = _csr.altnames || [];
|
||||||
if (
|
if (
|
||||||
options.domains.slice(0).sort().join(' ') !==
|
options.domains
|
||||||
_csr.altnames.slice(0).sort().join(' ')
|
.slice(0)
|
||||||
|
.sort()
|
||||||
|
.join(' ') !==
|
||||||
|
_csr.altnames
|
||||||
|
.slice(0)
|
||||||
|
.sort()
|
||||||
|
.join(' ')
|
||||||
) {
|
) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error('certificate altnames do not match requested domains')
|
new Error('certificate altnames do not match requested domains')
|
||||||
|
@ -1272,7 +1249,7 @@ ACME._prepRequest = function (me, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// a cheap check to see if there are non-ascii characters in any of the domains
|
// a cheap check to see if there are non-ascii characters in any of the domains
|
||||||
var nonAsciiDomains = options.domains.some(function (d) {
|
var nonAsciiDomains = options.domains.some(function(d) {
|
||||||
// IDN / unicode / utf-8 / punycode
|
// IDN / unicode / utf-8 / punycode
|
||||||
return Enc.strToBin(d) !== d;
|
return Enc.strToBin(d) !== d;
|
||||||
});
|
});
|
||||||
|
@ -1283,7 +1260,7 @@ ACME._prepRequest = function (me, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Promise.all()?
|
// TODO Promise.all()?
|
||||||
(options._presenterTypes || []).forEach(function (key) {
|
(options._presenterTypes || []).forEach(function(key) {
|
||||||
var presenter = options.challenges[key];
|
var presenter = options.challenges[key];
|
||||||
if (
|
if (
|
||||||
'function' === typeof presenter.init &&
|
'function' === typeof presenter.init &&
|
||||||
|
@ -1297,7 +1274,7 @@ ACME._prepRequest = function (me, options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Request a challenge for each authorization in the order
|
// Request a challenge for each authorization in the order
|
||||||
ACME._getAllChallenges = function (me, options, kid, zonenames, order) {
|
ACME._getAllChallenges = function(me, options, kid, zonenames, order) {
|
||||||
var claims = [];
|
var claims = [];
|
||||||
//#console.debug("[acme-v2] POST newOrder has authorizations");
|
//#console.debug("[acme-v2] POST newOrder has authorizations");
|
||||||
var challengeAuths = order.authorizations.slice(0);
|
var challengeAuths = order.authorizations.slice(0);
|
||||||
|
@ -1314,14 +1291,14 @@ ACME._getAllChallenges = function (me, options, kid, zonenames, order) {
|
||||||
kid,
|
kid,
|
||||||
zonenames,
|
zonenames,
|
||||||
authUrl
|
authUrl
|
||||||
).then(function (claim) {
|
).then(function(claim) {
|
||||||
// var domain = options.domains[i]; // claim.identifier.value
|
// var domain = options.domains[i]; // claim.identifier.value
|
||||||
claims.push(claim);
|
claims.push(claim);
|
||||||
return getNext();
|
return getNext();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return getNext().then(function () {
|
return getNext().then(function() {
|
||||||
return claims;
|
return claims;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1339,12 +1316,12 @@ ACME.splitPemChain = function splitPemChain(str) {
|
||||||
return str
|
return str
|
||||||
.trim()
|
.trim()
|
||||||
.split(/[\r\n]{2,}/g)
|
.split(/[\r\n]{2,}/g)
|
||||||
.map(function (str) {
|
.map(function(str) {
|
||||||
return str + '\n';
|
return str + '\n';
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._csrToUrlBase64 = function (csr) {
|
ACME._csrToUrlBase64 = function(csr) {
|
||||||
// if der, convert to base64
|
// if der, convert to base64
|
||||||
if ('string' !== typeof csr) {
|
if ('string' !== typeof csr) {
|
||||||
csr = Enc.bufToUrlBase64(csr);
|
csr = Enc.bufToUrlBase64(csr);
|
||||||
|
@ -1353,16 +1330,21 @@ ACME._csrToUrlBase64 = function (csr) {
|
||||||
// TODO use PEM.parseBlock()
|
// TODO use PEM.parseBlock()
|
||||||
// nix PEM headers, if any
|
// nix PEM headers, if any
|
||||||
if ('-' === csr[0]) {
|
if ('-' === csr[0]) {
|
||||||
csr = csr.split(/\n+/).slice(1, -1).join('');
|
csr = csr
|
||||||
|
.split(/\n+/)
|
||||||
|
.slice(1, -1)
|
||||||
|
.join('');
|
||||||
}
|
}
|
||||||
return Enc.base64ToUrlBase64(csr.trim().replace(/\s+/g, ''));
|
return Enc.base64ToUrlBase64(csr.trim().replace(/\s+/g, ''));
|
||||||
};
|
};
|
||||||
|
|
||||||
// In v8 this is crypto random, but we're just using it for pseudorandom
|
// In v8 this is crypto random, but we're just using it for pseudorandom
|
||||||
ACME._prnd = function (n) {
|
ACME._prnd = function(n) {
|
||||||
var rnd = '';
|
var rnd = '';
|
||||||
while (rnd.length / 2 < n) {
|
while (rnd.length / 2 < n) {
|
||||||
var i = Math.random().toString().substr(2);
|
var i = Math.random()
|
||||||
|
.toString()
|
||||||
|
.substr(2);
|
||||||
var h = parseInt(i, 10).toString(16);
|
var h = parseInt(i, 10).toString(16);
|
||||||
if (h.length % 2) {
|
if (h.length % 2) {
|
||||||
h = '0' + h;
|
h = '0' + h;
|
||||||
|
@ -1372,7 +1354,7 @@ ACME._prnd = function (n) {
|
||||||
return rnd.substr(0, n * 2);
|
return rnd.substr(0, n * 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._notify = function (me, options, ev, params) {
|
ACME._notify = function(me, options, ev, params) {
|
||||||
if (!options.notify && !me.notify) {
|
if (!options.notify && !me.notify) {
|
||||||
//console.info(ev, params);
|
//console.info(ev, params);
|
||||||
return;
|
return;
|
||||||
|
@ -1386,7 +1368,7 @@ ACME._notify = function (me, options, ev, params) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ACME._wait = function wait(ms) {
|
ACME._wait = function wait(ms) {
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function(resolve) {
|
||||||
setTimeout(resolve, ms || 1100);
|
setTimeout(resolve, ms || 1100);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1403,12 +1385,12 @@ function newZoneRegExp(zonename) {
|
||||||
|
|
||||||
function pluckZone(zonenames, dnsHost) {
|
function pluckZone(zonenames, dnsHost) {
|
||||||
return zonenames
|
return zonenames
|
||||||
.filter(function (zonename) {
|
.filter(function(zonename) {
|
||||||
// the only character that needs to be escaped for regex
|
// the only character that needs to be escaped for regex
|
||||||
// and is allowed in a domain name is '.'
|
// and is allowed in a domain name is '.'
|
||||||
return newZoneRegExp(zonename).test(dnsHost);
|
return newZoneRegExp(zonename).test(dnsHost);
|
||||||
})
|
})
|
||||||
.sort(function (a, b) {
|
.sort(function(a, b) {
|
||||||
// longest match first
|
// longest match first
|
||||||
return b.length - a.length;
|
return b.length - a.length;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
(async function () {
|
(async function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var UglifyJS = require('uglify-js');
|
var UglifyJS = require('uglify-js');
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
'../lib/asn1-parser.js',
|
'../lib/asn1-parser.js',
|
||||||
'../lib/csr.js',
|
'../lib/csr.js',
|
||||||
'../lib/acme.js'
|
'../lib/acme.js'
|
||||||
].map(async function (file) {
|
].map(async function(file) {
|
||||||
return (await readFile(path.join(__dirname, file), 'utf8')).trim();
|
return (await readFile(path.join(__dirname, file), 'utf8')).trim();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
12
errors.js
12
errors.js
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
var E = module.exports;
|
var E = module.exports;
|
||||||
|
|
||||||
E.NO_SUITABLE_CHALLENGE = function (domain, challenges, presenters) {
|
E.NO_SUITABLE_CHALLENGE = function(domain, challenges, presenters) {
|
||||||
// Bail with a descriptive message if no usable challenge could be selected
|
// Bail with a descriptive message if no usable challenge could be selected
|
||||||
// For example, wildcards require dns-01 and, if we don't have that, we have to bail
|
// For example, wildcards require dns-01 and, if we don't have that, we have to bail
|
||||||
var enabled = presenters.join(', ') || 'none';
|
var enabled = presenters.join(', ') || 'none';
|
||||||
var suitable =
|
var suitable =
|
||||||
challenges
|
challenges
|
||||||
.map(function (r) {
|
.map(function(r) {
|
||||||
return r.type;
|
return r.type;
|
||||||
})
|
})
|
||||||
.join(', ') || 'none';
|
.join(', ') || 'none';
|
||||||
|
@ -24,7 +24,7 @@ E.NO_SUITABLE_CHALLENGE = function (domain, challenges, presenters) {
|
||||||
' ).'
|
' ).'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
E.UNHANDLED_ORDER_STATUS = function (options, domains, resp) {
|
E.UNHANDLED_ORDER_STATUS = function(options, domains, resp) {
|
||||||
return new Error(
|
return new Error(
|
||||||
"Didn't finalize order: Unhandled status '" +
|
"Didn't finalize order: Unhandled status '" +
|
||||||
resp.body.status +
|
resp.body.status +
|
||||||
|
@ -41,7 +41,7 @@ E.UNHANDLED_ORDER_STATUS = function (options, domains, resp) {
|
||||||
'Please open an issue at https://git.rootprojects.org/root/acme.js'
|
'Please open an issue at https://git.rootprojects.org/root/acme.js'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
E.DOUBLE_READY_ORDER = function (options, domains, resp) {
|
E.DOUBLE_READY_ORDER = function(options, domains, resp) {
|
||||||
return new Error(
|
return new Error(
|
||||||
"Did not finalize order: status 'ready'." +
|
"Did not finalize order: status 'ready'." +
|
||||||
" Hmmm... this state shouldn't be possible here. That was the last state." +
|
" Hmmm... this state shouldn't be possible here. That was the last state." +
|
||||||
|
@ -57,7 +57,7 @@ E.DOUBLE_READY_ORDER = function (options, domains, resp) {
|
||||||
'Please open an issue at https://git.rootprojects.org/root/acme.js'
|
'Please open an issue at https://git.rootprojects.org/root/acme.js'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
E.ORDER_INVALID = function (options, domains, resp) {
|
E.ORDER_INVALID = function(options, domains, resp) {
|
||||||
return new Error(
|
return new Error(
|
||||||
"Did not finalize order: status 'invalid'." +
|
"Did not finalize order: status 'invalid'." +
|
||||||
' Best guess: One or more of the domain challenges could not be verified' +
|
' Best guess: One or more of the domain challenges could not be verified' +
|
||||||
|
@ -71,7 +71,7 @@ E.ORDER_INVALID = function (options, domains, resp) {
|
||||||
JSON.stringify(resp.body, null, 2)
|
JSON.stringify(resp.body, null, 2)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
E.NO_AUTHORIZATIONS = function (options, resp) {
|
E.NO_AUTHORIZATIONS = function(options, resp) {
|
||||||
return new Error(
|
return new Error(
|
||||||
"[acme-v2.js] authorizations were not fetched for '" +
|
"[acme-v2.js] authorizations were not fetched for '" +
|
||||||
options.domains.join() +
|
options.domains.join() +
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*global Promise*/
|
/*global Promise*/
|
||||||
(function () {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Keypairs = require('@root/keypairs');
|
var Keypairs = require('@root/keypairs');
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
console.log('hello');
|
console.log('hello');
|
||||||
|
|
||||||
// Show different options for ECDSA vs RSA
|
// Show different options for ECDSA vs RSA
|
||||||
$$('input[name="kty"]').forEach(function ($el) {
|
$$('input[name="kty"]').forEach(function($el) {
|
||||||
$el.addEventListener('change', function (ev) {
|
$el.addEventListener('change', function(ev) {
|
||||||
console.log(this);
|
console.log(this);
|
||||||
console.log(ev);
|
console.log(ev);
|
||||||
if ('RSA' === ev.target.value) {
|
if ('RSA' === ev.target.value) {
|
||||||
|
@ -44,20 +44,20 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate a key on submit
|
// Generate a key on submit
|
||||||
$('form.js-keygen').addEventListener('submit', function (ev) {
|
$('form.js-keygen').addEventListener('submit', function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
$('.js-loading').hidden = false;
|
$('.js-loading').hidden = false;
|
||||||
$('.js-jwk').hidden = true;
|
$('.js-jwk').hidden = true;
|
||||||
$('.js-toc-der-public').hidden = true;
|
$('.js-toc-der-public').hidden = true;
|
||||||
$('.js-toc-der-private').hidden = true;
|
$('.js-toc-der-private').hidden = true;
|
||||||
$$('.js-toc-pem').forEach(function ($el) {
|
$$('.js-toc-pem').forEach(function($el) {
|
||||||
$el.hidden = true;
|
$el.hidden = true;
|
||||||
});
|
});
|
||||||
$$('input').map(function ($el) {
|
$$('input').map(function($el) {
|
||||||
$el.disabled = true;
|
$el.disabled = true;
|
||||||
});
|
});
|
||||||
$$('button').map(function ($el) {
|
$$('button').map(function($el) {
|
||||||
$el.disabled = true;
|
$el.disabled = true;
|
||||||
});
|
});
|
||||||
var opts = {
|
var opts = {
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
};
|
};
|
||||||
var then = Date.now();
|
var then = Date.now();
|
||||||
console.log('opts', opts);
|
console.log('opts', opts);
|
||||||
Keypairs.generate(opts).then(function (results) {
|
Keypairs.generate(opts).then(function(results) {
|
||||||
console.log('Key generation time:', Date.now() - then + 'ms');
|
console.log('Key generation time:', Date.now() - then + 'ms');
|
||||||
var pubDer;
|
var pubDer;
|
||||||
var privDer;
|
var privDer;
|
||||||
|
@ -77,19 +77,19 @@
|
||||||
Eckles.export({
|
Eckles.export({
|
||||||
jwk: results.private,
|
jwk: results.private,
|
||||||
format: 'sec1'
|
format: 'sec1'
|
||||||
}).then(function (pem) {
|
}).then(function(pem) {
|
||||||
$('.js-input-pem-sec1-private').innerText = pem;
|
$('.js-input-pem-sec1-private').innerText = pem;
|
||||||
$('.js-toc-pem-sec1-private').hidden = false;
|
$('.js-toc-pem-sec1-private').hidden = false;
|
||||||
});
|
});
|
||||||
Eckles.export({
|
Eckles.export({
|
||||||
jwk: results.private,
|
jwk: results.private,
|
||||||
format: 'pkcs8'
|
format: 'pkcs8'
|
||||||
}).then(function (pem) {
|
}).then(function(pem) {
|
||||||
$('.js-input-pem-pkcs8-private').innerText = pem;
|
$('.js-input-pem-pkcs8-private').innerText = pem;
|
||||||
$('.js-toc-pem-pkcs8-private').hidden = false;
|
$('.js-toc-pem-pkcs8-private').hidden = false;
|
||||||
});
|
});
|
||||||
Eckles.export({ jwk: results.public, public: true }).then(
|
Eckles.export({ jwk: results.public, public: true }).then(
|
||||||
function (pem) {
|
function(pem) {
|
||||||
$('.js-input-pem-spki-public').innerText = pem;
|
$('.js-input-pem-spki-public').innerText = pem;
|
||||||
$('.js-toc-pem-spki-public').hidden = false;
|
$('.js-toc-pem-spki-public').hidden = false;
|
||||||
}
|
}
|
||||||
|
@ -100,25 +100,25 @@
|
||||||
Rasha.export({
|
Rasha.export({
|
||||||
jwk: results.private,
|
jwk: results.private,
|
||||||
format: 'pkcs1'
|
format: 'pkcs1'
|
||||||
}).then(function (pem) {
|
}).then(function(pem) {
|
||||||
$('.js-input-pem-pkcs1-private').innerText = pem;
|
$('.js-input-pem-pkcs1-private').innerText = pem;
|
||||||
$('.js-toc-pem-pkcs1-private').hidden = false;
|
$('.js-toc-pem-pkcs1-private').hidden = false;
|
||||||
});
|
});
|
||||||
Rasha.export({
|
Rasha.export({
|
||||||
jwk: results.private,
|
jwk: results.private,
|
||||||
format: 'pkcs8'
|
format: 'pkcs8'
|
||||||
}).then(function (pem) {
|
}).then(function(pem) {
|
||||||
$('.js-input-pem-pkcs8-private').innerText = pem;
|
$('.js-input-pem-pkcs8-private').innerText = pem;
|
||||||
$('.js-toc-pem-pkcs8-private').hidden = false;
|
$('.js-toc-pem-pkcs8-private').hidden = false;
|
||||||
});
|
});
|
||||||
Rasha.export({ jwk: results.public, format: 'pkcs1' }).then(
|
Rasha.export({ jwk: results.public, format: 'pkcs1' }).then(
|
||||||
function (pem) {
|
function(pem) {
|
||||||
$('.js-input-pem-pkcs1-public').innerText = pem;
|
$('.js-input-pem-pkcs1-public').innerText = pem;
|
||||||
$('.js-toc-pem-pkcs1-public').hidden = false;
|
$('.js-toc-pem-pkcs1-public').hidden = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Rasha.export({ jwk: results.public, format: 'spki' }).then(
|
Rasha.export({ jwk: results.public, format: 'spki' }).then(
|
||||||
function (pem) {
|
function(pem) {
|
||||||
$('.js-input-pem-spki-public').innerText = pem;
|
$('.js-input-pem-spki-public').innerText = pem;
|
||||||
$('.js-toc-pem-spki-public').hidden = false;
|
$('.js-toc-pem-spki-public').hidden = false;
|
||||||
}
|
}
|
||||||
|
@ -132,10 +132,10 @@
|
||||||
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
|
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
|
||||||
$('.js-loading').hidden = true;
|
$('.js-loading').hidden = true;
|
||||||
$('.js-jwk').hidden = false;
|
$('.js-jwk').hidden = false;
|
||||||
$$('input').map(function ($el) {
|
$$('input').map(function($el) {
|
||||||
$el.disabled = false;
|
$el.disabled = false;
|
||||||
});
|
});
|
||||||
$$('button').map(function ($el) {
|
$$('button').map(function($el) {
|
||||||
$el.disabled = false;
|
$el.disabled = false;
|
||||||
});
|
});
|
||||||
$('.js-toc-jwk').hidden = false;
|
$('.js-toc-jwk').hidden = false;
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('form.js-acme-account').addEventListener('submit', function (ev) {
|
$('form.js-acme-account').addEventListener('submit', function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
$('.js-loading').hidden = false;
|
$('.js-loading').hidden = false;
|
||||||
|
@ -155,7 +155,7 @@
|
||||||
});
|
});
|
||||||
acme.init(
|
acme.init(
|
||||||
'https://acme-staging-v02.api.letsencrypt.org/directory'
|
'https://acme-staging-v02.api.letsencrypt.org/directory'
|
||||||
).then(function (result) {
|
).then(function(result) {
|
||||||
console.log('acme result', result);
|
console.log('acme result', result);
|
||||||
var privJwk = JSON.parse($('.js-jwk').innerText).private;
|
var privJwk = JSON.parse($('.js-jwk').innerText).private;
|
||||||
var email = $('.js-email').value;
|
var email = $('.js-email').value;
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
agreeToTerms: checkTos,
|
agreeToTerms: checkTos,
|
||||||
accountKeypair: { privateKeyJwk: privJwk }
|
accountKeypair: { privateKeyJwk: privJwk }
|
||||||
})
|
})
|
||||||
.then(function (account) {
|
.then(function(account) {
|
||||||
console.log('account created result:', account);
|
console.log('account created result:', account);
|
||||||
accountStuff.account = account;
|
accountStuff.account = account;
|
||||||
accountStuff.privateJwk = privJwk;
|
accountStuff.privateJwk = privJwk;
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
'.js-acme-account-response'
|
'.js-acme-account-response'
|
||||||
).innerText = JSON.stringify(account, null, 2);
|
).innerText = JSON.stringify(account, null, 2);
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
console.error('A bad thing happened:');
|
console.error('A bad thing happened:');
|
||||||
console.error(err);
|
console.error(err);
|
||||||
window.alert(
|
window.alert(
|
||||||
|
@ -187,13 +187,13 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('form.js-csr').addEventListener('submit', function (ev) {
|
$('form.js-csr').addEventListener('submit', function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
generateCsr();
|
generateCsr();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('form.js-acme-order').addEventListener('submit', function (ev) {
|
$('form.js-acme-order').addEventListener('submit', function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
var account = accountStuff.account;
|
var account = accountStuff.account;
|
||||||
|
@ -204,7 +204,7 @@
|
||||||
var domains = ($('.js-domains').value || 'example.com').split(
|
var domains = ($('.js-domains').value || 'example.com').split(
|
||||||
/[, ]+/g
|
/[, ]+/g
|
||||||
);
|
);
|
||||||
return getDomainPrivkey().then(function (domainPrivJwk) {
|
return getDomainPrivkey().then(function(domainPrivJwk) {
|
||||||
console.log('Has CSR already?');
|
console.log('Has CSR already?');
|
||||||
console.log(accountStuff.csr);
|
console.log(accountStuff.csr);
|
||||||
return acme.certificates
|
return acme.certificates
|
||||||
|
@ -219,11 +219,11 @@
|
||||||
agreeToTerms: checkTos,
|
agreeToTerms: checkTos,
|
||||||
challenges: {
|
challenges: {
|
||||||
'dns-01': {
|
'dns-01': {
|
||||||
set: function (opts) {
|
set: function(opts) {
|
||||||
console.info('dns-01 set challenge:');
|
console.info('dns-01 set challenge:');
|
||||||
console.info('TXT', opts.dnsHost);
|
console.info('TXT', opts.dnsHost);
|
||||||
console.info(opts.dnsAuthorization);
|
console.info(opts.dnsAuthorization);
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function(resolve) {
|
||||||
while (
|
while (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
'Did you set the challenge?'
|
'Did you set the challenge?'
|
||||||
|
@ -232,11 +232,11 @@
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
remove: function (opts) {
|
remove: function(opts) {
|
||||||
console.log('dns-01 remove challenge:');
|
console.log('dns-01 remove challenge:');
|
||||||
console.info('TXT', opts.dnsHost);
|
console.info('TXT', opts.dnsHost);
|
||||||
console.info(opts.dnsAuthorization);
|
console.info(opts.dnsAuthorization);
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function(resolve) {
|
||||||
while (
|
while (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
'Did you delete the challenge?'
|
'Did you delete the challenge?'
|
||||||
|
@ -247,11 +247,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'http-01': {
|
'http-01': {
|
||||||
set: function (opts) {
|
set: function(opts) {
|
||||||
console.info('http-01 set challenge:');
|
console.info('http-01 set challenge:');
|
||||||
console.info(opts.challengeUrl);
|
console.info(opts.challengeUrl);
|
||||||
console.info(opts.keyAuthorization);
|
console.info(opts.keyAuthorization);
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function(resolve) {
|
||||||
while (
|
while (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
'Did you set the challenge?'
|
'Did you set the challenge?'
|
||||||
|
@ -260,11 +260,11 @@
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
remove: function (opts) {
|
remove: function(opts) {
|
||||||
console.log('http-01 remove challenge:');
|
console.log('http-01 remove challenge:');
|
||||||
console.info(opts.challengeUrl);
|
console.info(opts.challengeUrl);
|
||||||
console.info(opts.keyAuthorization);
|
console.info(opts.keyAuthorization);
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function(resolve) {
|
||||||
while (
|
while (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
'Did you delete the challenge?'
|
'Did you delete the challenge?'
|
||||||
|
@ -279,7 +279,7 @@
|
||||||
$('input[name="acme-challenge-type"]:checked').value
|
$('input[name="acme-challenge-type"]:checked').value
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.then(function (results) {
|
.then(function(results) {
|
||||||
console.log('Got Certificates:');
|
console.log('Got Certificates:');
|
||||||
console.log(results);
|
console.log(results);
|
||||||
$('.js-toc-acme-order-response').hidden = false;
|
$('.js-toc-acme-order-response').hidden = false;
|
||||||
|
@ -289,7 +289,7 @@
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
console.error('challenge failed:');
|
console.error('challenge failed:');
|
||||||
console.error(err);
|
console.error(err);
|
||||||
window.alert(
|
window.alert(
|
||||||
|
@ -310,7 +310,7 @@
|
||||||
kty: $('input[name="kty"]:checked').value,
|
kty: $('input[name="kty"]:checked').value,
|
||||||
namedCurve: $('input[name="ec-crv"]:checked').value,
|
namedCurve: $('input[name="ec-crv"]:checked').value,
|
||||||
modulusLength: $('input[name="rsa-len"]:checked').value
|
modulusLength: $('input[name="rsa-len"]:checked').value
|
||||||
}).then(function (pair) {
|
}).then(function(pair) {
|
||||||
console.log('domain keypair:', pair);
|
console.log('domain keypair:', pair);
|
||||||
accountStuff.domainPrivateJwk = pair.private;
|
accountStuff.domainPrivateJwk = pair.private;
|
||||||
return pair.private;
|
return pair.private;
|
||||||
|
@ -320,9 +320,9 @@
|
||||||
function generateCsr() {
|
function generateCsr() {
|
||||||
var domains = ($('.js-domains').value || 'example.com').split(/[, ]+/g);
|
var domains = ($('.js-domains').value || 'example.com').split(/[, ]+/g);
|
||||||
//var privJwk = JSON.parse($('.js-jwk').innerText).private;
|
//var privJwk = JSON.parse($('.js-jwk').innerText).private;
|
||||||
return getDomainPrivkey().then(function (privJwk) {
|
return getDomainPrivkey().then(function(privJwk) {
|
||||||
accountStuff.domainPrivateJwk = privJwk;
|
accountStuff.domainPrivateJwk = privJwk;
|
||||||
return CSR({ jwk: privJwk, domains: domains }).then(function (pem) {
|
return CSR({ jwk: privJwk, domains: domains }).then(function(pem) {
|
||||||
// Verify with https://www.sslshopper.com/csr-decoder.html
|
// Verify with https://www.sslshopper.com/csr-decoder.html
|
||||||
accountStuff.csr = pem;
|
accountStuff.csr = pem;
|
||||||
console.log('Created CSR:');
|
console.log('Created CSR:');
|
||||||
|
|
|
@ -5,7 +5,7 @@ async function main() {
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
// just to trigger the warning message out of the way
|
// just to trigger the warning message out of the way
|
||||||
await fs.promises.readFile().catch(function () {});
|
await fs.promises.readFile().catch(function() {});
|
||||||
console.warn('\n');
|
console.warn('\n');
|
||||||
var MY_DOMAINS = process.env.DOMAINS.split(/[,\s]+/);
|
var MY_DOMAINS = process.env.DOMAINS.split(/[,\s]+/);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ async function main() {
|
||||||
|
|
||||||
// If you are multi-tenanted or white-labled and need to present the terms of
|
// If you are multi-tenanted or white-labled and need to present the terms of
|
||||||
// use to the Subscriber running the service, you can do so with a function.
|
// use to the Subscriber running the service, you can do so with a function.
|
||||||
var agreeToTerms = async function () {
|
var agreeToTerms = async function() {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ async function main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(function (e) {
|
main().catch(function(e) {
|
||||||
console.error(e.stack);
|
console.error(e.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ var key = fs.readFileSync('./privkey.pem');
|
||||||
var cert = fs.readFileSync('./fullchain.pem');
|
var cert = fs.readFileSync('./fullchain.pem');
|
||||||
|
|
||||||
var server = https
|
var server = https
|
||||||
.createSecureServer({ key, cert }, function (req, res) {
|
.createSecureServer({ key, cert }, function(req, res) {
|
||||||
res.end('Hello, Encrypted World!');
|
res.end('Hello, Encrypted World!');
|
||||||
})
|
})
|
||||||
.listen(443, function () {
|
.listen(443, function() {
|
||||||
console.info('Listening on', server.address());
|
console.info('Listening on', server.address());
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var https = require('http2');
|
|
||||||
var tls = require('tls');
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
var key = fs.readFileSync('./privkey.pem');
|
|
||||||
var cert = fs.readFileSync('./fullchain.pem');
|
|
||||||
|
|
||||||
function SNICallback(servername, cb) {
|
|
||||||
console.log('sni:', servername);
|
|
||||||
cb(null, tls.createSecureContext({ key, cert }));
|
|
||||||
}
|
|
||||||
|
|
||||||
var server = https
|
|
||||||
.createSecureServer({ SNICallback: SNICallback }, function (req, res) {
|
|
||||||
res.end('Hello, Encrypted World!');
|
|
||||||
})
|
|
||||||
.listen(443, function () {
|
|
||||||
console.info('Listening on', server.address());
|
|
||||||
});
|
|
|
@ -13,12 +13,12 @@ var nameserver = nameservers[index];
|
||||||
|
|
||||||
app.use('/', express.static(__dirname));
|
app.use('/', express.static(__dirname));
|
||||||
app.use('/api', express.json());
|
app.use('/api', express.json());
|
||||||
app.get('/api/dns/:domain', function (req, res, next) {
|
app.get('/api/dns/:domain', function(req, res, next) {
|
||||||
var domain = req.params.domain;
|
var domain = req.params.domain;
|
||||||
var casedDomain = domain
|
var casedDomain = domain
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split('')
|
.split('')
|
||||||
.map(function (ch) {
|
.map(function(ch) {
|
||||||
// dns0x20 takes advantage of the fact that the binary operation for toUpperCase is
|
// dns0x20 takes advantage of the fact that the binary operation for toUpperCase is
|
||||||
// ch = ch | 0x20;
|
// ch = ch | 0x20;
|
||||||
return Math.round(Math.random()) % 2 ? ch : ch.toUpperCase();
|
return Math.round(Math.random()) % 2 ? ch : ch.toUpperCase();
|
||||||
|
@ -46,10 +46,10 @@ app.get('/api/dns/:domain', function (req, res, next) {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
var opts = {
|
var opts = {
|
||||||
onError: function (err) {
|
onError: function(err) {
|
||||||
next(err);
|
next(err);
|
||||||
},
|
},
|
||||||
onMessage: function (packet) {
|
onMessage: function(packet) {
|
||||||
var fail0x20;
|
var fail0x20;
|
||||||
|
|
||||||
if (packet.id !== query.id) {
|
if (packet.id !== query.id) {
|
||||||
|
@ -62,17 +62,17 @@ app.get('/api/dns/:domain', function (req, res, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.question.forEach(function (q) {
|
packet.question.forEach(function(q) {
|
||||||
// if (-1 === q.name.lastIndexOf(cli.casedQuery))
|
// if (-1 === q.name.lastIndexOf(cli.casedQuery))
|
||||||
if (q.name !== casedDomain) {
|
if (q.name !== casedDomain) {
|
||||||
fail0x20 = q.name;
|
fail0x20 = q.name;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
['question', 'answer', 'authority', 'additional'].forEach(function (
|
['question', 'answer', 'authority', 'additional'].forEach(function(
|
||||||
group
|
group
|
||||||
) {
|
) {
|
||||||
(packet[group] || []).forEach(function (a) {
|
(packet[group] || []).forEach(function(a) {
|
||||||
var an = a.name;
|
var an = a.name;
|
||||||
var i = domain
|
var i = domain
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
|
@ -133,13 +133,13 @@ app.get('/api/dns/:domain', function (req, res, next) {
|
||||||
edns_options: packet.edns_options
|
edns_options: packet.edns_options
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onListening: function () {},
|
onListening: function() {},
|
||||||
onSent: function (/*res*/) {},
|
onSent: function(/*res*/) {},
|
||||||
onTimeout: function (res) {
|
onTimeout: function(res) {
|
||||||
console.error('dns timeout:', res);
|
console.error('dns timeout:', res);
|
||||||
next(new Error('DNS timeout - no response'));
|
next(new Error('DNS timeout - no response'));
|
||||||
},
|
},
|
||||||
onClose: function () {},
|
onClose: function() {},
|
||||||
//, mdns: cli.mdns
|
//, mdns: cli.mdns
|
||||||
nameserver: nameserver,
|
nameserver: nameserver,
|
||||||
port: 53,
|
port: 53,
|
||||||
|
@ -148,13 +148,13 @@ app.get('/api/dns/:domain', function (req, res, next) {
|
||||||
|
|
||||||
dig.resolveJson(query, opts);
|
dig.resolveJson(query, opts);
|
||||||
});
|
});
|
||||||
app.get('/api/http', function (req, res) {
|
app.get('/api/http', function(req, res) {
|
||||||
var url = req.query.url;
|
var url = req.query.url;
|
||||||
return request({ method: 'GET', url: url }).then(function (resp) {
|
return request({ method: 'GET', url: url }).then(function(resp) {
|
||||||
res.send(resp.body);
|
res.send(resp.body);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.get('/api/_acme_api_', function (req, res) {
|
app.get('/api/_acme_api_', function(req, res) {
|
||||||
res.send({ success: true });
|
res.send({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,27 @@
|
||||||
|
|
||||||
var native = module.exports;
|
var native = module.exports;
|
||||||
|
|
||||||
native._canCheck = function (me) {
|
native._canCheck = function(me) {
|
||||||
me._canCheck = {};
|
me._canCheck = {};
|
||||||
return me
|
return me
|
||||||
.request({ url: me._baseUrl + '/api/_acme_api_/' })
|
.request({ url: me._baseUrl + '/api/_acme_api_/' })
|
||||||
.then(function (resp) {
|
.then(function(resp) {
|
||||||
if (resp.body.success) {
|
if (resp.body.success) {
|
||||||
me._canCheck['http-01'] = true;
|
me._canCheck['http-01'] = true;
|
||||||
me._canCheck['dns-01'] = true;
|
me._canCheck['dns-01'] = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function() {
|
||||||
// ignore
|
// ignore
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
native._dns01 = function (me, ch) {
|
native._dns01 = function(me, ch) {
|
||||||
return me
|
return me
|
||||||
.request({
|
.request({
|
||||||
url: me._baseUrl + '/api/dns/' + ch.dnsHost + '?type=TXT'
|
url: me._baseUrl + '/api/dns/' + ch.dnsHost + '?type=TXT'
|
||||||
})
|
})
|
||||||
.then(function (resp) {
|
.then(function(resp) {
|
||||||
var err;
|
var err;
|
||||||
if (!resp.body || !Array.isArray(resp.body.answer)) {
|
if (!resp.body || !Array.isArray(resp.body.answer)) {
|
||||||
err = new Error('failed to get DNS response');
|
err = new Error('failed to get DNS response');
|
||||||
|
@ -35,20 +35,20 @@ native._dns01 = function (me, ch) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
answer: resp.body.answer.map(function (ans) {
|
answer: resp.body.answer.map(function(ans) {
|
||||||
return { data: ans.data, ttl: ans.ttl };
|
return { data: ans.data, ttl: ans.ttl };
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
native._http01 = function (me, ch) {
|
native._http01 = function(me, ch) {
|
||||||
var url = encodeURIComponent(ch.challengeUrl);
|
var url = encodeURIComponent(ch.challengeUrl);
|
||||||
return me
|
return me
|
||||||
.request({
|
.request({
|
||||||
url: me._baseUrl + '/api/http?url=' + url
|
url: me._baseUrl + '/api/http?url=' + url
|
||||||
})
|
})
|
||||||
.then(function (resp) {
|
.then(function(resp) {
|
||||||
return resp.body;
|
return resp.body;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var UserAgent = module.exports;
|
var UserAgent = module.exports;
|
||||||
UserAgent.get = function () {
|
UserAgent.get = function() {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,30 +2,30 @@
|
||||||
|
|
||||||
var http = module.exports;
|
var http = module.exports;
|
||||||
|
|
||||||
http.request = function (opts) {
|
http.request = function(opts) {
|
||||||
opts.cors = true;
|
opts.cors = true;
|
||||||
return window.fetch(opts.url, opts).then(function (resp) {
|
return window.fetch(opts.url, opts).then(function(resp) {
|
||||||
var headers = {};
|
var headers = {};
|
||||||
var result = {
|
var result = {
|
||||||
statusCode: resp.status,
|
statusCode: resp.status,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
toJSON: function () {
|
toJSON: function() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Array.from(resp.headers.entries()).forEach(function (h) {
|
Array.from(resp.headers.entries()).forEach(function(h) {
|
||||||
headers[h[0]] = h[1];
|
headers[h[0]] = h[1];
|
||||||
});
|
});
|
||||||
if (!headers['content-type']) {
|
if (!headers['content-type']) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (/json/.test(headers['content-type'])) {
|
if (/json/.test(headers['content-type'])) {
|
||||||
return resp.json().then(function (json) {
|
return resp.json().then(function(json) {
|
||||||
result.body = json;
|
result.body = json;
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return resp.text().then(function (txt) {
|
return resp.text().then(function(txt) {
|
||||||
result.body = txt;
|
result.body = txt;
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
var sha2 = module.exports;
|
var sha2 = module.exports;
|
||||||
|
|
||||||
var encoder = new TextEncoder();
|
var encoder = new TextEncoder();
|
||||||
sha2.sum = function (alg, str) {
|
sha2.sum = function(alg, str) {
|
||||||
var data = str;
|
var data = str;
|
||||||
if ('string' === typeof data) {
|
if ('string' === typeof data) {
|
||||||
data = encoder.encode(str);
|
data = encoder.encode(str);
|
||||||
|
|
|
@ -5,18 +5,18 @@ var promisify = require('util').promisify;
|
||||||
var resolveTxt = promisify(require('dns').resolveTxt);
|
var resolveTxt = promisify(require('dns').resolveTxt);
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
|
||||||
native._canCheck = function (me) {
|
native._canCheck = function(me) {
|
||||||
me._canCheck = {};
|
me._canCheck = {};
|
||||||
me._canCheck['http-01'] = true;
|
me._canCheck['http-01'] = true;
|
||||||
me._canCheck['dns-01'] = true;
|
me._canCheck['dns-01'] = true;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
native._dns01 = function (me, ch) {
|
native._dns01 = function(me, ch) {
|
||||||
// TODO use digd.js
|
// TODO use digd.js
|
||||||
return resolveTxt(ch.dnsHost).then(function (records) {
|
return resolveTxt(ch.dnsHost).then(function(records) {
|
||||||
return {
|
return {
|
||||||
answer: records.map(function (rr) {
|
answer: records.map(function(rr) {
|
||||||
return {
|
return {
|
||||||
data: rr
|
data: rr
|
||||||
};
|
};
|
||||||
|
@ -25,10 +25,10 @@ native._dns01 = function (me, ch) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
native._http01 = function (me, ch) {
|
native._http01 = function(me, ch) {
|
||||||
return new me.request({
|
return new me.request({
|
||||||
url: ch.challengeUrl
|
url: ch.challengeUrl
|
||||||
}).then(function (resp) {
|
}).then(function(resp) {
|
||||||
return resp.body;
|
return resp.body;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -36,12 +36,12 @@ native._http01 = function (me, ch) {
|
||||||
// the hashcash here is for browser parity only
|
// the hashcash here is for browser parity only
|
||||||
// basically we ask the client to find a needle in a haystack
|
// basically we ask the client to find a needle in a haystack
|
||||||
// (very similar to CloudFlare's api protection)
|
// (very similar to CloudFlare's api protection)
|
||||||
native._hashcash = function (ch) {
|
native._hashcash = function(ch) {
|
||||||
if (!ch || !ch.nonce) {
|
if (!ch || !ch.nonce) {
|
||||||
ch = { nonce: 'xxx' };
|
ch = { nonce: 'xxx' };
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(function () {
|
.then(function() {
|
||||||
// only get easy answers
|
// only get easy answers
|
||||||
var len = ch.needle.length;
|
var len = ch.needle.length;
|
||||||
var start = ch.start || 0;
|
var start = ch.start || 0;
|
||||||
|
@ -80,7 +80,7 @@ native._hashcash = function (ch) {
|
||||||
}
|
}
|
||||||
return ch.nonce + ':xxx';
|
return ch.nonce + ':xxx';
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function() {
|
||||||
//console.log('[debug]', err);
|
//console.log('[debug]', err);
|
||||||
// ignore any error
|
// ignore any error
|
||||||
return ch.nonce + ':xxx';
|
return ch.nonce + ':xxx';
|
||||||
|
|
|
@ -4,7 +4,7 @@ var os = require('os');
|
||||||
var ver = require('../../package.json').version;
|
var ver = require('../../package.json').version;
|
||||||
|
|
||||||
var UserAgent = module.exports;
|
var UserAgent = module.exports;
|
||||||
UserAgent.get = function (me) {
|
UserAgent.get = function(me) {
|
||||||
// ACME clients MUST have an RFC7231-compliant User-Agent
|
// ACME clients MUST have an RFC7231-compliant User-Agent
|
||||||
// ex: Greenlock/v3 ACME.js/v3 node/v12.0.0 darwin/17.7.0 Darwin/x64
|
// ex: Greenlock/v3 ACME.js/v3 node/v12.0.0 darwin/17.7.0 Darwin/x64
|
||||||
//
|
//
|
||||||
|
|
|
@ -4,6 +4,6 @@ var http = module.exports;
|
||||||
var promisify = require('util').promisify;
|
var promisify = require('util').promisify;
|
||||||
var request = promisify(require('@root/request'));
|
var request = promisify(require('@root/request'));
|
||||||
|
|
||||||
http.request = function (opts) {
|
http.request = function(opts) {
|
||||||
return request(opts);
|
return request(opts);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,11 +4,14 @@
|
||||||
var sha2 = module.exports;
|
var sha2 = module.exports;
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
|
||||||
sha2.sum = function (alg, str) {
|
sha2.sum = function(alg, str) {
|
||||||
return Promise.resolve().then(function () {
|
return Promise.resolve().then(function() {
|
||||||
var sha = 'sha' + String(alg).replace(/^sha-?/i, '');
|
var sha = 'sha' + String(alg).replace(/^sha-?/i, '');
|
||||||
// utf8 is the default for strings
|
// utf8 is the default for strings
|
||||||
var buf = Buffer.from(str);
|
var buf = Buffer.from(str);
|
||||||
return crypto.createHash(sha).update(buf).digest();
|
return crypto
|
||||||
|
.createHash(sha)
|
||||||
|
.update(buf)
|
||||||
|
.digest();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ var native = require('./lib/native.js');
|
||||||
// something breaks or has a serious bug or flaw.
|
// something breaks or has a serious bug or flaw.
|
||||||
|
|
||||||
var oldCollegeTries = {};
|
var oldCollegeTries = {};
|
||||||
M.init = function (me) {
|
M.init = function(me) {
|
||||||
if (oldCollegeTries[me.maintainerEmail]) {
|
if (oldCollegeTries[me.maintainerEmail]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -32,23 +32,21 @@ M.init = function (me) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
M._init = function (me, tz, locale) {
|
M._init = function(me, tz, locale) {
|
||||||
setTimeout(function () {
|
|
||||||
// prevent a stampede from misconfigured clients in an eternal loop
|
// prevent a stampede from misconfigured clients in an eternal loop
|
||||||
|
setTimeout(function() {
|
||||||
me.request({
|
me.request({
|
||||||
timeout: 3000,
|
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: 'https://api.rootprojects.org/api/nonce',
|
url: 'https://api.rootprojects.org/api/nonce',
|
||||||
json: true
|
json: true
|
||||||
})
|
})
|
||||||
.then(function (resp) {
|
.then(function(resp) {
|
||||||
// in the browser this will work until solved, but in
|
// in the browser this will work until solved, but in
|
||||||
// node this will bail unless the challenge is trivial
|
// node this will bail unless the challenge is trivial
|
||||||
return native._hashcash(resp.body || {});
|
return native._hashcash(resp.body || {});
|
||||||
})
|
})
|
||||||
.then(function (hashcash) {
|
.then(function(hashcash) {
|
||||||
var req = {
|
var req = {
|
||||||
timeout: 3000,
|
|
||||||
headers: {
|
headers: {
|
||||||
'x-root-nonce-v1': hashcash
|
'x-root-nonce-v1': hashcash
|
||||||
},
|
},
|
||||||
|
@ -62,20 +60,18 @@ M._init = function (me, tz, locale) {
|
||||||
locale: locale
|
locale: locale
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return me.request(req);
|
return me
|
||||||
})
|
.request(req)
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
if (me.debug) {
|
if (true || me.debug) {
|
||||||
console.error(
|
|
||||||
'error adding maintainer to support notices:'
|
|
||||||
);
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function (/*resp*/) {
|
.then(function(/*resp*/) {
|
||||||
oldCollegeTries[me.maintainerEmail] = true;
|
oldCollegeTries[me.maintainerEmail] = true;
|
||||||
//console.log(resp);
|
//console.log(resp);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}, me.__timeout || 3000);
|
}, me.__timeout || 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@root/acme",
|
"name": "@root/acme",
|
||||||
"version": "3.1.0",
|
"version": "3.0.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz",
|
||||||
"integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==",
|
"integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@root/asn1": "^1.0.0",
|
"@root/asn1": "^1.0.0",
|
||||||
"@root/pem": "^1.0.4",
|
"@root/pem": "^1.0.4",
|
||||||
|
@ -28,9 +29,9 @@
|
||||||
"integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ=="
|
"integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ=="
|
||||||
},
|
},
|
||||||
"@root/keypairs": {
|
"@root/keypairs": {
|
||||||
"version": "0.10.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.9.0.tgz",
|
||||||
"integrity": "sha512-t8VocY46Mtb0NTsxzyLLf5tsgfw0BXLYVADAyiRdEdqHcvPFGJdjkXNtHVQuSV/FMaC65iTOHVP4E6X8iT3Ikg==",
|
"integrity": "sha512-NXE2L9Gv7r3iC4kB/gTPZE1vO9Ox/p14zDzAJ5cGpTpytbWOlWF7QoHSJbtVX4H7mRG/Hp7HR3jWdWdb2xaaXg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@root/encoding": "^1.0.1",
|
"@root/encoding": "^1.0.1",
|
||||||
"@root/pem": "^1.0.4",
|
"@root/pem": "^1.0.4",
|
||||||
|
@ -43,9 +44,9 @@
|
||||||
"integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA=="
|
"integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA=="
|
||||||
},
|
},
|
||||||
"@root/request": {
|
"@root/request": {
|
||||||
"version": "1.6.1",
|
"version": "1.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz",
|
||||||
"integrity": "sha512-8wrWyeBLRp7T8J36GkT3RODJ6zYmL0/maWlAUD5LOXT28D3TDquUepyYDKYANNA3Gc8R5ZCgf+AXvSTYpJEWwQ=="
|
"integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw=="
|
||||||
},
|
},
|
||||||
"@root/x509": {
|
"@root/x509": {
|
||||||
"version": "0.7.2",
|
"version": "0.7.2",
|
||||||
|
@ -152,9 +153,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.6",
|
"version": "7.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@root/acme",
|
"name": "@root/acme",
|
||||||
"version": "3.1.0",
|
"version": "3.0.5",
|
||||||
"description": "Free SSL certificates for Node.js and Browsers. Issued via Let's Encrypt",
|
"description": "Free SSL certificates for Node.js and Browsers. Issued via Let's Encrypt",
|
||||||
"homepage": "https://rootprojects.org/acme/",
|
"homepage": "https://rootprojects.org/acme/",
|
||||||
"main": "acme.js",
|
"main": "acme.js",
|
||||||
|
@ -42,14 +42,14 @@
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@root/csr": "^0.8.1",
|
|
||||||
"@root/encoding": "^1.0.1",
|
"@root/encoding": "^1.0.1",
|
||||||
"@root/keypairs": "^0.10.0",
|
"@root/keypairs": "^0.9.0",
|
||||||
"@root/pem": "^1.0.4",
|
"@root/pem": "^1.0.4",
|
||||||
"@root/request": "^1.6.1",
|
"@root/request": "^1.3.11",
|
||||||
"@root/x509": "^0.7.2"
|
"@root/x509": "^0.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@root/csr": "^0.8.1",
|
||||||
"dig.js": "^1.3.9",
|
"dig.js": "^1.3.9",
|
||||||
"dns-suite": "^1.2.13",
|
"dns-suite": "^1.2.13",
|
||||||
"dotenv": "^8.1.0",
|
"dotenv": "^8.1.0",
|
||||||
|
|
|
@ -51,7 +51,7 @@ async function main() {
|
||||||
challenge: ch,
|
challenge: ch,
|
||||||
dnsPrefix: '_test-challenge'
|
dnsPrefix: '_test-challenge'
|
||||||
})
|
})
|
||||||
.then(function (auth) {
|
.then(function(auth) {
|
||||||
if ('dns-01' === ch.type) {
|
if ('dns-01' === ch.type) {
|
||||||
if (auth.keyAuthorizationDigest !== expectedKeyAuthDigest) {
|
if (auth.keyAuthorizationDigest !== expectedKeyAuthDigest) {
|
||||||
console.error('[keyAuthorizationDigest]');
|
console.error('[keyAuthorizationDigest]');
|
||||||
|
@ -84,7 +84,7 @@ async function main() {
|
||||||
console.info('PASS', hostname, ch.type);
|
console.info('PASS', hostname, ch.type);
|
||||||
return next();
|
return next();
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
err.message =
|
err.message =
|
||||||
'Error computing ' +
|
'Error computing ' +
|
||||||
ch.type +
|
ch.type +
|
||||||
|
@ -99,12 +99,12 @@ async function main() {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function() {
|
||||||
return main(authorization)
|
return main(authorization)
|
||||||
.then(function () {
|
.then(function() {
|
||||||
console.info('PASS');
|
console.info('PASS');
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,10 +37,10 @@ var tests = [
|
||||||
|
|
||||||
var ACME = require('../');
|
var ACME = require('../');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function() {
|
||||||
console.info('\n[Test] can split and format PEM chain properly');
|
console.info('\n[Test] can split and format PEM chain properly');
|
||||||
|
|
||||||
tests.forEach(function (str) {
|
tests.forEach(function(str) {
|
||||||
var actual = ACME.formatPemChain(str);
|
var actual = ACME.formatPemChain(str);
|
||||||
if (expected !== actual) {
|
if (expected !== actual) {
|
||||||
console.error('input: ', JSON.stringify(str));
|
console.error('input: ', JSON.stringify(str));
|
||||||
|
@ -68,7 +68,7 @@ module.exports = function () {
|
||||||
|
|
||||||
ACME.splitPemChain(
|
ACME.splitPemChain(
|
||||||
'--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n'
|
'--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n'
|
||||||
).forEach(function (str) {
|
).forEach(function(str) {
|
||||||
if ('--B--\nxxxx\nyyyy\n--E--\n' !== str) {
|
if ('--B--\nxxxx\nyyyy\n--E--\n' !== str) {
|
||||||
throw new Error('bad thingy');
|
throw new Error('bad thingy');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = async function () {
|
module.exports = async function() {
|
||||||
console.log('[Test] can generate, export, and import key');
|
console.log('[Test] can generate, export, and import key');
|
||||||
var Keypairs = require('@root/keypairs');
|
var Keypairs = require('@root/keypairs');
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ module.exports = async function () {
|
||||||
var jwk = await Keypairs.import({
|
var jwk = await Keypairs.import({
|
||||||
pem: pem
|
pem: pem
|
||||||
});
|
});
|
||||||
['kty', 'd', 'n', 'e'].forEach(function (k) {
|
['kty', 'd', 'n', 'e'].forEach(function(k) {
|
||||||
if (!jwk[k] || jwk[k] !== certKeypair.private[k]) {
|
if (!jwk[k] || jwk[k] !== certKeypair.private[k]) {
|
||||||
throw new Error('bad export/import');
|
throw new Error('bad export/import');
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,14 @@ var pluginPrefix = 'acme-' + config.challengeType + '-';
|
||||||
var pluginName = config.challengeModule;
|
var pluginName = config.challengeModule;
|
||||||
var plugin;
|
var plugin;
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function() {
|
||||||
console.info('\n[Test] end-to-end issue certificates');
|
console.info('\n[Test] end-to-end issue certificates');
|
||||||
|
|
||||||
var acme = ACME.create({
|
var acme = ACME.create({
|
||||||
// debug: true
|
// debug: true
|
||||||
maintainerEmail: config.email,
|
maintainerEmail: config.email,
|
||||||
packageAgent: 'test-' + pkg.name + '/' + pkg.version,
|
packageAgent: 'test-' + pkg.name + '/' + pkg.version,
|
||||||
notify: function (ev, params) {
|
notify: function(ev, params) {
|
||||||
console.info(
|
console.info(
|
||||||
'\t' + ev,
|
'\t' + ev,
|
||||||
params.subject || params.altname || params.domain || '',
|
params.subject || params.altname || params.domain || '',
|
||||||
|
@ -169,7 +169,7 @@ module.exports = function () {
|
||||||
console.info('Get certificates for random domains:');
|
console.info('Get certificates for random domains:');
|
||||||
console.info(
|
console.info(
|
||||||
domains
|
domains
|
||||||
.map(function (puny) {
|
.map(function(puny) {
|
||||||
var uni = punycode.toUnicode(puny);
|
var uni = punycode.toUnicode(puny);
|
||||||
if (puny !== uni) {
|
if (puny !== uni) {
|
||||||
return puny + ' (' + uni + ')';
|
return puny + ' (' + uni + ')';
|
||||||
|
@ -221,25 +221,25 @@ module.exports = function () {
|
||||||
// Try EC + RSA
|
// Try EC + RSA
|
||||||
var rnd = random();
|
var rnd = random();
|
||||||
happyPath('EC', 'RSA', rnd)
|
happyPath('EC', 'RSA', rnd)
|
||||||
.then(function () {
|
.then(function() {
|
||||||
console.info('PASS: ECDSA account key with RSA server key');
|
console.info('PASS: ECDSA account key with RSA server key');
|
||||||
// Now try RSA + EC
|
// Now try RSA + EC
|
||||||
rnd = random();
|
rnd = random();
|
||||||
return happyPath('RSA', 'EC', rnd).then(function () {
|
return happyPath('RSA', 'EC', rnd).then(function() {
|
||||||
console.info('PASS: RSA account key with ECDSA server key');
|
console.info('PASS: RSA account key with ECDSA server key');
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function() {
|
||||||
console.info('PASS');
|
console.info('PASS');
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function(err) {
|
||||||
console.error('Error:');
|
console.error('Error:');
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
function randomDomains(rnd) {
|
function randomDomains(rnd) {
|
||||||
return ['foo-acmejs', 'bar-acmejs', '*.baz-acmejs', 'baz-acmejs'].map(
|
return ['foo-acmejs', 'bar-acmejs', '*.baz-acmejs', 'baz-acmejs'].map(
|
||||||
function (pre) {
|
function(pre) {
|
||||||
return punycode.toASCII(pre + '-' + rnd + '.' + config.domain);
|
return punycode.toASCII(pre + '-' + rnd + '.' + config.domain);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -247,7 +247,12 @@ module.exports = function () {
|
||||||
|
|
||||||
function random() {
|
function random() {
|
||||||
return (
|
return (
|
||||||
parseInt(Math.random().toString().slice(2, 99), 10)
|
parseInt(
|
||||||
|
Math.random()
|
||||||
|
.toString()
|
||||||
|
.slice(2, 99),
|
||||||
|
10
|
||||||
|
)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
.slice(0, 4) + '例'
|
.slice(0, 4) + '例'
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ native
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 2
|
end: 2
|
||||||
})
|
})
|
||||||
.then(function (hashcash) {
|
.then(function(hashcash) {
|
||||||
if ('00:76de' !== hashcash) {
|
if ('00:76de' !== hashcash) {
|
||||||
throw new Error('hashcash algorthim changed');
|
throw new Error('hashcash algorthim changed');
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ native
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 2
|
end: 2
|
||||||
})
|
})
|
||||||
.then(function (hashcash) {
|
.then(function(hashcash) {
|
||||||
if ('10:00' !== hashcash) {
|
if ('10:00' !== hashcash) {
|
||||||
throw new Error('hashcash algorthim changed');
|
throw new Error('hashcash algorthim changed');
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,10 @@ native
|
||||||
|
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
var nonce = '20';
|
var nonce = '20';
|
||||||
var needle = crypto.randomBytes(3).toString('hex').slice(0, 5);
|
var needle = crypto
|
||||||
|
.randomBytes(3)
|
||||||
|
.toString('hex')
|
||||||
|
.slice(0, 5);
|
||||||
native
|
native
|
||||||
._hashcash({
|
._hashcash({
|
||||||
alg: 'SHA-256',
|
alg: 'SHA-256',
|
||||||
|
@ -42,7 +45,7 @@ native
|
||||||
start: 0,
|
start: 0,
|
||||||
end: Math.ceil(needle.length / 2)
|
end: Math.ceil(needle.length / 2)
|
||||||
})
|
})
|
||||||
.then(function (hashcash) {
|
.then(function(hashcash) {
|
||||||
var later = Date.now();
|
var later = Date.now();
|
||||||
var parts = hashcash.split(':');
|
var parts = hashcash.split(':');
|
||||||
var answer = parts[1];
|
var answer = parts[1];
|
||||||
|
|
31
utils.js
31
utils.js
|
@ -6,18 +6,17 @@ var Keypairs = require('@root/keypairs');
|
||||||
var UserAgent = require('./lib/node/client-user-agent.js');
|
var UserAgent = require('./lib/node/client-user-agent.js');
|
||||||
|
|
||||||
// Handle nonce, signing, and request altogether
|
// Handle nonce, signing, and request altogether
|
||||||
U._jwsRequest = function (me, bigopts) {
|
U._jwsRequest = function(me, bigopts) {
|
||||||
return U._getNonce(me).then(function (nonce) {
|
return U._getNonce(me).then(function(nonce) {
|
||||||
bigopts.protected.nonce = nonce;
|
bigopts.protected.nonce = nonce;
|
||||||
bigopts.protected.url = bigopts.url;
|
bigopts.protected.url = bigopts.url;
|
||||||
// protected.alg: added by Keypairs.signJws
|
// protected.alg: added by Keypairs.signJws
|
||||||
if (bigopts.protected.jwk) {
|
if (!bigopts.protected.jwk) {
|
||||||
bigopts.protected.kid = false;
|
// protected.kid must be overwritten due to ACME's interpretation of the spec
|
||||||
} else if (!('kid' in bigopts.protected)) {
|
if (!('kid' in bigopts.protected)) {
|
||||||
// protected.kid must be provided according to ACME's interpretation of the spec
|
|
||||||
// (using the provided URL rather than the Key's Thumbprint as Key ID)
|
|
||||||
bigopts.protected.kid = bigopts.kid;
|
bigopts.protected.kid = bigopts.kid;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this will shasum the thumbprint the 2nd time
|
// this will shasum the thumbprint the 2nd time
|
||||||
return Keypairs.signJws({
|
return Keypairs.signJws({
|
||||||
|
@ -25,12 +24,12 @@ U._jwsRequest = function (me, bigopts) {
|
||||||
protected: bigopts.protected,
|
protected: bigopts.protected,
|
||||||
payload: bigopts.payload
|
payload: bigopts.payload
|
||||||
})
|
})
|
||||||
.then(function (jws) {
|
.then(function(jws) {
|
||||||
//#console.debug('[ACME.js] url: ' + bigopts.url + ':');
|
//#console.debug('[ACME.js] url: ' + bigopts.url + ':');
|
||||||
//#console.debug(jws);
|
//#console.debug(jws);
|
||||||
return U._request(me, { url: bigopts.url, json: jws });
|
return U._request(me, { url: bigopts.url, json: jws });
|
||||||
})
|
})
|
||||||
.catch(function (e) {
|
.catch(function(e) {
|
||||||
if (/badNonce$/.test(e.urn)) {
|
if (/badNonce$/.test(e.urn)) {
|
||||||
// retry badNonces
|
// retry badNonces
|
||||||
var retryable = bigopts._retries >= 2;
|
var retryable = bigopts._retries >= 2;
|
||||||
|
@ -44,7 +43,7 @@ U._jwsRequest = function (me, bigopts) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
U._getNonce = function (me) {
|
U._getNonce = function(me) {
|
||||||
var nonce;
|
var nonce;
|
||||||
while (true) {
|
while (true) {
|
||||||
nonce = me._nonces.shift();
|
nonce = me._nonces.shift();
|
||||||
|
@ -65,13 +64,13 @@ U._getNonce = function (me) {
|
||||||
return U._request(me, {
|
return U._request(me, {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
url: me._directoryUrls.newNonce
|
url: me._directoryUrls.newNonce
|
||||||
}).then(function (resp) {
|
}).then(function(resp) {
|
||||||
return resp.headers['replay-nonce'];
|
return resp.headers['replay-nonce'];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle some ACME-specific defaults
|
// Handle some ACME-specific defaults
|
||||||
U._request = function (me, opts) {
|
U._request = function(me, opts) {
|
||||||
// no-op on browser
|
// no-op on browser
|
||||||
var ua = UserAgent.get(me, opts);
|
var ua = UserAgent.get(me, opts);
|
||||||
|
|
||||||
|
@ -101,7 +100,7 @@ U._request = function (me, opts) {
|
||||||
|
|
||||||
//console.log('\n[debug] REQUEST');
|
//console.log('\n[debug] REQUEST');
|
||||||
//console.log(opts);
|
//console.log(opts);
|
||||||
return me.__request(opts).then(function (resp) {
|
return me.__request(opts).then(function(resp) {
|
||||||
if (resp.toJSON) {
|
if (resp.toJSON) {
|
||||||
resp = resp.toJSON();
|
resp = resp.toJSON();
|
||||||
}
|
}
|
||||||
|
@ -140,11 +139,11 @@ U._request = function (me, opts) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
U._setNonce = function (me, nonce) {
|
U._setNonce = function(me, nonce) {
|
||||||
me._nonces.unshift({ nonce: nonce, createdAt: Date.now() });
|
me._nonces.unshift({ nonce: nonce, createdAt: Date.now() });
|
||||||
};
|
};
|
||||||
|
|
||||||
U._importKeypair = function (key) {
|
U._importKeypair = function(key) {
|
||||||
var p;
|
var p;
|
||||||
var pub;
|
var pub;
|
||||||
|
|
||||||
|
@ -163,7 +162,7 @@ U._importKeypair = function (key) {
|
||||||
throw new Error('no private key given');
|
throw new Error('no private key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.then(function (pair) {
|
return p.then(function(pair) {
|
||||||
if (pair.public.kid) {
|
if (pair.public.kid) {
|
||||||
pair = JSON.parse(JSON.stringify(pair));
|
pair = JSON.parse(JSON.stringify(pair));
|
||||||
delete pair.public.kid;
|
delete pair.public.kid;
|
||||||
|
|
Loading…
Reference in New Issue