https
express server läuft jetzt mit https
This commit is contained in:
18
express-server/node_modules/acme-v2/.jshintrc
generated
vendored
Normal file
18
express-server/node_modules/acme-v2/.jshintrc
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{ "node": true
|
||||
, "browser": true
|
||||
, "jquery": true
|
||||
, "globals": { "angular": true, "Promise": true }
|
||||
|
||||
, "indent": 2
|
||||
, "onevar": true
|
||||
, "laxcomma": true
|
||||
, "laxbreak": true
|
||||
, "curly": true
|
||||
, "nonbsp": true
|
||||
|
||||
, "eqeqeq": true
|
||||
, "immed": true
|
||||
, "undef": true
|
||||
, "unused": true
|
||||
, "latedef": true
|
||||
}
|
41
express-server/node_modules/acme-v2/LICENSE
generated
vendored
Normal file
41
express-server/node_modules/acme-v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
Copyright 2018 AJ ONeal
|
||||
|
||||
This is open source software; you can redistribute it and/or modify it under the
|
||||
terms of either:
|
||||
|
||||
a) the "MIT License"
|
||||
b) the "Apache-2.0 License"
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Apache-2.0 License Summary
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
221
express-server/node_modules/acme-v2/README.md
generated
vendored
Normal file
221
express-server/node_modules/acme-v2/README.md
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
| Sponsored by [ppl](https://ppl.family)
|
||||
| **acme-v2.js** ([npm](https://www.npmjs.com/package/acme-v2))
|
||||
| [acme-v2-cli.js](https://git.coolaj86.com/coolaj86/acme-v2-cli.js)
|
||||
| [greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js)
|
||||
| [goldilocks.js](https://git.coolaj86.com/coolaj86/goldilocks.js)
|
||||
|
|
||||
|
||||
acme-v2.js
|
||||
==========
|
||||
|
||||
A framework for building Let's Encrypt v2 (ACME draft 11) clients, successor to `le-acme-core.js`.
|
||||
Built [by request](https://git.coolaj86.com/coolaj86/greenlock.js/issues/5#issuecomment-8).
|
||||
|
||||
## Looking for Quick 'n' Easy™?
|
||||
|
||||
If you're looking for an *ACME-enabled webserver*, try [goldilocks.js](https://git.coolaj86.com/coolaj86/goldilocks.js).
|
||||
If you're looking to *build a webserver*, try [greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js).
|
||||
|
||||
* [greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js)
|
||||
* [goldilocks.js](https://git.coolaj86.com/coolaj86/goldilocks.js)
|
||||
|
||||
## How to build ACME clients
|
||||
|
||||
As this is intended to build ACME clients, there is not a simple 2-line example.
|
||||
|
||||
I'd recommend first running the example CLI client with a test domain and then investigating the files used for that example:
|
||||
|
||||
```bash
|
||||
node examples/cli.js
|
||||
```
|
||||
|
||||
The example cli has the following prompts:
|
||||
|
||||
```
|
||||
What web address(es) would you like to get certificates for? (ex: example.com,*.example.com)
|
||||
What challenge will you be testing today? http-01 or dns-01? [http-01]
|
||||
What email should we use? (optional)
|
||||
What API style would you like to test? v1-compat or promise? [v1-compat]
|
||||
|
||||
Put the string 'mBfh0SqaAV3MOK3B6cAhCbIReAyDuwuxlO1Sl70x6bM.VNAzCR4THe4czVzo9piNn73B1ZXRLaB2CESwJfKkvRM' into a file at 'example.com/.well-known/acme-challenge/mBfh0SqaAV3MOK3B6cAhCbIReAyDuwuxlO1Sl70x6bM'
|
||||
|
||||
echo 'mBfh0SqaAV3MOK3B6cAhCbIReAyDuwuxlO1Sl70x6bM.VNAzCR4THe4czVzo9piNn73B1ZXRLaB2CESwJfKkvRM' > 'example.com/.well-known/acme-challenge/mBfh0SqaAV3MOK3B6cAhCbIReAyDuwuxlO1Sl70x6bM'
|
||||
|
||||
Then hit the 'any' key to continue...
|
||||
```
|
||||
|
||||
When you've completed the challenge you can hit a key to continue the process.
|
||||
|
||||
If you place the certificate you receive back in `tests/fullchain.pem`
|
||||
you can then test it with `examples/https-server.js`.
|
||||
|
||||
```
|
||||
examples/cli.js
|
||||
examples/genkeypair.js
|
||||
tests/compat.js
|
||||
examples/https-server.js
|
||||
examples/http-server.js
|
||||
```
|
||||
|
||||
## Let's Encrypt Directory URLs
|
||||
|
||||
```
|
||||
# Production URL
|
||||
https://acme-v02.api.letsencrypt.org/directory
|
||||
```
|
||||
|
||||
```
|
||||
# Staging URL
|
||||
https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
```
|
||||
|
||||
## Two API versions, Two Implementations
|
||||
|
||||
This library (acme-v2.js) supports ACME [*draft 11*](https://tools.ietf.org/html/draft-ietf-acme-acme-11),
|
||||
otherwise known as Let's Encrypt v2 (or v02).
|
||||
|
||||
* ACME draft 11
|
||||
* Let's Encrypt v2
|
||||
* Let's Encrypt v02
|
||||
|
||||
The predecessor (le-acme-core) supports Let's Encrypt v1 (or v01), which was a
|
||||
[hodge-podge of various drafts](https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md)
|
||||
of the ACME spec early on.
|
||||
|
||||
* ACME early draft
|
||||
* Let's Encrypt v1
|
||||
* Let's Encrypt v01
|
||||
|
||||
This library maintains compatibility with le-acme-core so that it can be used as a **drop-in replacement**
|
||||
and requires **no changes to existing code**,
|
||||
but also provides an updated API more congruent with draft 11.
|
||||
|
||||
## le-acme-core-compatible API (recommended)
|
||||
|
||||
Status: Stable, Locked, Bugfix-only
|
||||
|
||||
See Full Documentation at <https://git.coolaj86.com/coolaj86/le-acme-core.js>
|
||||
|
||||
```
|
||||
var RSA = require('rsa-compat').RSA;
|
||||
var acme = require('acme-v2/compat.js').ACME.create({ RSA: RSA });
|
||||
|
||||
//
|
||||
// Use exactly the same as le-acme-core
|
||||
//
|
||||
```
|
||||
|
||||
## Promise API (dev)
|
||||
|
||||
Status: Almost stable, but **not semver locked**
|
||||
|
||||
This API is a simple evolution of le-acme-core,
|
||||
but tries to provide a better mapping to the new draft 11 APIs.
|
||||
|
||||
```
|
||||
// Create Instance (Dependency Injection)
|
||||
var ACME = require('acme-v2').ACME.create({
|
||||
RSA: require('rsa-compat').RSA
|
||||
|
||||
// other overrides
|
||||
, request: require('request')
|
||||
, promisify: require('util').promisify
|
||||
|
||||
// used for constructing user-agent
|
||||
, os: require('os')
|
||||
, process: require('process')
|
||||
|
||||
// used for overriding the default user-agent
|
||||
, userAgent: 'My custom UA String'
|
||||
, getUserAgentString: function (deps) { return 'My custom UA String'; }
|
||||
|
||||
|
||||
// don't try to validate challenges locally
|
||||
, skipChallengeTest: false
|
||||
// ask if the certificate can be issued up to 10 times before failing
|
||||
, retryPoll: 8
|
||||
// ask if the certificate has been validated up to 6 times before cancelling
|
||||
, retryPending: 4
|
||||
// Wait 1000ms between retries
|
||||
, retryInterval: 1000
|
||||
// Wait 10,000ms after deauthorizing a challenge before retrying
|
||||
, deauthWait: 10 * 1000
|
||||
});
|
||||
|
||||
|
||||
// Discover Directory URLs
|
||||
ACME.init(acmeDirectoryUrl) // returns Promise<acmeUrls={keyChange,meta,newAccount,newNonce,newOrder,revokeCert}>
|
||||
|
||||
|
||||
// Accounts
|
||||
ACME.accounts.create(options) // returns Promise<regr> registration data
|
||||
|
||||
{ email: '<email>' // valid email (server checks MX records)
|
||||
, accountKeypair: { // privateKeyPem or privateKeyJwt
|
||||
privateKeyPem: '<ASCII PEM>'
|
||||
}
|
||||
, agreeToTerms: fn (tosUrl) {} // returns Promise with tosUrl
|
||||
}
|
||||
|
||||
|
||||
// Registration
|
||||
ACME.certificates.create(options) // returns Promise<pems={ privkey (key), cert, chain (ca) }>
|
||||
|
||||
{ newAuthzUrl: '<url>' // specify acmeUrls.newAuthz
|
||||
, newCertUrl: '<url>' // specify acmeUrls.newCert
|
||||
|
||||
, domainKeypair: {
|
||||
privateKeyPem: '<ASCII PEM>'
|
||||
}
|
||||
, accountKeypair: {
|
||||
privateKeyPem: '<ASCII PEM>'
|
||||
}
|
||||
, domains: [ 'example.com' ]
|
||||
|
||||
, setChallenge: fn (hostname, key, val) // return Promise
|
||||
, removeChallenge: fn (hostname, key) // return Promise
|
||||
}
|
||||
```
|
||||
|
||||
Helpers & Stuff
|
||||
|
||||
```javascript
|
||||
// Constants
|
||||
ACME.challengePrefixes['http-01'] // '/.well-known/acme-challenge'
|
||||
ACME.challengePrefixes['dns-01'] // '_acme-challenge'
|
||||
```
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
* v1.0.5 - cleanup logging
|
||||
* v1.0.4 - v6- compat use `promisify` from node's util or bluebird
|
||||
* v1.0.3 - documentation cleanup
|
||||
* v1.0.2
|
||||
* use `options.contact` to provide raw contact array
|
||||
* made `options.email` optional
|
||||
* file cleanup
|
||||
* v1.0.1
|
||||
* Compat API is ready for use
|
||||
* Eliminate debug logging
|
||||
* Apr 10, 2018 - tested backwards-compatibility using greenlock.js
|
||||
* Apr 5, 2018 - export http and dns challenge tests
|
||||
* Apr 5, 2018 - test http and dns challenges (success and failure)
|
||||
* Apr 5, 2018 - test subdomains and its wildcard
|
||||
* Apr 5, 2018 - test two subdomains
|
||||
* Apr 5, 2018 - test wildcard
|
||||
* Apr 5, 2018 - completely match api for acme v1 (le-acme-core.js)
|
||||
* Mar 21, 2018 - *mostly* matches le-acme-core.js API
|
||||
* Mar 21, 2018 - can now accept values (not hard coded)
|
||||
* Mar 20, 2018 - SUCCESS - got a test certificate (hard-coded)
|
||||
* Mar 20, 2018 - download certificate
|
||||
* Mar 20, 2018 - poll for status
|
||||
* Mar 20, 2018 - finalize order (submit csr)
|
||||
* Mar 20, 2018 - generate domain keypair
|
||||
* Mar 20, 2018 - respond to challenges
|
||||
* Mar 16, 2018 - get challenges
|
||||
* Mar 16, 2018 - new order
|
||||
* Mar 15, 2018 - create account
|
||||
* Mar 15, 2018 - generate account keypair
|
||||
* Mar 15, 2018 - get nonce
|
||||
* Mar 15, 2018 - get directory
|
77
express-server/node_modules/acme-v2/compat.js
generated
vendored
Normal file
77
express-server/node_modules/acme-v2/compat.js
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
/* global Promise */
|
||||
|
||||
var ACME2 = require('./').ACME;
|
||||
|
||||
function resolveFn(cb) {
|
||||
return function (val) {
|
||||
// nextTick to get out of Promise chain
|
||||
process.nextTick(function () { cb(null, val); });
|
||||
};
|
||||
}
|
||||
function rejectFn(cb) {
|
||||
return function (err) {
|
||||
console.error('[acme-v2] handled(?) rejection as errback:');
|
||||
console.error(err.stack);
|
||||
|
||||
// nextTick to get out of Promise chain
|
||||
process.nextTick(function () { cb(err); });
|
||||
|
||||
// do not resolve promise further
|
||||
return new Promise(function () {});
|
||||
};
|
||||
}
|
||||
|
||||
function create(deps) {
|
||||
deps.LeCore = {};
|
||||
var acme2 = ACME2.create(deps);
|
||||
acme2.registerNewAccount = function (options, cb) {
|
||||
acme2.accounts.create(options).then(resolveFn(cb), rejectFn(cb));
|
||||
};
|
||||
acme2.getCertificate = function (options, cb) {
|
||||
options.agreeToTerms = options.agreeToTerms || function (tos) {
|
||||
return Promise.resolve(tos);
|
||||
};
|
||||
acme2.certificates.create(options).then(function (certs) {
|
||||
var privkeyPem = acme2.RSA.exportPrivatePem(options.domainKeypair);
|
||||
certs.privkey = privkeyPem;
|
||||
resolveFn(cb)(certs);
|
||||
}, rejectFn(cb));
|
||||
};
|
||||
acme2.getAcmeUrls = function (options, cb) {
|
||||
acme2.init(options).then(resolveFn(cb), rejectFn(cb));
|
||||
};
|
||||
acme2.getOptions = function () {
|
||||
var defs = {};
|
||||
|
||||
Object.keys(module.exports.defaults).forEach(function (key) {
|
||||
defs[key] = defs[deps] || module.exports.defaults[key];
|
||||
});
|
||||
|
||||
return defs;
|
||||
};
|
||||
acme2.stagingServerUrl = module.exports.defaults.stagingServerUrl;
|
||||
acme2.productionServerUrl = module.exports.defaults.productionServerUrl;
|
||||
acme2.acmeChallengePrefix = module.exports.defaults.acmeChallengePrefix;
|
||||
return acme2;
|
||||
}
|
||||
|
||||
module.exports.ACME = { };
|
||||
module.exports.defaults = {
|
||||
productionServerUrl: 'https://acme-v02.api.letsencrypt.org/directory'
|
||||
, stagingServerUrl: 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
||||
, knownEndpoints: [ 'keyChange', 'meta', 'newAccount', 'newNonce', 'newOrder', 'revokeCert' ]
|
||||
, challengeTypes: [ 'http-01', 'dns-01' ]
|
||||
, challengeType: 'http-01'
|
||||
//, keyType: 'rsa' // ecdsa
|
||||
//, keySize: 2048 // 256
|
||||
, rsaKeySize: 2048 // 256
|
||||
, acmeChallengePrefix: '/.well-known/acme-challenge/'
|
||||
};
|
||||
Object.keys(module.exports.defaults).forEach(function (key) {
|
||||
module.exports.ACME[key] = module.exports.defaults[key];
|
||||
});
|
||||
Object.keys(ACME2).forEach(function (key) {
|
||||
module.exports.ACME[key] = ACME2[key];
|
||||
});
|
||||
module.exports.ACME.create = create;
|
68
express-server/node_modules/acme-v2/examples/cli.js
generated
vendored
Normal file
68
express-server/node_modules/acme-v2/examples/cli.js
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
var RSA = require('rsa-compat').RSA;
|
||||
var readline = require('readline');
|
||||
var rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
require('./genkeypair.js');
|
||||
|
||||
function getWeb() {
|
||||
rl.question('What web address(es) would you like to get certificates for? (ex: example.com,*.example.com) ', function (web) {
|
||||
web = (web||'').trim().split(/,/g);
|
||||
if (!web[0]) { getWeb(); return; }
|
||||
|
||||
if (web.some(function (w) { return '*' === w[0]; })) {
|
||||
console.log('Wildcard domains must use dns-01');
|
||||
getEmail(web, 'dns-01');
|
||||
} else {
|
||||
getChallengeType(web);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getChallengeType(web) {
|
||||
rl.question('What challenge will you be testing today? http-01 or dns-01? [http-01] ', function (chType) {
|
||||
chType = (chType||'').trim();
|
||||
if (!chType) { chType = 'http-01'; }
|
||||
|
||||
getEmail(web, chType);
|
||||
});
|
||||
}
|
||||
|
||||
function getEmail(web, chType) {
|
||||
rl.question('What email should we use? (optional) ', function (email) {
|
||||
email = (email||'').trim();
|
||||
if (!email) { email = null; }
|
||||
|
||||
getApiStyle(web, chType, email);
|
||||
});
|
||||
}
|
||||
|
||||
function getApiStyle(web, chType, email) {
|
||||
var defaultStyle = 'compat';
|
||||
rl.question('What API style would you like to test? v1-compat or promise? [v1-compat] ', function (apiStyle) {
|
||||
apiStyle = (apiStyle||'').trim();
|
||||
if (!apiStyle) { apiStyle = 'v1-compat'; }
|
||||
|
||||
rl.close();
|
||||
|
||||
var RSA = require('rsa-compat').RSA;
|
||||
var accountKeypair = RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/../tests/account.privkey.pem') });
|
||||
var domainKeypair = RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/../tests/privkey.pem') });
|
||||
var directoryUrl = 'https://acme-staging-v02.api.letsencrypt.org/directory';
|
||||
|
||||
if ('promise' === apiStyle) {
|
||||
require('../tests/promise.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair);
|
||||
} else if ('cb' === apiStyle) {
|
||||
require('../tests/cb.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair);
|
||||
} else {
|
||||
if ('v1-compat' !== apiStyle) { console.warn("Didn't understand '" + apiStyle + "', using 'v1-compat' instead..."); }
|
||||
require('../tests/compat.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getWeb();
|
22
express-server/node_modules/acme-v2/examples/genkeypair.js
generated
vendored
Normal file
22
express-server/node_modules/acme-v2/examples/genkeypair.js
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
var RSA = require('rsa-compat').RSA;
|
||||
var fs = require('fs');
|
||||
|
||||
if (!fs.existsSync(__dirname + '/../tests/account.privkey.pem')) {
|
||||
RSA.generateKeypair(2048, 65537, {}, function (err, keypair) {
|
||||
console.log(keypair);
|
||||
var privkeyPem = RSA.exportPrivatePem(keypair)
|
||||
console.log(privkeyPem);
|
||||
|
||||
fs.writeFileSync(__dirname + '/../tests/account.privkey.pem', privkeyPem);
|
||||
});
|
||||
}
|
||||
|
||||
if (!fs.existsSync(__dirname + '/../tests/privkey.pem')) {
|
||||
RSA.generateKeypair(2048, 65537, {}, function (err, keypair) {
|
||||
console.log(keypair);
|
||||
var privkeyPem = RSA.exportPrivatePem(keypair)
|
||||
console.log(privkeyPem);
|
||||
|
||||
fs.writeFileSync(__dirname + '/../tests/privkey.pem', privkeyPem);
|
||||
});
|
||||
}
|
7
express-server/node_modules/acme-v2/examples/http-server.js
generated
vendored
Normal file
7
express-server/node_modules/acme-v2/examples/http-server.js
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var http = require('http');
|
||||
var express = require('express');
|
||||
var server = http.createServer(express.static('../tests')).listen(80, function () {
|
||||
console.log('Listening on', this.address());
|
||||
});
|
11
express-server/node_modules/acme-v2/examples/https-server.js
generated
vendored
Normal file
11
express-server/node_modules/acme-v2/examples/https-server.js
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
var https = require('https');
|
||||
var server = https.createServer({
|
||||
key: require('fs').readFileSync('../tests/privkey.pem')
|
||||
, cert: require('fs').readFileSync('../tests/fullchain.pem')
|
||||
}, function (req, res) {
|
||||
res.end("Hello, World!");
|
||||
}).listen(443, function () {
|
||||
console.log('Listening on', this.address());
|
||||
});
|
719
express-server/node_modules/acme-v2/node.js
generated
vendored
Normal file
719
express-server/node_modules/acme-v2/node.js
generated
vendored
Normal file
@ -0,0 +1,719 @@
|
||||
/*!
|
||||
* acme-v2.js
|
||||
* Copyright(c) 2018 AJ ONeal <aj@ppl.family> https://ppl.family
|
||||
* Apache-2.0 OR MIT (and hence also MPL 2.0)
|
||||
*/
|
||||
'use strict';
|
||||
/* globals Promise */
|
||||
|
||||
var ACME = module.exports.ACME = {};
|
||||
|
||||
ACME.formatPemChain = function formatPemChain(str) {
|
||||
return str.trim().replace(/[\r\n]+/g, '\n').replace(/\-\n\-/g, '-\n\n-') + '\n';
|
||||
};
|
||||
ACME.splitPemChain = function splitPemChain(str) {
|
||||
return str.trim().split(/[\r\n]{2,}/g).map(function (str) {
|
||||
return str + '\n';
|
||||
});
|
||||
};
|
||||
|
||||
ACME.challengePrefixes = {
|
||||
'http-01': '/.well-known/acme-challenge'
|
||||
, 'dns-01': '_acme-challenge'
|
||||
};
|
||||
ACME.challengeTests = {
|
||||
'http-01': function (me, auth) {
|
||||
var url = 'http://' + auth.hostname + ACME.challengePrefixes['http-01'] + '/' + auth.token;
|
||||
return me._request({ url: url }).then(function (resp) {
|
||||
var err;
|
||||
|
||||
if (auth.keyAuthorization === resp.body.toString('utf8').trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = new Error(
|
||||
"Error: Failed HTTP-01 Dry Run.\n"
|
||||
+ "curl '" + url + "' does not return '" + auth.keyAuthorization + "'\n"
|
||||
+ "See https://git.coolaj86.com/coolaj86/acme-v2.js/issues/4"
|
||||
);
|
||||
err.code = 'E_FAIL_DRY_CHALLENGE';
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
, 'dns-01': function (me, auth) {
|
||||
var hostname = ACME.challengePrefixes['dns-01'] + '.' + auth.hostname;
|
||||
return me._dig({
|
||||
type: 'TXT'
|
||||
, name: hostname
|
||||
}).then(function (ans) {
|
||||
var err;
|
||||
|
||||
if (ans.answer.some(function (txt) {
|
||||
return auth.dnsAuthorization === txt.data[0];
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = new Error(
|
||||
"Error: Failed DNS-01 Dry Run.\n"
|
||||
+ "dig TXT '" + hostname + "' does not return '" + auth.dnsAuthorization + "'\n"
|
||||
+ "See https://git.coolaj86.com/coolaj86/acme-v2.js/issues/4"
|
||||
);
|
||||
err.code = 'E_FAIL_DRY_CHALLENGE';
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ACME._getUserAgentString = function (deps) {
|
||||
var uaDefaults = {
|
||||
pkg: "Greenlock/" + deps.pkg.version
|
||||
, os: "(" + deps.os.type() + "; " + deps.process.arch + " " + deps.os.platform() + " " + deps.os.release() + ")"
|
||||
, node: "Node.js/" + deps.process.version
|
||||
, user: ''
|
||||
};
|
||||
|
||||
var userAgent = [];
|
||||
|
||||
//Object.keys(currentUAProps)
|
||||
Object.keys(uaDefaults).forEach(function (key) {
|
||||
if (uaDefaults[key]) {
|
||||
userAgent.push(uaDefaults[key]);
|
||||
}
|
||||
});
|
||||
|
||||
return userAgent.join(' ').trim();
|
||||
};
|
||||
ACME._directory = function (me) {
|
||||
return me._request({ url: me.directoryUrl, json: true });
|
||||
};
|
||||
ACME._getNonce = function (me) {
|
||||
if (me._nonce) { return new Promise(function (resolve) { resolve(me._nonce); return; }); }
|
||||
return me._request({ method: 'HEAD', url: me._directoryUrls.newNonce }).then(function (resp) {
|
||||
me._nonce = resp.toJSON().headers['replay-nonce'];
|
||||
return me._nonce;
|
||||
});
|
||||
};
|
||||
// ACME RFC Section 7.3 Account Creation
|
||||
/*
|
||||
{
|
||||
"protected": base64url({
|
||||
"alg": "ES256",
|
||||
"jwk": {...},
|
||||
"nonce": "6S8IqOGY7eL2lsGoTZYifg",
|
||||
"url": "https://example.com/acme/new-account"
|
||||
}),
|
||||
"payload": base64url({
|
||||
"termsOfServiceAgreed": true,
|
||||
"onlyReturnExisting": false,
|
||||
"contact": [
|
||||
"mailto:cert-admin@example.com",
|
||||
"mailto:admin@example.com"
|
||||
]
|
||||
}),
|
||||
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
|
||||
}
|
||||
*/
|
||||
ACME._registerAccount = function (me, options) {
|
||||
if (me.debug) { console.debug('[acme-v2] accounts.create'); }
|
||||
|
||||
return ACME._getNonce(me).then(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
function agree(tosUrl) {
|
||||
var err;
|
||||
if (me._tos !== tosUrl) {
|
||||
err = new Error("You must agree to the ToS at '" + me._tos + "'");
|
||||
err.code = "E_AGREE_TOS";
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var jwk = me.RSA.exportPublicJwk(options.accountKeypair);
|
||||
var contact;
|
||||
if (options.contact) {
|
||||
contact = options.contact.slice(0);
|
||||
} else if (options.email) {
|
||||
contact = [ 'mailto:' + options.email ];
|
||||
}
|
||||
var body = {
|
||||
termsOfServiceAgreed: tosUrl === me._tos
|
||||
, onlyReturnExisting: false
|
||||
, contact: contact
|
||||
};
|
||||
if (options.externalAccount) {
|
||||
body.externalAccountBinding = me.RSA.signJws(
|
||||
options.externalAccount.secret
|
||||
, undefined
|
||||
, { alg: "HS256"
|
||||
, kid: options.externalAccount.id
|
||||
, url: me._directoryUrls.newAccount
|
||||
}
|
||||
, Buffer.from(JSON.stringify(jwk))
|
||||
);
|
||||
}
|
||||
var payload = JSON.stringify(body);
|
||||
var jws = me.RSA.signJws(
|
||||
options.accountKeypair
|
||||
, undefined
|
||||
, { nonce: me._nonce
|
||||
, alg: 'RS256'
|
||||
, url: me._directoryUrls.newAccount
|
||||
, jwk: jwk
|
||||
}
|
||||
, Buffer.from(payload)
|
||||
);
|
||||
|
||||
delete jws.header;
|
||||
if (me.debug) { console.debug('[acme-v2] accounts.create JSON body:'); }
|
||||
if (me.debug) { console.debug(jws); }
|
||||
me._nonce = null;
|
||||
return me._request({
|
||||
method: 'POST'
|
||||
, url: me._directoryUrls.newAccount
|
||||
, headers: { 'Content-Type': 'application/jose+json' }
|
||||
, json: jws
|
||||
}).then(function (resp) {
|
||||
var account = resp.body;
|
||||
|
||||
if (2 !== Math.floor(resp.statusCode / 100)) {
|
||||
throw new Error('account error: ' + JSON.stringify(body));
|
||||
}
|
||||
|
||||
me._nonce = resp.toJSON().headers['replay-nonce'];
|
||||
var location = resp.toJSON().headers.location;
|
||||
// the account id url
|
||||
me._kid = location;
|
||||
if (me.debug) { console.debug('[DEBUG] new account location:'); }
|
||||
if (me.debug) { console.debug(location); }
|
||||
if (me.debug) { console.debug(resp.toJSON()); }
|
||||
|
||||
/*
|
||||
{
|
||||
contact: ["mailto:jon@example.com"],
|
||||
orders: "https://some-url",
|
||||
status: 'valid'
|
||||
}
|
||||
*/
|
||||
if (!account) { account = { _emptyResponse: true, key: {} }; }
|
||||
// https://git.coolaj86.com/coolaj86/acme-v2.js/issues/8
|
||||
if (!account.key) { account.key = {}; }
|
||||
account.key.kid = me._kid;
|
||||
return account;
|
||||
}).then(resolve, reject);
|
||||
}
|
||||
|
||||
if (me.debug) { console.debug('[acme-v2] agreeToTerms'); }
|
||||
if (1 === options.agreeToTerms.length) {
|
||||
// newer promise API
|
||||
return options.agreeToTerms(me._tos).then(agree, reject);
|
||||
}
|
||||
else if (2 === options.agreeToTerms.length) {
|
||||
// backwards compat cb API
|
||||
return options.agreeToTerms(me._tos, function (err, tosUrl) {
|
||||
if (!err) { agree(tosUrl); return; }
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
reject(new Error('agreeToTerms has incorrect function signature.'
|
||||
+ ' Should be fn(tos) { return Promise<tos>; }'));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
/*
|
||||
POST /acme/new-order HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/jose+json
|
||||
|
||||
{
|
||||
"protected": base64url({
|
||||
"alg": "ES256",
|
||||
"kid": "https://example.com/acme/acct/1",
|
||||
"nonce": "5XJ1L3lEkMG7tR6pA00clA",
|
||||
"url": "https://example.com/acme/new-order"
|
||||
}),
|
||||
"payload": base64url({
|
||||
"identifiers": [{"type:"dns","value":"example.com"}],
|
||||
"notBefore": "2016-01-01T00:00:00Z",
|
||||
"notAfter": "2016-01-08T00:00:00Z"
|
||||
}),
|
||||
"signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
|
||||
}
|
||||
*/
|
||||
ACME._getChallenges = function (me, options, auth) {
|
||||
if (me.debug) { console.debug('\n[DEBUG] getChallenges\n'); }
|
||||
return me._request({ method: 'GET', url: auth, json: true }).then(function (resp) {
|
||||
return resp.body;
|
||||
});
|
||||
};
|
||||
ACME._wait = function wait(ms) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(resolve, (ms || 1100));
|
||||
});
|
||||
};
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
|
||||
ACME._postChallenge = function (me, options, identifier, ch) {
|
||||
var RETRY_INTERVAL = me.retryInterval || 1000;
|
||||
var DEAUTH_INTERVAL = me.deauthWait || 10 * 1000;
|
||||
var MAX_POLL = me.retryPoll || 8;
|
||||
var MAX_PEND = me.retryPending || 4;
|
||||
var count = 0;
|
||||
|
||||
var thumbprint = me.RSA.thumbprint(options.accountKeypair);
|
||||
var keyAuthorization = ch.token + '.' + thumbprint;
|
||||
// keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
|
||||
// /.well-known/acme-challenge/:token
|
||||
var auth = {
|
||||
identifier: identifier
|
||||
, hostname: identifier.value
|
||||
, type: ch.type
|
||||
, token: ch.token
|
||||
, thumbprint: thumbprint
|
||||
, keyAuthorization: keyAuthorization
|
||||
, dnsAuthorization: me.RSA.utils.toWebsafeBase64(
|
||||
require('crypto').createHash('sha256').update(keyAuthorization).digest('base64')
|
||||
)
|
||||
};
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
/*
|
||||
POST /acme/authz/1234 HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/jose+json
|
||||
|
||||
{
|
||||
"protected": base64url({
|
||||
"alg": "ES256",
|
||||
"kid": "https://example.com/acme/acct/1",
|
||||
"nonce": "xWCM9lGbIyCgue8di6ueWQ",
|
||||
"url": "https://example.com/acme/authz/1234"
|
||||
}),
|
||||
"payload": base64url({
|
||||
"status": "deactivated"
|
||||
}),
|
||||
"signature": "srX9Ji7Le9bjszhu...WTFdtujObzMtZcx4"
|
||||
}
|
||||
*/
|
||||
function deactivate() {
|
||||
var jws = me.RSA.signJws(
|
||||
options.accountKeypair
|
||||
, undefined
|
||||
, { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid }
|
||||
, Buffer.from(JSON.stringify({ "status": "deactivated" }))
|
||||
);
|
||||
me._nonce = null;
|
||||
return me._request({
|
||||
method: 'POST'
|
||||
, url: ch.url
|
||||
, headers: { 'Content-Type': 'application/jose+json' }
|
||||
, json: jws
|
||||
}).then(function (resp) {
|
||||
if (me.debug) { console.debug('[acme-v2.js] deactivate:'); }
|
||||
if (me.debug) { console.debug(resp.headers); }
|
||||
if (me.debug) { console.debug(resp.body); }
|
||||
if (me.debug) { console.debug(); }
|
||||
|
||||
me._nonce = resp.toJSON().headers['replay-nonce'];
|
||||
if (me.debug) { console.debug('deactivate challenge: resp.body:'); }
|
||||
if (me.debug) { console.debug(resp.body); }
|
||||
return ACME._wait(DEAUTH_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
function pollStatus() {
|
||||
if (count >= MAX_POLL) {
|
||||
return Promise.reject(new Error("[acme-v2] stuck in bad pending/processing state"));
|
||||
}
|
||||
|
||||
count += 1;
|
||||
|
||||
if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); }
|
||||
return me._request({ method: 'GET', url: ch.url, json: true }).then(function (resp) {
|
||||
|
||||
if ('processing' === resp.body.status) {
|
||||
if (me.debug) { console.debug('poll: again'); }
|
||||
return ACME._wait(RETRY_INTERVAL).then(pollStatus);
|
||||
}
|
||||
|
||||
// This state should never occur
|
||||
if ('pending' === resp.body.status) {
|
||||
if (count >= MAX_PEND) {
|
||||
return ACME._wait(RETRY_INTERVAL).then(deactivate).then(testChallenge);
|
||||
}
|
||||
if (me.debug) { console.debug('poll: again'); }
|
||||
return ACME._wait(RETRY_INTERVAL).then(testChallenge);
|
||||
}
|
||||
|
||||
if ('valid' === resp.body.status) {
|
||||
if (me.debug) { console.debug('poll: valid'); }
|
||||
|
||||
try {
|
||||
if (1 === options.removeChallenge.length) {
|
||||
options.removeChallenge(auth).then(function () {}, function () {});
|
||||
} else if (2 === options.removeChallenge.length) {
|
||||
options.removeChallenge(auth, function (err) { return err; });
|
||||
} else {
|
||||
options.removeChallenge(identifier.value, ch.token, function () {});
|
||||
}
|
||||
} catch(e) {}
|
||||
return resp.body;
|
||||
}
|
||||
|
||||
if (!resp.body.status) {
|
||||
console.error("[acme-v2] (E_STATE_EMPTY) empty challenge state:");
|
||||
}
|
||||
else if ('invalid' === resp.body.status) {
|
||||
console.error("[acme-v2] (E_STATE_INVALID) challenge state: '" + resp.body.status + "'");
|
||||
}
|
||||
else {
|
||||
console.error("[acme-v2] (E_STATE_UKN) challenge state: '" + resp.body.status + "'");
|
||||
}
|
||||
|
||||
return Promise.reject(new Error("[acme-v2] [error] unacceptable challenge state '" + resp.body.status + "'"));
|
||||
});
|
||||
}
|
||||
|
||||
function respondToChallenge() {
|
||||
var jws = me.RSA.signJws(
|
||||
options.accountKeypair
|
||||
, undefined
|
||||
, { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid }
|
||||
, Buffer.from(JSON.stringify({ }))
|
||||
);
|
||||
me._nonce = null;
|
||||
return me._request({
|
||||
method: 'POST'
|
||||
, url: ch.url
|
||||
, headers: { 'Content-Type': 'application/jose+json' }
|
||||
, json: jws
|
||||
}).then(function (resp) {
|
||||
if (me.debug) { console.debug('[acme-v2.js] challenge accepted!'); }
|
||||
if (me.debug) { console.debug(resp.headers); }
|
||||
if (me.debug) { console.debug(resp.body); }
|
||||
if (me.debug) { console.debug(); }
|
||||
|
||||
me._nonce = resp.toJSON().headers['replay-nonce'];
|
||||
if (me.debug) { console.debug('respond to challenge: resp.body:'); }
|
||||
if (me.debug) { console.debug(resp.body); }
|
||||
return ACME._wait(RETRY_INTERVAL).then(pollStatus);
|
||||
});
|
||||
}
|
||||
|
||||
function testChallenge() {
|
||||
// TODO put check dns / http checks here?
|
||||
// http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
|
||||
// dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
|
||||
|
||||
if (me.debug) {console.debug('\n[DEBUG] postChallenge\n'); }
|
||||
//if (me.debug) console.debug('\n[DEBUG] stop to fix things\n'); return;
|
||||
|
||||
return ACME._wait(RETRY_INTERVAL).then(function () {
|
||||
if (!me.skipChallengeTest) {
|
||||
return ACME.challengeTests[ch.type](me, auth);
|
||||
}
|
||||
}).then(respondToChallenge);
|
||||
}
|
||||
|
||||
try {
|
||||
if (1 === options.setChallenge.length) {
|
||||
options.setChallenge(auth).then(testChallenge).then(resolve, reject);
|
||||
} else if (2 === options.setChallenge.length) {
|
||||
options.setChallenge(auth, function (err) {
|
||||
if(err) {
|
||||
reject(err);
|
||||
} else {
|
||||
testChallenge().then(resolve, reject);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var challengeCb = function(err) {
|
||||
if(err) {
|
||||
reject(err);
|
||||
} else {
|
||||
testChallenge().then(resolve, reject);
|
||||
}
|
||||
};
|
||||
Object.keys(auth).forEach(function (key) {
|
||||
challengeCb[key] = auth[key];
|
||||
});
|
||||
options.setChallenge(identifier.value, ch.token, keyAuthorization, challengeCb);
|
||||
}
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
ACME._finalizeOrder = function (me, options, validatedDomains) {
|
||||
if (me.debug) { console.debug('finalizeOrder:'); }
|
||||
var csr = me.RSA.generateCsrWeb64(options.domainKeypair, validatedDomains);
|
||||
var body = { csr: csr };
|
||||
var payload = JSON.stringify(body);
|
||||
|
||||
function pollCert() {
|
||||
var jws = me.RSA.signJws(
|
||||
options.accountKeypair
|
||||
, undefined
|
||||
, { nonce: me._nonce, alg: 'RS256', url: me._finalize, kid: me._kid }
|
||||
, Buffer.from(payload)
|
||||
);
|
||||
|
||||
if (me.debug) { console.debug('finalize:', me._finalize); }
|
||||
me._nonce = null;
|
||||
return me._request({
|
||||
method: 'POST'
|
||||
, url: me._finalize
|
||||
, headers: { 'Content-Type': 'application/jose+json' }
|
||||
, json: jws
|
||||
}).then(function (resp) {
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.3
|
||||
// Possible values are: "pending" => ("invalid" || "ready") => "processing" => "valid"
|
||||
me._nonce = resp.toJSON().headers['replay-nonce'];
|
||||
|
||||
if (me.debug) { console.debug('order finalized: resp.body:'); }
|
||||
if (me.debug) { console.debug(resp.body); }
|
||||
|
||||
if ('valid' === resp.body.status) {
|
||||
me._expires = resp.body.expires;
|
||||
me._certificate = resp.body.certificate;
|
||||
|
||||
return resp.body; // return order
|
||||
}
|
||||
|
||||
if ('processing' === resp.body.status) {
|
||||
return ACME._wait().then(pollCert);
|
||||
}
|
||||
|
||||
if (me.debug) { console.debug("Error: bad status:\n" + JSON.stringify(resp.body, null, 2)); }
|
||||
|
||||
if ('pending' === resp.body.status) {
|
||||
return Promise.reject(new Error(
|
||||
"Did not finalize order: status 'pending'."
|
||||
+ " Best guess: You have not accepted at least one challenge for each domain." + "\n\n"
|
||||
+ JSON.stringify(resp.body, null, 2)
|
||||
));
|
||||
}
|
||||
|
||||
if ('invalid' === resp.body.status) {
|
||||
return Promise.reject(new Error(
|
||||
"Did not finalize order: status 'invalid'."
|
||||
+ " Best guess: One or more of the domain challenges could not be verified"
|
||||
+ " (or the order was canceled)." + "\n\n"
|
||||
+ JSON.stringify(resp.body, null, 2)
|
||||
));
|
||||
}
|
||||
|
||||
if ('ready' === resp.body.status) {
|
||||
return Promise.reject(new Error(
|
||||
"Did not finalize order: status 'ready'."
|
||||
+ " Hmmm... this state shouldn't be possible here. That was the last state."
|
||||
+ " This one should at least be 'processing'." + "\n\n"
|
||||
+ JSON.stringify(resp.body, null, 2) + "\n\n"
|
||||
+ "Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js"
|
||||
));
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(
|
||||
"Didn't finalize order: Unhandled status '" + resp.body.status + "'."
|
||||
+ " This is not one of the known statuses...\n\n"
|
||||
+ JSON.stringify(resp.body, null, 2) + "\n\n"
|
||||
+ "Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js"
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
return pollCert();
|
||||
};
|
||||
ACME._getCertificate = function (me, options) {
|
||||
if (me.debug) { console.debug('[acme-v2] DEBUG get cert 1'); }
|
||||
|
||||
if (!options.challengeTypes) {
|
||||
if (!options.challengeType) {
|
||||
return Promise.reject(new Error("challenge type must be specified"));
|
||||
}
|
||||
options.challengeTypes = [ options.challengeType ];
|
||||
}
|
||||
|
||||
if (!me._kid) {
|
||||
if (options.accountKid) {
|
||||
me._kid = options.accountKid;
|
||||
} else {
|
||||
//return Promise.reject(new Error("must include KeyID"));
|
||||
return ACME._registerAccount(me, options).then(function () {
|
||||
return ACME._getCertificate(me, options);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (me.debug) { console.debug('[acme-v2] certificates.create'); }
|
||||
return ACME._getNonce(me).then(function () {
|
||||
var body = {
|
||||
identifiers: options.domains.map(function (hostname) {
|
||||
return { type: "dns" , value: hostname };
|
||||
})
|
||||
//, "notBefore": "2016-01-01T00:00:00Z"
|
||||
//, "notAfter": "2016-01-08T00:00:00Z"
|
||||
};
|
||||
|
||||
var payload = JSON.stringify(body);
|
||||
var jws = me.RSA.signJws(
|
||||
options.accountKeypair
|
||||
, undefined
|
||||
, { nonce: me._nonce, alg: 'RS256', url: me._directoryUrls.newOrder, kid: me._kid }
|
||||
, Buffer.from(payload)
|
||||
);
|
||||
|
||||
if (me.debug) { console.debug('\n[DEBUG] newOrder\n'); }
|
||||
me._nonce = null;
|
||||
return me._request({
|
||||
method: 'POST'
|
||||
, url: me._directoryUrls.newOrder
|
||||
, headers: { 'Content-Type': 'application/jose+json' }
|
||||
, json: jws
|
||||
}).then(function (resp) {
|
||||
me._nonce = resp.toJSON().headers['replay-nonce'];
|
||||
var location = resp.toJSON().headers.location;
|
||||
var auths;
|
||||
if (me.debug) { console.debug(location); } // the account id url
|
||||
if (me.debug) { console.debug(resp.toJSON()); }
|
||||
me._authorizations = resp.body.authorizations;
|
||||
me._order = location;
|
||||
me._finalize = resp.body.finalize;
|
||||
//if (me.debug) console.debug('[DEBUG] finalize:', me._finalize); return;
|
||||
|
||||
if (!me._authorizations) {
|
||||
console.error("[acme-v2.js] authorizations were not fetched:");
|
||||
console.error(resp.body);
|
||||
return Promise.reject(new Error("authorizations were not fetched"));
|
||||
}
|
||||
if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); }
|
||||
|
||||
//return resp.body;
|
||||
auths = me._authorizations.slice(0);
|
||||
|
||||
function next() {
|
||||
var authUrl = auths.shift();
|
||||
if (!authUrl) { return; }
|
||||
|
||||
return ACME._getChallenges(me, options, authUrl).then(function (results) {
|
||||
// var domain = options.domains[i]; // results.identifier.value
|
||||
var chType = options.challengeTypes.filter(function (chType) {
|
||||
return results.challenges.some(function (ch) {
|
||||
return ch.type === chType;
|
||||
});
|
||||
})[0];
|
||||
|
||||
var challenge = results.challenges.filter(function (ch) {
|
||||
if (chType === ch.type) {
|
||||
return ch;
|
||||
}
|
||||
})[0];
|
||||
|
||||
if (!challenge) {
|
||||
return Promise.reject(new Error("Server didn't offer any challenge we can handle."));
|
||||
}
|
||||
|
||||
return ACME._postChallenge(me, options, results.identifier, challenge);
|
||||
}).then(function () {
|
||||
return next();
|
||||
});
|
||||
}
|
||||
|
||||
return next().then(function () {
|
||||
if (me.debug) { console.debug("[getCertificate] next.then"); }
|
||||
var validatedDomains = body.identifiers.map(function (ident) {
|
||||
return ident.value;
|
||||
});
|
||||
|
||||
return ACME._finalizeOrder(me, options, validatedDomains);
|
||||
}).then(function (order) {
|
||||
if (me.debug) { console.debug('acme-v2: order was finalized'); }
|
||||
return me._request({ method: 'GET', url: me._certificate, json: true }).then(function (resp) {
|
||||
if (me.debug) { console.debug('acme-v2: csr submitted and cert received:'); }
|
||||
// https://github.com/certbot/certbot/issues/5721
|
||||
var certsarr = ACME.splitPemChain(ACME.formatPemChain((resp.body||'')));
|
||||
// cert, chain, fullchain, privkey, /*TODO, subject, altnames, issuedAt, expiresAt */
|
||||
var certs = {
|
||||
expires: order.expires
|
||||
, identifiers: order.identifiers
|
||||
//, authorizations: order.authorizations
|
||||
, cert: certsarr.shift()
|
||||
//, privkey: privkeyPem
|
||||
, chain: certsarr.join('\n')
|
||||
};
|
||||
if (me.debug) { console.debug(certs); }
|
||||
return certs;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ACME.create = function create(me) {
|
||||
if (!me) { me = {}; }
|
||||
// me.debug = true;
|
||||
me.challengePrefixes = ACME.challengePrefixes;
|
||||
me.RSA = me.RSA || require('rsa-compat').RSA;
|
||||
me.request = me.request || require('@coolaj86/urequest');
|
||||
me._dig = function (query) {
|
||||
// TODO use digd.js
|
||||
return new Promise(function (resolve, reject) {
|
||||
var dns = require('dns');
|
||||
dns.resolveTxt(query.name, function (err, records) {
|
||||
if (err) { reject(err); return; }
|
||||
|
||||
resolve({
|
||||
answer: records.map(function (rr) {
|
||||
return {
|
||||
data: rr
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
me.promisify = me.promisify || require('util').promisify /*node v8+*/ || require('bluebird').promisify /*node v6*/;
|
||||
|
||||
|
||||
if ('function' !== typeof me.getUserAgentString) {
|
||||
me.pkg = me.pkg || require('./package.json');
|
||||
me.os = me.os || require('os');
|
||||
me.process = me.process || require('process');
|
||||
me.userAgent = ACME._getUserAgentString(me);
|
||||
}
|
||||
|
||||
function getRequest(opts) {
|
||||
if (!opts) { opts = {}; }
|
||||
|
||||
return me.request.defaults({
|
||||
headers: {
|
||||
'User-Agent': opts.userAgent || me.userAgent || me.getUserAgentString(me)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ('function' !== typeof me._request) {
|
||||
me._request = me.promisify(getRequest({}));
|
||||
}
|
||||
|
||||
me.init = function (_directoryUrl) {
|
||||
me.directoryUrl = me.directoryUrl || _directoryUrl;
|
||||
return ACME._directory(me).then(function (resp) {
|
||||
me._directoryUrls = resp.body;
|
||||
me._tos = me._directoryUrls.meta.termsOfService;
|
||||
return me._directoryUrls;
|
||||
});
|
||||
};
|
||||
me.accounts = {
|
||||
create: function (options) {
|
||||
return ACME._registerAccount(me, options);
|
||||
}
|
||||
};
|
||||
me.certificates = {
|
||||
create: function (options) {
|
||||
return ACME._getCertificate(me, options);
|
||||
}
|
||||
};
|
||||
return me;
|
||||
};
|
102
express-server/node_modules/acme-v2/package.json
generated
vendored
Normal file
102
express-server/node_modules/acme-v2/package.json
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"acme-v2@^1.2.0",
|
||||
"/nodeapps/https-test/greenlock-express.js/node_modules/greenlock"
|
||||
]
|
||||
],
|
||||
"_from": "acme-v2@>=1.2.0 <2.0.0",
|
||||
"_id": "acme-v2@1.2.1",
|
||||
"_inCache": true,
|
||||
"_installable": true,
|
||||
"_location": "/acme-v2",
|
||||
"_nodeVersion": "10.6.0",
|
||||
"_npmOperationalInternal": {
|
||||
"host": "s3://npm-registry-packages",
|
||||
"tmp": "tmp/acme-v2_1.2.1_1534465950183_0.44524737605461473"
|
||||
},
|
||||
"_npmUser": {
|
||||
"email": "coolaj86@gmail.com",
|
||||
"name": "coolaj86"
|
||||
},
|
||||
"_npmVersion": "6.1.0",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"name": "acme-v2",
|
||||
"raw": "acme-v2@^1.2.0",
|
||||
"rawSpec": "^1.2.0",
|
||||
"scope": null,
|
||||
"spec": ">=1.2.0 <2.0.0",
|
||||
"type": "range"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/acme",
|
||||
"/greenlock"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/acme-v2/-/acme-v2-1.2.1.tgz",
|
||||
"_shasum": "15ef5063b45172e900cfa6f05d608bde590860cc",
|
||||
"_shrinkwrap": null,
|
||||
"_spec": "acme-v2@^1.2.0",
|
||||
"_where": "/nodeapps/https-test/greenlock-express.js/node_modules/greenlock",
|
||||
"author": {
|
||||
"email": "coolaj86@gmail.com",
|
||||
"name": "AJ ONeal",
|
||||
"url": "https://coolaj86.com/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coolaj86/urequest": "^1.3.6",
|
||||
"rsa-compat": "^1.5.1"
|
||||
},
|
||||
"description": "Free SSL. A framework for building Let's Encrypt v2 clients, and other ACME v2 (draft 11) clients. Successor to le-acme-core.js",
|
||||
"devDependencies": {},
|
||||
"directories": {},
|
||||
"dist": {
|
||||
"fileCount": 14,
|
||||
"integrity": "sha512-7FRl/vgZpcm7VCOiiAU6ntkclHkkEdCk1uNAkuEA0sZ8R0YX3pBjh066y/QqzEAfmDbbiYr+DYlVhZoHTbmXEQ==",
|
||||
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbdhefCRA9TVsSAnZWagAA/XcQAKMNjyd4wCZzld6wlDfC\nr+Mo1CfUugIko2fW6MZ7TGhxPIiVhedC7v/jUxwsZV3oYDdrttTbflkxxoUB\n6ivIdPaTXp/9JXfg0u+2cWrCZufy4d5PQtDDdfrp0GugsUHz/+wrrC2jpCjM\nPRCzGYbwJ7NvEnY5AYU28XI9WL1voCxxxLSjYkPqDjBtGrvcUrghJUsfeqOw\n0gOd3LhSdFunnIV44PTHV4GknAkTUffbpigcfGAtiXfigJ/3xbrjV0k5dpBb\n55q6l+i6Vdvjb8xhYkUO+V7zE/3zZWAaVKHLI+oeVnX/1OrA7tLU05GYj03c\nFaEm57/uS3OQ4atHAy8X6vjXOjuGOoCXpmmNUKZ2whiUHDvsrzEVXhgFAGxb\n9iWghhgwCTn7zPQQKIJdviNEbQXmY+phbG+5mmg2+Z/EHceCi+4t4m/sEoIe\nAzBUTNouU8mMxfWFFDcT8c02LyFanHHsQGC+mtICORZxU8GlCwz7LTU5YLl3\nfkswVaLIYr2Qvlswyf/sJCWCccHgX2j6qlbdNhdV1LBvS1jYmgN83OkmcGTD\nmVyM+lDYqmBkycw29NEAkdZ3LvHsb1t4GOnqDboTOFZll2TSSiaOaLf3yveq\n6beSy/HNuCYzmTxXTeaGPnc1ZwY7iTYZcjTLitzNvKyCwO3Vjiyrtc9Onqx5\n/Yyp\r\n=dHr8\r\n-----END PGP SIGNATURE-----\r\n",
|
||||
"shasum": "15ef5063b45172e900cfa6f05d608bde590860cc",
|
||||
"tarball": "https://registry.npmjs.org/acme-v2/-/acme-v2-1.2.1.tgz",
|
||||
"unpackedSize": 50763
|
||||
},
|
||||
"gitHead": "ca15b8faf0a11b15d9973a9786c652c117d2547d",
|
||||
"homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js",
|
||||
"keywords": [
|
||||
"ACME",
|
||||
"Let's Encrypt",
|
||||
"automated https",
|
||||
"draft-11",
|
||||
"draft-12",
|
||||
"free ssl",
|
||||
"letsencrypt",
|
||||
"tls",
|
||||
"v02",
|
||||
"v2"
|
||||
],
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"main": "node.js",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "coolaj86",
|
||||
"email": "coolaj86@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "ppl",
|
||||
"email": "npm@ppl.family"
|
||||
},
|
||||
{
|
||||
"name": "thejshaver",
|
||||
"email": "john@jshaver.net"
|
||||
}
|
||||
],
|
||||
"name": "acme-v2",
|
||||
"optionalDependencies": {},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://gitea@git.coolaj86.com:22042/coolaj86/acme-v2.js.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "1.2.1"
|
||||
}
|
79
express-server/node_modules/acme-v2/tests/cb.js
generated
vendored
Normal file
79
express-server/node_modules/acme-v2/tests/cb.js
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
module.exports.run = function run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair) {
|
||||
// [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01'
|
||||
var acme2 = require('../').ACME.create({ RSA: RSA });
|
||||
acme2.init(directoryUrl).then(function () {
|
||||
var options = {
|
||||
agreeToTerms: function (tosUrl, agree) {
|
||||
agree(null, tosUrl);
|
||||
}
|
||||
, setChallenge: function (opts, cb) {
|
||||
var pathname;
|
||||
|
||||
console.log("");
|
||||
console.log('identifier:');
|
||||
console.log(opts.identifier);
|
||||
console.log('hostname:');
|
||||
console.log(opts.hostname);
|
||||
console.log('type:');
|
||||
console.log(opts.type);
|
||||
console.log('token:');
|
||||
console.log(opts.token);
|
||||
console.log('thumbprint:');
|
||||
console.log(opts.thumbprint);
|
||||
console.log('keyAuthorization:');
|
||||
console.log(opts.keyAuthorization);
|
||||
console.log('dnsAuthorization:');
|
||||
console.log(opts.dnsAuthorization);
|
||||
console.log("");
|
||||
|
||||
if ('http-01' === opts.type) {
|
||||
pathname = opts.hostname + acme2.challengePrefixes['http-01'] + "/" + opts.token;
|
||||
console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'");
|
||||
console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'");
|
||||
} else if ('dns-01' === opts.type) {
|
||||
pathname = acme2.challengePrefixes['dns-01'] + "." + opts.hostname.replace(/^\*\./, '');
|
||||
console.log("Put the string '" + opts.dnsAuthorization + "' into the TXT record '" + pathname + "'");
|
||||
console.log("ddig TXT " + pathname + " '" + opts.dnsAuthorization + "'");
|
||||
} else {
|
||||
cb(new Error("[acme-v2] unrecognized challenge type"));
|
||||
return;
|
||||
}
|
||||
console.log("\nThen hit the 'any' key to continue...");
|
||||
|
||||
function onAny() {
|
||||
console.log("'any' key was hit");
|
||||
process.stdin.pause();
|
||||
process.stdin.removeListener('data', onAny);
|
||||
process.stdin.setRawMode(false);
|
||||
cb();
|
||||
}
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
process.stdin.on('data', onAny);
|
||||
}
|
||||
, removeChallenge: function (opts, cb) {
|
||||
// hostname, key
|
||||
console.log('[acme-v2] remove challenge', opts.hostname, opts.keyAuthorization);
|
||||
setTimeout(cb, 1 * 1000);
|
||||
}
|
||||
, challengeType: chType
|
||||
, email: email
|
||||
, accountKeypair: accountKeypair
|
||||
, domainKeypair: domainKeypair
|
||||
, domains: web
|
||||
};
|
||||
|
||||
acme2.accounts.create(options).then(function (account) {
|
||||
console.log('[acme-v2] account:');
|
||||
console.log(account);
|
||||
|
||||
acme2.certificates.create(options).then(function (fullchainPem) {
|
||||
console.log('[acme-v2] fullchain.pem:');
|
||||
console.log(fullchainPem);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
68
express-server/node_modules/acme-v2/tests/compat.js
generated
vendored
Normal file
68
express-server/node_modules/acme-v2/tests/compat.js
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
module.exports.run = function (directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair) {
|
||||
console.log('[DEBUG] run', web, chType, email);
|
||||
|
||||
var acme2 = require('../compat.js').ACME.create({ RSA: RSA });
|
||||
acme2.getAcmeUrls(acme2.stagingServerUrl, function (err/*, directoryUrls*/) {
|
||||
if (err) { console.log('err 1'); throw err; }
|
||||
|
||||
var options = {
|
||||
agreeToTerms: function (tosUrl, agree) {
|
||||
agree(null, tosUrl);
|
||||
}
|
||||
, setChallenge: function (hostname, token, val, cb) {
|
||||
var pathname;
|
||||
|
||||
if ('http-01' === cb.type) {
|
||||
pathname = hostname + acme2.acmeChallengePrefix + token;
|
||||
console.log("Put the string '" + val /*keyAuthorization*/ + "' into a file at '" + pathname + "'");
|
||||
console.log("echo '" + val /*keyAuthorization*/ + "' > '" + pathname + "'");
|
||||
console.log("\nThen hit the 'any' key to continue...");
|
||||
} else if ('dns-01' === cb.type) {
|
||||
// forwards-backwards compat
|
||||
pathname = acme2.challengePrefixes['dns-01'] + "." + hostname.replace(/^\*\./, '');
|
||||
console.log("Put the string '" + cb.dnsAuthorization + "' into the TXT record '" + pathname + "'");
|
||||
console.log("dig TXT " + pathname + " '" + cb.dnsAuthorization + "'");
|
||||
console.log("\nThen hit the 'any' key to continue...");
|
||||
} else {
|
||||
cb(new Error("[acme-v2] unrecognized challenge type: " + cb.type));
|
||||
return;
|
||||
}
|
||||
|
||||
function onAny() {
|
||||
console.log("'any' key was hit");
|
||||
process.stdin.pause();
|
||||
process.stdin.removeListener('data', onAny);
|
||||
process.stdin.setRawMode(false);
|
||||
cb();
|
||||
}
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
process.stdin.on('data', onAny);
|
||||
}
|
||||
, removeChallenge: function (hostname, key, cb) {
|
||||
console.log('[DEBUG] remove challenge', hostname, key);
|
||||
setTimeout(cb, 1 * 1000);
|
||||
}
|
||||
, challengeType: chType
|
||||
, email: email
|
||||
, accountKeypair: accountKeypair
|
||||
, domainKeypair: domainKeypair
|
||||
, domains: web
|
||||
};
|
||||
|
||||
acme2.registerNewAccount(options, function (err, account) {
|
||||
if (err) { console.log('err 2'); throw err; }
|
||||
if (options.debug) console.debug('account:');
|
||||
if (options.debug) console.log(account);
|
||||
|
||||
acme2.getCertificate(options, function (err, fullchainPem) {
|
||||
if (err) { console.log('err 3'); throw err; }
|
||||
console.log('[acme-v2] A fullchain.pem:');
|
||||
console.log(fullchainPem);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
77
express-server/node_modules/acme-v2/tests/fullchain-formats.js
generated
vendored
Normal file
77
express-server/node_modules/acme-v2/tests/fullchain-formats.js
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
-----BEGIN CERTIFICATE-----LF
|
||||
xxxLF
|
||||
yyyLF
|
||||
-----END CERTIFICATE-----LF
|
||||
LF
|
||||
-----BEGIN CERTIFICATE-----LF
|
||||
xxxLF
|
||||
yyyLF
|
||||
-----END CERTIFICATE-----LF
|
||||
|
||||
Rules
|
||||
* Only Unix LF (\n) Line endings
|
||||
* Each PEM's lines are separated with \n
|
||||
* Each PEM ends with \n
|
||||
* Each PEM is separated with a \n (just like commas separating an array)
|
||||
*/
|
||||
|
||||
// https://github.com/certbot/certbot/issues/5721#issuecomment-402362709
|
||||
var expected = "----\nxxxx\nyyyy\n----\n\n----\nxxxx\nyyyy\n----\n";
|
||||
var tests = [
|
||||
"----\r\nxxxx\r\nyyyy\r\n----\r\n\r\n----\r\nxxxx\r\nyyyy\r\n----\r\n"
|
||||
, "----\r\nxxxx\r\nyyyy\r\n----\r\n----\r\nxxxx\r\nyyyy\r\n----\r\n"
|
||||
, "----\nxxxx\nyyyy\n----\n\n----\r\nxxxx\r\nyyyy\r\n----"
|
||||
, "----\nxxxx\nyyyy\n----\n----\r\nxxxx\r\nyyyy\r\n----"
|
||||
, "----\nxxxx\nyyyy\n----\n----\nxxxx\nyyyy\n----"
|
||||
, "----\nxxxx\nyyyy\n----\n----\nxxxx\nyyyy\n----\n"
|
||||
, "----\nxxxx\nyyyy\n----\n\n----\nxxxx\nyyyy\n----\n"
|
||||
, "----\nxxxx\nyyyy\n----\r\n----\nxxxx\ryyyy\n----\n"
|
||||
];
|
||||
|
||||
function formatPemChain(str) {
|
||||
return str.trim().replace(/[\r\n]+/g, '\n').replace(/\-\n\-/g, '-\n\n-') + '\n';
|
||||
}
|
||||
function splitPemChain(str) {
|
||||
return str.trim().split(/[\r\n]{2,}/g).map(function (str) {
|
||||
return str + '\n';
|
||||
});
|
||||
}
|
||||
|
||||
tests.forEach(function (str) {
|
||||
var actual = formatPemChain(str);
|
||||
if (expected !== actual) {
|
||||
console.error('input: ', JSON.stringify(str));
|
||||
console.error('expected:', JSON.stringify(expected));
|
||||
console.error('actual: ', JSON.stringify(actual));
|
||||
throw new Error("did not pass");
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
"----\nxxxx\nyyyy\n----\n"
|
||||
!==
|
||||
formatPemChain("\n\n----\r\nxxxx\r\nyyyy\r\n----\n\n")
|
||||
) {
|
||||
throw new Error("Not proper for single cert in chain");
|
||||
}
|
||||
|
||||
if (
|
||||
"--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n"
|
||||
!==
|
||||
formatPemChain("\n\n\n--B--\nxxxx\nyyyy\n--E--\n\n\n\n--B--\nxxxx\nyyyy\n--E--\n\n\n--B--\nxxxx\nyyyy\n--E--\n\n\n")
|
||||
) {
|
||||
throw new Error("Not proper for three certs in chain");
|
||||
}
|
||||
|
||||
splitPemChain(
|
||||
"--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n"
|
||||
).forEach(function (str) {
|
||||
if ("--B--\nxxxx\nyyyy\n--E--\n" !== str) {
|
||||
throw new Error("bad thingy");
|
||||
}
|
||||
});
|
||||
|
||||
console.info('PASS');
|
85
express-server/node_modules/acme-v2/tests/promise.js
generated
vendored
Normal file
85
express-server/node_modules/acme-v2/tests/promise.js
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
'use strict';
|
||||
|
||||
/* global Promise */
|
||||
module.exports.run = function run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair) {
|
||||
var acme2 = require('../').ACME.create({ RSA: RSA });
|
||||
// [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01'
|
||||
acme2.init(directoryUrl).then(function () {
|
||||
var options = {
|
||||
agreeToTerms: function (tosUrl) {
|
||||
return Promise.resolve(tosUrl);
|
||||
}
|
||||
, setChallenge: function (opts) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var pathname;
|
||||
|
||||
console.log("");
|
||||
console.log('identifier:');
|
||||
console.log(opts.identifier);
|
||||
console.log('hostname:');
|
||||
console.log(opts.hostname);
|
||||
console.log('type:');
|
||||
console.log(opts.type);
|
||||
console.log('token:');
|
||||
console.log(opts.token);
|
||||
console.log('thumbprint:');
|
||||
console.log(opts.thumbprint);
|
||||
console.log('keyAuthorization:');
|
||||
console.log(opts.keyAuthorization);
|
||||
console.log('dnsAuthorization:');
|
||||
console.log(opts.dnsAuthorization);
|
||||
console.log("");
|
||||
|
||||
if ('http-01' === opts.type) {
|
||||
pathname = opts.hostname + acme2.challengePrefixes['http-01'] + "/" + opts.token;
|
||||
console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'");
|
||||
console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'");
|
||||
} else if ('dns-01' === opts.type) {
|
||||
pathname = acme2.challengePrefixes['dns-01'] + "." + opts.hostname.replace(/^\*\./, '');
|
||||
console.log("Put the string '" + opts.dnsAuthorization + "' into the TXT record '" + pathname + "'");
|
||||
console.log("dig TXT " + pathname + " '" + opts.dnsAuthorization + "'");
|
||||
} else {
|
||||
reject(new Error("[acme-v2] unrecognized challenge type"));
|
||||
return;
|
||||
}
|
||||
console.log("\nThen hit the 'any' key to continue...");
|
||||
|
||||
function onAny() {
|
||||
console.log("'any' key was hit");
|
||||
process.stdin.pause();
|
||||
process.stdin.removeListener('data', onAny);
|
||||
process.stdin.setRawMode(false);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
process.stdin.on('data', onAny);
|
||||
});
|
||||
}
|
||||
, removeChallenge: function (opts) {
|
||||
console.log('[acme-v2] remove challenge', opts.hostname, opts.keyAuthorization);
|
||||
return new Promise(function (resolve) {
|
||||
// hostname, key
|
||||
setTimeout(resolve, 1 * 1000);
|
||||
});
|
||||
}
|
||||
, challengeType: chType
|
||||
, email: email
|
||||
, accountKeypair: accountKeypair
|
||||
, domainKeypair: domainKeypair
|
||||
, domains: web
|
||||
};
|
||||
|
||||
acme2.accounts.create(options).then(function (account) {
|
||||
console.log('[acme-v2] account:');
|
||||
console.log(account);
|
||||
|
||||
acme2.certificates.create(options).then(function (fullchainPem) {
|
||||
console.log('[acme-v2] fullchain.pem:');
|
||||
console.log(fullchainPem);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user