express server läuft jetzt mit https
This commit is contained in:
Lukas Nowy
2018-12-16 19:08:08 +01:00
parent 5589b0df3f
commit fd947bd852
475 changed files with 91128 additions and 0 deletions

18
express-server/node_modules/acme-v2/.jshintrc generated vendored Normal file
View 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
View 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
View 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
View 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
View 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();

View 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);
});
}

View 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());
});

View 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
View 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
View 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
View 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
View 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);
});
});
});
};

View 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
View 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);
});
});
});
};