add .prettierrc, and make prettier

This commit is contained in:
AJ ONeal 2019-06-13 01:55:25 -06:00
parent 17a1535dcc
commit dfbee8aa79
14 changed files with 1772 additions and 1215 deletions

8
.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"printWidth": 80,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": true
}

118
README.md
View File

@ -8,21 +8,21 @@
# [acme-v2.js](https://git.coolaj86.com/coolaj86/acme-v2.js) # [acme-v2.js](https://git.coolaj86.com/coolaj86/acme-v2.js)
A lightweight, **Low Dependency*** framework for building A lightweight, **Low Dependency**\* framework for building
Let's Encrypt v2 (ACME draft 12) clients, successor to `le-acme-core.js`. Let's Encrypt v2 (ACME draft 12) clients, successor to `le-acme-core.js`.
Built [by request](https://git.coolaj86.com/coolaj86/greenlock.js/issues/5#issuecomment-8). Built [by request](https://git.coolaj86.com/coolaj86/greenlock.js/issues/5#issuecomment-8).
&#42; <small>although `node-forge` and `ursa` are included as `optionalDependencies` \* <small>although `node-forge` and `ursa` are included as `optionalDependencies`
for backwards compatibility with older versions of node, there are no other for backwards compatibility with older versions of node, there are no other
dependencies except those that I wrote for this (and related) projects.</small> dependencies except those that I wrote for this (and related) projects.</small>
## Looking for Quick 'n' Easy&trade;? ## Looking for Quick 'n' Easy&trade;?
If you're looking to *build a webserver*, try [greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js). If you're looking to _build a webserver_, try [greenlock.js](https://git.coolaj86.com/coolaj86/greenlock.js).
If you're looking for an *ACME-enabled webserver*, try [goldilocks.js](https://git.coolaj86.com/coolaj86/goldilocks.js). If you're looking for an _ACME-enabled webserver_, try [goldilocks.js](https://git.coolaj86.com/coolaj86/goldilocks.js).
* [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) - [goldilocks.js](https://git.coolaj86.com/coolaj86/goldilocks.js)
## How to build ACME clients ## How to build ACME clients
@ -77,20 +77,20 @@ https://acme-staging-v02.api.letsencrypt.org/directory
## Two API versions, Two Implementations ## Two API versions, Two Implementations
This library (acme-v2.js) supports ACME [*draft 11*](https://tools.ietf.org/html/draft-ietf-acme-acme-11), 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). otherwise known as Let's Encrypt v2 (or v02).
* ACME draft 11 - ACME draft 11
* Let's Encrypt v2 - Let's Encrypt v2
* Let's Encrypt v02 - Let's Encrypt v02
The predecessor (le-acme-core) supports Let's Encrypt v1 (or v01), which was a 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) [hodge-podge of various drafts](https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md)
of the ACME spec early on. of the ACME spec early on.
* ACME early draft - ACME early draft
* Let's Encrypt v1 - Let's Encrypt v1
* Let's Encrypt v01 - Let's Encrypt v01
This library maintains compatibility with le-acme-core so that it can be used as a **drop-in replacement** 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**, and requires **no changes to existing code**,
@ -102,7 +102,7 @@ Status: Stable, Locked, Bugfix-only
See Full Documentation at <https://git.coolaj86.com/coolaj86/le-acme-core.js> See Full Documentation at <https://git.coolaj86.com/coolaj86/le-acme-core.js>
``` ```js
var RSA = require('rsa-compat').RSA; var RSA = require('rsa-compat').RSA;
var acme = require('acme-v2/compat.js').ACME.create({ RSA: RSA }); var acme = require('acme-v2/compat.js').ACME.create({ RSA: RSA });
@ -118,7 +118,7 @@ Status: Almost stable, but **not semver locked**
This API is a simple evolution of le-acme-core, This API is a simple evolution of le-acme-core,
but tries to provide a better mapping to the new draft 11 APIs. but tries to provide a better mapping to the new draft 11 APIs.
``` ```js
// Create Instance (Dependency Injection) // Create Instance (Dependency Injection)
var ACME = require('acme-v2').ACME.create({ var ACME = require('acme-v2').ACME.create({
RSA: require('rsa-compat').RSA RSA: require('rsa-compat').RSA
@ -187,54 +187,54 @@ Helpers & Stuff
```javascript ```javascript
// Constants // Constants
ACME.challengePrefixes['http-01'] // '/.well-known/acme-challenge' ACME.challengePrefixes['http-01']; // '/.well-known/acme-challenge'
ACME.challengePrefixes['dns-01'] // '_acme-challenge' ACME.challengePrefixes['dns-01']; // '_acme-challenge'
``` ```
# Changelog # Changelog
* v1.5 - v1.5
* perform full test challenge first (even before nonce) - perform full test challenge first (even before nonce)
* v1.3 - v1.3
* Use node RSA keygen by default - Use node RSA keygen by default
* No non-optional external deps! - No non-optional external deps!
* v1.2 - v1.2
* fix some API out-of-specness - fix some API out-of-specness
* doc some magic numbers (status) - doc some magic numbers (status)
* updated deps - updated deps
* v1.1.0 - v1.1.0
* reduce dependencies (use lightweight @coolaj86/request instead of request) - reduce dependencies (use lightweight @coolaj86/request instead of request)
* v1.0.5 - cleanup logging - v1.0.5 - cleanup logging
* v1.0.4 - v6- compat use `promisify` from node's util or bluebird - v1.0.4 - v6- compat use `promisify` from node's util or bluebird
* v1.0.3 - documentation cleanup - v1.0.3 - documentation cleanup
* v1.0.2 - v1.0.2
* use `options.contact` to provide raw contact array - use `options.contact` to provide raw contact array
* made `options.email` optional - made `options.email` optional
* file cleanup - file cleanup
* v1.0.1 - v1.0.1
* Compat API is ready for use - Compat API is ready for use
* Eliminate debug logging - Eliminate debug logging
* Apr 10, 2018 - tested backwards-compatibility using greenlock.js - Apr 10, 2018 - tested backwards-compatibility using greenlock.js
* Apr 5, 2018 - export http and dns challenge tests - Apr 5, 2018 - export http and dns challenge tests
* Apr 5, 2018 - test http and dns challenges (success and failure) - Apr 5, 2018 - test http and dns challenges (success and failure)
* Apr 5, 2018 - test subdomains and its wildcard - Apr 5, 2018 - test subdomains and its wildcard
* Apr 5, 2018 - test two subdomains - Apr 5, 2018 - test two subdomains
* Apr 5, 2018 - test wildcard - Apr 5, 2018 - test wildcard
* Apr 5, 2018 - completely match api for acme v1 (le-acme-core.js) - 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 - _mostly_ matches le-acme-core.js API
* Mar 21, 2018 - can now accept values (not hard coded) - Mar 21, 2018 - can now accept values (not hard coded)
* Mar 20, 2018 - SUCCESS - got a test certificate (hard-coded) - Mar 20, 2018 - SUCCESS - got a test certificate (hard-coded)
* Mar 20, 2018 - download certificate - Mar 20, 2018 - download certificate
* Mar 20, 2018 - poll for status - Mar 20, 2018 - poll for status
* Mar 20, 2018 - finalize order (submit csr) - Mar 20, 2018 - finalize order (submit csr)
* Mar 20, 2018 - generate domain keypair - Mar 20, 2018 - generate domain keypair
* Mar 20, 2018 - respond to challenges - Mar 20, 2018 - respond to challenges
* Mar 16, 2018 - get challenges - Mar 16, 2018 - get challenges
* Mar 16, 2018 - new order - Mar 16, 2018 - new order
* Mar 15, 2018 - create account - Mar 15, 2018 - create account
* Mar 15, 2018 - generate account keypair - Mar 15, 2018 - generate account keypair
* Mar 15, 2018 - get nonce - Mar 15, 2018 - get nonce
* Mar 15, 2018 - get directory - Mar 15, 2018 - get directory
# Legal # Legal

View File

@ -8,47 +8,53 @@
var ACME2 = require('./').ACME; var ACME2 = require('./').ACME;
function resolveFn(cb) { function resolveFn(cb) {
return function (val) { return function(val) {
// nextTick to get out of Promise chain // nextTick to get out of Promise chain
process.nextTick(function () { cb(null, val); }); process.nextTick(function() {
cb(null, val);
});
}; };
} }
function rejectFn(cb) { function rejectFn(cb) {
return function (err) { return function(err) {
console.error('[acme-v2] handled(?) rejection as errback:'); console.error('[acme-v2] handled(?) rejection as errback:');
console.error(err.stack); console.error(err.stack);
// nextTick to get out of Promise chain // nextTick to get out of Promise chain
process.nextTick(function () { cb(err); }); process.nextTick(function() {
cb(err);
});
// do not resolve promise further // do not resolve promise further
return new Promise(function () {}); return new Promise(function() {});
}; };
} }
function create(deps) { function create(deps) {
deps.LeCore = {}; deps.LeCore = {};
var acme2 = ACME2.create(deps); var acme2 = ACME2.create(deps);
acme2.registerNewAccount = function (options, cb) { acme2.registerNewAccount = function(options, cb) {
acme2.accounts.create(options).then(resolveFn(cb), rejectFn(cb)); acme2.accounts.create(options).then(resolveFn(cb), rejectFn(cb));
}; };
acme2.getCertificate = function (options, cb) { acme2.getCertificate = function(options, cb) {
options.agreeToTerms = options.agreeToTerms || function (tos) { options.agreeToTerms =
options.agreeToTerms ||
function(tos) {
return Promise.resolve(tos); return Promise.resolve(tos);
}; };
acme2.certificates.create(options).then(function (certs) { acme2.certificates.create(options).then(function(certs) {
var privkeyPem = acme2.RSA.exportPrivatePem(options.domainKeypair); var privkeyPem = acme2.RSA.exportPrivatePem(options.domainKeypair);
certs.privkey = privkeyPem; certs.privkey = privkeyPem;
resolveFn(cb)(certs); resolveFn(cb)(certs);
}, rejectFn(cb)); }, rejectFn(cb));
}; };
acme2.getAcmeUrls = function (options, cb) { acme2.getAcmeUrls = function(options, cb) {
acme2.init(options).then(resolveFn(cb), rejectFn(cb)); acme2.init(options).then(resolveFn(cb), rejectFn(cb));
}; };
acme2.getOptions = function () { acme2.getOptions = function() {
var defs = {}; var defs = {};
Object.keys(module.exports.defaults).forEach(function (key) { Object.keys(module.exports.defaults).forEach(function(key) {
defs[key] = defs[deps] || module.exports.defaults[key]; defs[key] = defs[deps] || module.exports.defaults[key];
}); });
@ -60,22 +66,29 @@ function create(deps) {
return acme2; return acme2;
} }
module.exports.ACME = { }; module.exports.ACME = {};
module.exports.defaults = { module.exports.defaults = {
productionServerUrl: 'https://acme-v02.api.letsencrypt.org/directory' productionServerUrl: 'https://acme-v02.api.letsencrypt.org/directory',
, stagingServerUrl: 'https://acme-staging-v02.api.letsencrypt.org/directory' stagingServerUrl: 'https://acme-staging-v02.api.letsencrypt.org/directory',
, knownEndpoints: [ 'keyChange', 'meta', 'newAccount', 'newNonce', 'newOrder', 'revokeCert' ] knownEndpoints: [
, challengeTypes: [ 'http-01', 'dns-01' ] 'keyChange',
, challengeType: 'http-01' 'meta',
//, keyType: 'rsa' // ecdsa 'newAccount',
//, keySize: 2048 // 256 'newNonce',
, rsaKeySize: 2048 // 256 'newOrder',
, acmeChallengePrefix: '/.well-known/acme-challenge/' '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) { Object.keys(module.exports.defaults).forEach(function(key) {
module.exports.ACME[key] = module.exports.defaults[key]; module.exports.ACME[key] = module.exports.defaults[key];
}); });
Object.keys(ACME2).forEach(function (key) { Object.keys(ACME2).forEach(function(key) {
module.exports.ACME[key] = ACME2[key]; module.exports.ACME[key] = ACME2[key];
}); });
module.exports.ACME.create = create; module.exports.ACME.create = create;

View File

@ -14,32 +14,49 @@ var rl = readline.createInterface({
require('./genkeypair.js'); require('./genkeypair.js');
function getWeb() { function getWeb() {
rl.question('What web address(es) would you like to get certificates for? (ex: example.com,*.example.com) ', function (web) { rl.question(
web = (web||'').trim().split(/,/g); 'What web address(es) would you like to get certificates for? (ex: example.com,*.example.com) ',
if (!web[0]) { getWeb(); return; } function(web) {
web = (web || '').trim().split(/,/g);
if (!web[0]) {
getWeb();
return;
}
if (web.some(function (w) { return '*' === w[0]; })) { if (
web.some(function(w) {
return '*' === w[0];
})
) {
console.log('Wildcard domains must use dns-01'); console.log('Wildcard domains must use dns-01');
getEmail(web, 'dns-01'); getEmail(web, 'dns-01');
} else { } else {
getChallengeType(web); getChallengeType(web);
} }
}); }
);
} }
function getChallengeType(web) { function getChallengeType(web) {
rl.question('What challenge will you be testing today? http-01 or dns-01? [http-01] ', function (chType) { rl.question(
chType = (chType||'').trim(); 'What challenge will you be testing today? http-01 or dns-01? [http-01] ',
if (!chType) { chType = 'http-01'; } function(chType) {
chType = (chType || '').trim();
if (!chType) {
chType = 'http-01';
}
getEmail(web, chType); getEmail(web, chType);
}); }
);
} }
function getEmail(web, chType) { function getEmail(web, chType) {
rl.question('What email should we use? (optional) ', function (email) { rl.question('What email should we use? (optional) ', function(email) {
email = (email||'').trim(); email = (email || '').trim();
if (!email) { email = null; } if (!email) {
email = null;
}
getApiStyle(web, chType, email); getApiStyle(web, chType, email);
}); });
@ -47,26 +64,68 @@ function getEmail(web, chType) {
function getApiStyle(web, chType, email) { function getApiStyle(web, chType, email) {
var defaultStyle = 'compat'; var defaultStyle = 'compat';
rl.question('What API style would you like to test? v1-compat or promise? [v1-compat] ', function (apiStyle) { rl.question(
apiStyle = (apiStyle||'').trim(); 'What API style would you like to test? v1-compat or promise? [v1-compat] ',
if (!apiStyle) { apiStyle = 'v1-compat'; } function(apiStyle) {
apiStyle = (apiStyle || '').trim();
if (!apiStyle) {
apiStyle = 'v1-compat';
}
rl.close(); rl.close();
var RSA = require('rsa-compat').RSA; var RSA = require('rsa-compat').RSA;
var accountKeypair = RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/../tests/account.privkey.pem') }); var accountKeypair = RSA.import({
var domainKeypair = RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/../tests/privkey.pem') }); privateKeyPem: require('fs').readFileSync(
var directoryUrl = 'https://acme-staging-v02.api.letsencrypt.org/directory'; __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) { if ('promise' === apiStyle) {
require('../tests/promise.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair); require('../tests/promise.js').run(
directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
);
} else if ('cb' === apiStyle) { } else if ('cb' === apiStyle) {
require('../tests/cb.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair); require('../tests/cb.js').run(
directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
);
} else { } else {
if ('v1-compat' !== apiStyle) { console.warn("Didn't understand '" + apiStyle + "', using 'v1-compat' instead..."); } if ('v1-compat' !== apiStyle) {
require('../tests/compat.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair); console.warn(
"Didn't understand '" + apiStyle + "', using 'v1-compat' instead..."
);
} }
}); require('../tests/compat.js').run(
directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
);
}
}
);
} }
getWeb(); getWeb();

View File

@ -6,9 +6,9 @@ var RSA = require('rsa-compat').RSA;
var fs = require('fs'); var fs = require('fs');
if (!fs.existsSync(__dirname + '/../tests/account.privkey.pem')) { if (!fs.existsSync(__dirname + '/../tests/account.privkey.pem')) {
RSA.generateKeypair(2048, 65537, {}, function (err, keypair) { RSA.generateKeypair(2048, 65537, {}, function(err, keypair) {
console.log(keypair); console.log(keypair);
var privkeyPem = RSA.exportPrivatePem(keypair) var privkeyPem = RSA.exportPrivatePem(keypair);
console.log(privkeyPem); console.log(privkeyPem);
fs.writeFileSync(__dirname + '/../tests/account.privkey.pem', privkeyPem); fs.writeFileSync(__dirname + '/../tests/account.privkey.pem', privkeyPem);
@ -16,9 +16,9 @@ if (!fs.existsSync(__dirname + '/../tests/account.privkey.pem')) {
} }
if (!fs.existsSync(__dirname + '/../tests/privkey.pem')) { if (!fs.existsSync(__dirname + '/../tests/privkey.pem')) {
RSA.generateKeypair(2048, 65537, {}, function (err, keypair) { RSA.generateKeypair(2048, 65537, {}, function(err, keypair) {
console.log(keypair); console.log(keypair);
var privkeyPem = RSA.exportPrivatePem(keypair) var privkeyPem = RSA.exportPrivatePem(keypair);
console.log(privkeyPem); console.log(privkeyPem);
fs.writeFileSync(__dirname + '/../tests/privkey.pem', privkeyPem); fs.writeFileSync(__dirname + '/../tests/privkey.pem', privkeyPem);

View File

@ -6,6 +6,8 @@
var http = require('http'); var http = require('http');
var express = require('express'); var express = require('express');
var server = http.createServer(express.static('../tests')).listen(80, function () { var server = http
.createServer(express.static('../tests'))
.listen(80, function() {
console.log('Listening on', this.address()); console.log('Listening on', this.address());
}); });

View File

@ -5,11 +5,16 @@
'use strict'; 'use strict';
var https = require('https'); var https = require('https');
var server = https.createServer({ var server = https
key: require('fs').readFileSync('../tests/privkey.pem') .createServer(
, cert: require('fs').readFileSync('../tests/fullchain.pem') {
}, function (req, res) { key: require('fs').readFileSync('../tests/privkey.pem'),
res.end("Hello, World!"); cert: require('fs').readFileSync('../tests/fullchain.pem')
}).listen(443, function () { },
function(req, res) {
res.end('Hello, World!');
}
)
.listen(443, function() {
console.log('Listening on', this.address()); console.log('Listening on', this.address());
}); });

1046
node.js

File diff suppressed because it is too large Load Diff

View File

@ -4,18 +4,26 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'; 'use strict';
module.exports.run = function run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair) { module.exports.run = function run(
directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
) {
// [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01' // [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01'
var acme2 = require('../').ACME.create({ RSA: RSA }); var acme2 = require('../').ACME.create({ RSA: RSA });
acme2.init(directoryUrl).then(function () { acme2.init(directoryUrl).then(function() {
var options = { var options = {
agreeToTerms: function (tosUrl, agree) { agreeToTerms: function(tosUrl, agree) {
agree(null, tosUrl); agree(null, tosUrl);
} },
, setChallenge: function (opts, cb) { setChallenge: function(opts, cb) {
var pathname; var pathname;
console.log(""); console.log('');
console.log('identifier:'); console.log('identifier:');
console.log(opts.identifier); console.log(opts.identifier);
console.log('hostname:'); console.log('hostname:');
@ -30,18 +38,41 @@ module.exports.run = function run(directoryUrl, RSA, web, chType, email, account
console.log(opts.keyAuthorization); console.log(opts.keyAuthorization);
console.log('dnsAuthorization:'); console.log('dnsAuthorization:');
console.log(opts.dnsAuthorization); console.log(opts.dnsAuthorization);
console.log(""); console.log('');
if ('http-01' === opts.type) { if ('http-01' === opts.type) {
pathname = opts.hostname + acme2.challengePrefixes['http-01'] + "/" + opts.token; pathname =
console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'"); opts.hostname +
console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'"); 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) { } else if ('dns-01' === opts.type) {
pathname = acme2.challengePrefixes['dns-01'] + "." + opts.hostname.replace(/^\*\./, ''); pathname =
console.log("Put the string '" + opts.dnsAuthorization + "' into the TXT record '" + pathname + "'"); acme2.challengePrefixes['dns-01'] +
console.log("ddig TXT " + pathname + " '" + opts.dnsAuthorization + "'"); '.' +
opts.hostname.replace(/^\*\./, '');
console.log(
"Put the string '" +
opts.dnsAuthorization +
"' into the TXT record '" +
pathname +
"'"
);
console.log(
'ddig TXT ' + pathname + " '" + opts.dnsAuthorization + "'"
);
} else { } else {
cb(new Error("[acme-v2] unrecognized challenge type")); cb(new Error('[acme-v2] unrecognized challenge type'));
return; return;
} }
console.log("\nThen hit the 'any' key to continue..."); console.log("\nThen hit the 'any' key to continue...");
@ -57,24 +88,28 @@ module.exports.run = function run(directoryUrl, RSA, web, chType, email, account
process.stdin.setRawMode(true); process.stdin.setRawMode(true);
process.stdin.resume(); process.stdin.resume();
process.stdin.on('data', onAny); process.stdin.on('data', onAny);
} },
, removeChallenge: function (opts, cb) { removeChallenge: function(opts, cb) {
// hostname, key // hostname, key
console.log('[acme-v2] remove challenge', opts.hostname, opts.keyAuthorization); console.log(
'[acme-v2] remove challenge',
opts.hostname,
opts.keyAuthorization
);
setTimeout(cb, 1 * 1000); setTimeout(cb, 1 * 1000);
} },
, challengeType: chType challengeType: chType,
, email: email email: email,
, accountKeypair: accountKeypair accountKeypair: accountKeypair,
, domainKeypair: domainKeypair domainKeypair: domainKeypair,
, domains: web domains: web
}; };
acme2.accounts.create(options).then(function (account) { acme2.accounts.create(options).then(function(account) {
console.log('[acme-v2] account:'); console.log('[acme-v2] account:');
console.log(account); console.log(account);
acme2.certificates.create(options).then(function (fullchainPem) { acme2.certificates.create(options).then(function(fullchainPem) {
console.log('[acme-v2] fullchain.pem:'); console.log('[acme-v2] fullchain.pem:');
console.log(fullchainPem); console.log(fullchainPem);
}); });

View File

@ -4,33 +4,61 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'; 'use strict';
module.exports.run = function (directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair) { module.exports.run = function(
directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
) {
console.log('[DEBUG] run', web, chType, email); console.log('[DEBUG] run', web, chType, email);
var acme2 = require('../compat.js').ACME.create({ RSA: RSA }); var acme2 = require('../compat.js').ACME.create({ RSA: RSA });
acme2.getAcmeUrls(acme2.stagingServerUrl, function (err/*, directoryUrls*/) { acme2.getAcmeUrls(acme2.stagingServerUrl, function(err /*, directoryUrls*/) {
if (err) { console.log('err 1'); throw err; } if (err) {
console.log('err 1');
throw err;
}
var options = { var options = {
agreeToTerms: function (tosUrl, agree) { agreeToTerms: function(tosUrl, agree) {
agree(null, tosUrl); agree(null, tosUrl);
} },
, setChallenge: function (hostname, token, val, cb) { setChallenge: function(hostname, token, val, cb) {
var pathname; var pathname;
if ('http-01' === cb.type) { if ('http-01' === cb.type) {
pathname = hostname + acme2.acmeChallengePrefix + token; pathname = hostname + acme2.acmeChallengePrefix + token;
console.log("Put the string '" + val /*keyAuthorization*/ + "' into a file at '" + pathname + "'"); console.log(
console.log("echo '" + val /*keyAuthorization*/ + "' > '" + pathname + "'"); "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..."); console.log("\nThen hit the 'any' key to continue...");
} else if ('dns-01' === cb.type) { } else if ('dns-01' === cb.type) {
// forwards-backwards compat // forwards-backwards compat
pathname = acme2.challengePrefixes['dns-01'] + "." + hostname.replace(/^\*\./, ''); pathname =
console.log("Put the string '" + cb.dnsAuthorization + "' into the TXT record '" + pathname + "'"); acme2.challengePrefixes['dns-01'] +
console.log("dig TXT " + pathname + " '" + cb.dnsAuthorization + "'"); '.' +
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..."); console.log("\nThen hit the 'any' key to continue...");
} else { } else {
cb(new Error("[acme-v2] unrecognized challenge type: " + cb.type)); cb(new Error('[acme-v2] unrecognized challenge type: ' + cb.type));
return; return;
} }
@ -45,25 +73,31 @@ module.exports.run = function (directoryUrl, RSA, web, chType, email, accountKey
process.stdin.setRawMode(true); process.stdin.setRawMode(true);
process.stdin.resume(); process.stdin.resume();
process.stdin.on('data', onAny); process.stdin.on('data', onAny);
} },
, removeChallenge: function (hostname, key, cb) { removeChallenge: function(hostname, key, cb) {
console.log('[DEBUG] remove challenge', hostname, key); console.log('[DEBUG] remove challenge', hostname, key);
setTimeout(cb, 1 * 1000); setTimeout(cb, 1 * 1000);
} },
, challengeType: chType challengeType: chType,
, email: email email: email,
, accountKeypair: accountKeypair accountKeypair: accountKeypair,
, domainKeypair: domainKeypair domainKeypair: domainKeypair,
, domains: web domains: web
}; };
acme2.registerNewAccount(options, function (err, account) { acme2.registerNewAccount(options, function(err, account) {
if (err) { console.log('err 2'); throw err; } if (err) {
console.log('err 2');
throw err;
}
if (options.debug) console.debug('account:'); if (options.debug) console.debug('account:');
if (options.debug) console.log(account); if (options.debug) console.log(account);
acme2.getCertificate(options, function (err, fullchainPem) { acme2.getCertificate(options, function(err, fullchainPem) {
if (err) { console.log('err 3'); throw err; } if (err) {
console.log('err 3');
throw err;
}
console.log('[acme-v2] A fullchain.pem:'); console.log('[acme-v2] A fullchain.pem:');
console.log(fullchainPem); console.log(fullchainPem);
}); });

View File

@ -23,58 +23,66 @@ Rules
*/ */
// https://github.com/certbot/certbot/issues/5721#issuecomment-402362709 // https://github.com/certbot/certbot/issues/5721#issuecomment-402362709
var expected = "----\nxxxx\nyyyy\n----\n\n----\nxxxx\nyyyy\n----\n"; var expected = '----\nxxxx\nyyyy\n----\n\n----\nxxxx\nyyyy\n----\n';
var tests = [ 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\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" '----\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\n----\r\nxxxx\r\nyyyy\r\n----',
, "----\nxxxx\nyyyy\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----',
, "----\nxxxx\nyyyy\n----\n----\nxxxx\nyyyy\n----\n" '----\nxxxx\nyyyy\n----\n----\nxxxx\nyyyy\n----\n',
, "----\nxxxx\nyyyy\n----\n\n----\nxxxx\nyyyy\n----\n" '----\nxxxx\nyyyy\n----\n\n----\nxxxx\nyyyy\n----\n',
, "----\nxxxx\nyyyy\n----\r\n----\nxxxx\ryyyy\n----\n" '----\nxxxx\nyyyy\n----\r\n----\nxxxx\ryyyy\n----\n'
]; ];
function formatPemChain(str) { function formatPemChain(str) {
return str.trim().replace(/[\r\n]+/g, '\n').replace(/\-\n\-/g, '-\n\n-') + '\n'; return (
str
.trim()
.replace(/[\r\n]+/g, '\n')
.replace(/\-\n\-/g, '-\n\n-') + '\n'
);
} }
function splitPemChain(str) { function splitPemChain(str) {
return str.trim().split(/[\r\n]{2,}/g).map(function (str) { return str
.trim()
.split(/[\r\n]{2,}/g)
.map(function(str) {
return str + '\n'; return str + '\n';
}); });
} }
tests.forEach(function (str) { tests.forEach(function(str) {
var actual = formatPemChain(str); var actual = formatPemChain(str);
if (expected !== actual) { if (expected !== actual) {
console.error('input: ', JSON.stringify(str)); console.error('input: ', JSON.stringify(str));
console.error('expected:', JSON.stringify(expected)); console.error('expected:', JSON.stringify(expected));
console.error('actual: ', JSON.stringify(actual)); console.error('actual: ', JSON.stringify(actual));
throw new Error("did not pass"); throw new Error('did not pass');
} }
}); });
if ( if (
"----\nxxxx\nyyyy\n----\n" '----\nxxxx\nyyyy\n----\n' !==
!== formatPemChain('\n\n----\r\nxxxx\r\nyyyy\r\n----\n\n')
formatPemChain("\n\n----\r\nxxxx\r\nyyyy\r\n----\n\n")
) { ) {
throw new Error("Not proper for single cert in chain"); throw new Error('Not proper for single cert in chain');
} }
if ( if (
"--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n" '--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n' !==
!== formatPemChain(
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") '\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"); throw new Error('Not proper for three certs in chain');
} }
splitPemChain( splitPemChain(
"--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n" '--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n\n--B--\nxxxx\nyyyy\n--E--\n'
).forEach(function (str) { ).forEach(function(str) {
if ("--B--\nxxxx\nyyyy\n--E--\n" !== str) { if ('--B--\nxxxx\nyyyy\n--E--\n' !== str) {
throw new Error("bad thingy"); throw new Error('bad thingy');
} }
}); });

View File

@ -5,19 +5,27 @@
'use strict'; 'use strict';
/* global Promise */ /* global Promise */
module.exports.run = function run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair) { module.exports.run = function run(
directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
) {
var acme2 = require('../').ACME.create({ RSA: RSA }); var acme2 = require('../').ACME.create({ RSA: RSA });
// [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01' // [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01'
acme2.init(directoryUrl).then(function () { acme2.init(directoryUrl).then(function() {
var options = { var options = {
agreeToTerms: function (tosUrl) { agreeToTerms: function(tosUrl) {
return Promise.resolve(tosUrl); return Promise.resolve(tosUrl);
} },
, setChallenge: function (opts) { setChallenge: function(opts) {
return new Promise(function (resolve, reject) { return new Promise(function(resolve, reject) {
var pathname; var pathname;
console.log(""); console.log('');
console.log('identifier:'); console.log('identifier:');
console.log(opts.identifier); console.log(opts.identifier);
console.log('hostname:'); console.log('hostname:');
@ -32,18 +40,41 @@ module.exports.run = function run(directoryUrl, RSA, web, chType, email, account
console.log(opts.keyAuthorization); console.log(opts.keyAuthorization);
console.log('dnsAuthorization:'); console.log('dnsAuthorization:');
console.log(opts.dnsAuthorization); console.log(opts.dnsAuthorization);
console.log(""); console.log('');
if ('http-01' === opts.type) { if ('http-01' === opts.type) {
pathname = opts.hostname + acme2.challengePrefixes['http-01'] + "/" + opts.token; pathname =
console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'"); opts.hostname +
console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'"); 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) { } else if ('dns-01' === opts.type) {
pathname = acme2.challengePrefixes['dns-01'] + "." + opts.hostname.replace(/^\*\./, ''); pathname =
console.log("Put the string '" + opts.dnsAuthorization + "' into the TXT record '" + pathname + "'"); acme2.challengePrefixes['dns-01'] +
console.log("dig TXT " + pathname + " '" + opts.dnsAuthorization + "'"); '.' +
opts.hostname.replace(/^\*\./, '');
console.log(
"Put the string '" +
opts.dnsAuthorization +
"' into the TXT record '" +
pathname +
"'"
);
console.log(
'dig TXT ' + pathname + " '" + opts.dnsAuthorization + "'"
);
} else { } else {
reject(new Error("[acme-v2] unrecognized challenge type")); reject(new Error('[acme-v2] unrecognized challenge type'));
return; return;
} }
console.log("\nThen hit the 'any' key to continue..."); console.log("\nThen hit the 'any' key to continue...");
@ -61,26 +92,30 @@ module.exports.run = function run(directoryUrl, RSA, web, chType, email, account
process.stdin.resume(); process.stdin.resume();
process.stdin.on('data', onAny); process.stdin.on('data', onAny);
}); });
} },
, removeChallenge: function (opts) { removeChallenge: function(opts) {
console.log('[acme-v2] remove challenge', opts.hostname, opts.keyAuthorization); console.log(
return new Promise(function (resolve) { '[acme-v2] remove challenge',
opts.hostname,
opts.keyAuthorization
);
return new Promise(function(resolve) {
// hostname, key // hostname, key
setTimeout(resolve, 1 * 1000); setTimeout(resolve, 1 * 1000);
}); });
} },
, challengeType: chType challengeType: chType,
, email: email email: email,
, accountKeypair: accountKeypair accountKeypair: accountKeypair,
, domainKeypair: domainKeypair domainKeypair: domainKeypair,
, domains: web domains: web
}; };
acme2.accounts.create(options).then(function (account) { acme2.accounts.create(options).then(function(account) {
console.log('[acme-v2] account:'); console.log('[acme-v2] account:');
console.log(account); console.log(account);
acme2.certificates.create(options).then(function (fullchainPem) { acme2.certificates.create(options).then(function(fullchainPem) {
console.log('[acme-v2] fullchain.pem:'); console.log('[acme-v2] fullchain.pem:');
console.log(fullchainPem); console.log(fullchainPem);
}); });