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

123
compat.js
View File

@ -8,74 +8,87 @@
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 =
return Promise.resolve(tos); options.agreeToTerms ||
}; function(tos) {
acme2.certificates.create(options).then(function (certs) { return Promise.resolve(tos);
var privkeyPem = acme2.RSA.exportPrivatePem(options.domainKeypair); };
certs.privkey = privkeyPem; acme2.certificates.create(options).then(function(certs) {
resolveFn(cb)(certs); var privkeyPem = acme2.RSA.exportPrivatePem(options.domainKeypair);
}, rejectFn(cb)); certs.privkey = privkeyPem;
}; resolveFn(cb)(certs);
acme2.getAcmeUrls = function (options, cb) { }, rejectFn(cb));
acme2.init(options).then(resolveFn(cb), rejectFn(cb)); };
}; acme2.getAcmeUrls = function(options, cb) {
acme2.getOptions = function () { acme2.init(options).then(resolveFn(cb), rejectFn(cb));
var defs = {}; };
acme2.getOptions = function() {
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];
}); });
return defs; return defs;
}; };
acme2.stagingServerUrl = module.exports.defaults.stagingServerUrl; acme2.stagingServerUrl = module.exports.defaults.stagingServerUrl;
acme2.productionServerUrl = module.exports.defaults.productionServerUrl; acme2.productionServerUrl = module.exports.defaults.productionServerUrl;
acme2.acmeChallengePrefix = module.exports.defaults.acmeChallengePrefix; acme2.acmeChallengePrefix = module.exports.defaults.acmeChallengePrefix;
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

@ -7,66 +7,125 @@
var RSA = require('rsa-compat').RSA; var RSA = require('rsa-compat').RSA;
var readline = require('readline'); var readline = require('readline');
var rl = readline.createInterface({ var rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout
}); });
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 (
console.log('Wildcard domains must use dns-01'); web.some(function(w) {
getEmail(web, 'dns-01'); return '*' === w[0];
} else { })
getChallengeType(web); ) {
} console.log('Wildcard domains must use dns-01');
}); getEmail(web, 'dns-01');
} else {
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);
}); });
} }
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(
} else if ('cb' === apiStyle) { directoryUrl,
require('../tests/cb.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair); RSA,
} else { web,
if ('v1-compat' !== apiStyle) { console.warn("Didn't understand '" + apiStyle + "', using 'v1-compat' instead..."); } chType,
require('../tests/compat.js').run(directoryUrl, RSA, web, chType, email, accountKeypair, domainKeypair); 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(); getWeb();

View File

@ -6,21 +6,21 @@ 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);
}); });
} }
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
console.log('Listening on', this.address()); .createServer(express.static('../tests'))
}); .listen(80, function() {
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 () { },
console.log('Listening on', this.address()); function(req, res) {
}); res.end('Hello, World!');
}
)
.listen(443, function() {
console.log('Listening on', this.address());
});

1824
node.js

File diff suppressed because it is too large Load Diff

76
package-lock.json generated
View File

@ -1,40 +1,40 @@
{ {
"name": "acme-v2", "name": "acme-v2",
"version": "1.7.6", "version": "1.7.6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@coolaj86/urequest": { "@coolaj86/urequest": {
"version": "1.3.7", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/@coolaj86/urequest/-/urequest-1.3.7.tgz", "resolved": "https://registry.npmjs.org/@coolaj86/urequest/-/urequest-1.3.7.tgz",
"integrity": "sha512-PPrVYra9aWvZjSCKl/x1pJ9ZpXda1652oJrPBYy5rQumJJMkmTBN3ux+sK2xAUwVvv2wnewDlaQaHLxLwSHnIA==" "integrity": "sha512-PPrVYra9aWvZjSCKl/x1pJ9ZpXda1652oJrPBYy5rQumJJMkmTBN3ux+sK2xAUwVvv2wnewDlaQaHLxLwSHnIA=="
}, },
"eckles": { "eckles": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/eckles/-/eckles-1.4.1.tgz", "resolved": "https://registry.npmjs.org/eckles/-/eckles-1.4.1.tgz",
"integrity": "sha512-auWyk/k8oSkVHaD4RxkPadKsLUcIwKgr/h8F7UZEueFDBO7BsE4y+H6IMUDbfqKIFPg/9MxV6KcBdJCmVVcxSA==" "integrity": "sha512-auWyk/k8oSkVHaD4RxkPadKsLUcIwKgr/h8F7UZEueFDBO7BsE4y+H6IMUDbfqKIFPg/9MxV6KcBdJCmVVcxSA=="
}, },
"keypairs": { "keypairs": {
"version": "1.2.14", "version": "1.2.14",
"resolved": "https://registry.npmjs.org/keypairs/-/keypairs-1.2.14.tgz", "resolved": "https://registry.npmjs.org/keypairs/-/keypairs-1.2.14.tgz",
"integrity": "sha512-ZoZfZMygyB0QcjSlz7Rh6wT2CJasYEHBPETtmHZEfxuJd7bnsOG5AdtPZqHZBT+hoHvuWCp/4y8VmvTvH0Y9uA==", "integrity": "sha512-ZoZfZMygyB0QcjSlz7Rh6wT2CJasYEHBPETtmHZEfxuJd7bnsOG5AdtPZqHZBT+hoHvuWCp/4y8VmvTvH0Y9uA==",
"requires": { "requires": {
"eckles": "^1.4.1", "eckles": "^1.4.1",
"rasha": "^1.2.4" "rasha": "^1.2.4"
} }
}, },
"rasha": { "rasha": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.5.tgz", "resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.5.tgz",
"integrity": "sha512-KxtX+/fBk+wM7O3CNgwjSh5elwFilLvqWajhr6wFr2Hd63JnKTTi43Tw+Jb1hxJQWOwoya+NZWR2xztn3hCrTw==" "integrity": "sha512-KxtX+/fBk+wM7O3CNgwjSh5elwFilLvqWajhr6wFr2Hd63JnKTTi43Tw+Jb1hxJQWOwoya+NZWR2xztn3hCrTw=="
}, },
"rsa-compat": { "rsa-compat": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-2.0.6.tgz", "resolved": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-2.0.6.tgz",
"integrity": "sha512-bQmpscAQec9442RaghDybrHMy1twQ3nUZOgTlqntio1yru+rMnDV64uGRzKp7dJ4VVhNv3mLh3X4MNON+YM0dA==", "integrity": "sha512-bQmpscAQec9442RaghDybrHMy1twQ3nUZOgTlqntio1yru+rMnDV64uGRzKp7dJ4VVhNv3mLh3X4MNON+YM0dA==",
"requires": { "requires": {
"keypairs": "^1.2.14" "keypairs": "^1.2.14"
} }
} }
} }
} }

View File

@ -1,32 +1,32 @@
{ {
"name": "acme-v2", "name": "acme-v2",
"version": "1.7.7", "version": "1.7.7",
"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", "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",
"homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js", "homepage": "https://git.coolaj86.com/coolaj86/acme-v2.js",
"main": "node.js", "main": "node.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "ssh://gitea@git.coolaj86.com:22042/coolaj86/acme-v2.js.git" "url": "ssh://gitea@git.coolaj86.com:22042/coolaj86/acme-v2.js.git"
}, },
"keywords": [ "keywords": [
"Let's Encrypt", "Let's Encrypt",
"ACME", "ACME",
"v02", "v02",
"v2", "v2",
"draft-11", "draft-11",
"draft-12", "draft-12",
"free ssl", "free ssl",
"tls", "tls",
"automated https", "automated https",
"letsencrypt" "letsencrypt"
], ],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"@coolaj86/urequest": "^1.3.6", "@coolaj86/urequest": "^1.3.6",
"rsa-compat": "^2.0.6" "rsa-compat": "^2.0.6"
} }
} }

View File

@ -4,80 +4,115 @@
* 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(
// [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01' directoryUrl,
var acme2 = require('../').ACME.create({ RSA: RSA }); RSA,
acme2.init(directoryUrl).then(function () { web,
var options = { chType,
agreeToTerms: function (tosUrl, agree) { email,
agree(null, tosUrl); accountKeypair,
} domainKeypair
, setChallenge: function (opts, cb) { ) {
var pathname; // [ '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('');
console.log('identifier:'); console.log('identifier:');
console.log(opts.identifier); console.log(opts.identifier);
console.log('hostname:'); console.log('hostname:');
console.log(opts.hostname); console.log(opts.hostname);
console.log('type:'); console.log('type:');
console.log(opts.type); console.log(opts.type);
console.log('token:'); console.log('token:');
console.log(opts.token); console.log(opts.token);
console.log('thumbprint:'); console.log('thumbprint:');
console.log(opts.thumbprint); console.log(opts.thumbprint);
console.log('keyAuthorization:'); console.log('keyAuthorization:');
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'] +
} else if ('dns-01' === opts.type) { '/' +
pathname = acme2.challengePrefixes['dns-01'] + "." + opts.hostname.replace(/^\*\./, ''); opts.token;
console.log("Put the string '" + opts.dnsAuthorization + "' into the TXT record '" + pathname + "'"); console.log(
console.log("ddig TXT " + pathname + " '" + opts.dnsAuthorization + "'"); "Put the string '" +
} else { opts.keyAuthorization +
cb(new Error("[acme-v2] unrecognized challenge type")); "' into a file at '" +
return; pathname +
} "'"
console.log("\nThen hit the 'any' key to continue..."); );
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() { function onAny() {
console.log("'any' key was hit"); console.log("'any' key was hit");
process.stdin.pause(); process.stdin.pause();
process.stdin.removeListener('data', onAny); process.stdin.removeListener('data', onAny);
process.stdin.setRawMode(false); process.stdin.setRawMode(false);
cb(); cb();
} }
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(
setTimeout(cb, 1 * 1000); '[acme-v2] remove challenge',
} opts.hostname,
, challengeType: chType opts.keyAuthorization
, email: email );
, accountKeypair: accountKeypair setTimeout(cb, 1 * 1000);
, domainKeypair: domainKeypair },
, domains: web challengeType: chType,
}; email: email,
accountKeypair: accountKeypair,
domainKeypair: domainKeypair,
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,69 +4,103 @@
* 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(
console.log('[DEBUG] run', web, chType, email); directoryUrl,
RSA,
web,
chType,
email,
accountKeypair,
domainKeypair
) {
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 '" +
console.log("\nThen hit the 'any' key to continue..."); val /*keyAuthorization*/ +
} else if ('dns-01' === cb.type) { "' into a file at '" +
// forwards-backwards compat pathname +
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(
console.log("\nThen hit the 'any' key to continue..."); "echo '" + val /*keyAuthorization*/ + "' > '" + pathname + "'"
} else { );
cb(new Error("[acme-v2] unrecognized challenge type: " + cb.type)); console.log("\nThen hit the 'any' key to continue...");
return; } 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() { function onAny() {
console.log("'any' key was hit"); console.log("'any' key was hit");
process.stdin.pause(); process.stdin.pause();
process.stdin.removeListener('data', onAny); process.stdin.removeListener('data', onAny);
process.stdin.setRawMode(false); process.stdin.setRawMode(false);
cb(); cb();
} }
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) {
if (options.debug) console.debug('account:'); console.log('err 2');
if (options.debug) console.log(account); throw err;
}
if (options.debug) console.debug('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('[acme-v2] A fullchain.pem:'); console.log('err 3');
console.log(fullchainPem); throw err;
}); }
}); console.log('[acme-v2] A fullchain.pem:');
}); console.log(fullchainPem);
});
});
});
}; };

View File

@ -23,59 +23,67 @@ 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
return str + '\n'; .trim()
}); .split(/[\r\n]{2,}/g)
.map(function(str) {
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');
} }
}); });
console.info('PASS'); console.info('PASS');

View File

@ -5,85 +5,120 @@
'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(
var acme2 = require('../').ACME.create({ RSA: RSA }); directoryUrl,
// [ 'test.ppl.family' ] 'coolaj86@gmail.com''http-01' RSA,
acme2.init(directoryUrl).then(function () { web,
var options = { chType,
agreeToTerms: function (tosUrl) { email,
return Promise.resolve(tosUrl); accountKeypair,
} domainKeypair
, setChallenge: function (opts) { ) {
return new Promise(function (resolve, reject) { var acme2 = require('../').ACME.create({ RSA: RSA });
var pathname; // [ '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('');
console.log('identifier:'); console.log('identifier:');
console.log(opts.identifier); console.log(opts.identifier);
console.log('hostname:'); console.log('hostname:');
console.log(opts.hostname); console.log(opts.hostname);
console.log('type:'); console.log('type:');
console.log(opts.type); console.log(opts.type);
console.log('token:'); console.log('token:');
console.log(opts.token); console.log(opts.token);
console.log('thumbprint:'); console.log('thumbprint:');
console.log(opts.thumbprint); console.log(opts.thumbprint);
console.log('keyAuthorization:'); console.log('keyAuthorization:');
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'] +
} else if ('dns-01' === opts.type) { '/' +
pathname = acme2.challengePrefixes['dns-01'] + "." + opts.hostname.replace(/^\*\./, ''); opts.token;
console.log("Put the string '" + opts.dnsAuthorization + "' into the TXT record '" + pathname + "'"); console.log(
console.log("dig TXT " + pathname + " '" + opts.dnsAuthorization + "'"); "Put the string '" +
} else { opts.keyAuthorization +
reject(new Error("[acme-v2] unrecognized challenge type")); "' into a file at '" +
return; pathname +
} "'"
console.log("\nThen hit the 'any' key to continue..."); );
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() { function onAny() {
console.log("'any' key was hit"); console.log("'any' key was hit");
process.stdin.pause(); process.stdin.pause();
process.stdin.removeListener('data', onAny); process.stdin.removeListener('data', onAny);
process.stdin.setRawMode(false); process.stdin.setRawMode(false);
resolve(); resolve();
return; return;
} }
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) { removeChallenge: function(opts) {
console.log('[acme-v2] remove challenge', opts.hostname, opts.keyAuthorization); console.log(
return new Promise(function (resolve) { '[acme-v2] remove challenge',
// hostname, key opts.hostname,
setTimeout(resolve, 1 * 1000); opts.keyAuthorization
}); );
} return new Promise(function(resolve) {
, challengeType: chType // hostname, key
, email: email setTimeout(resolve, 1 * 1000);
, accountKeypair: accountKeypair });
, domainKeypair: domainKeypair },
, domains: web challengeType: chType,
}; email: email,
accountKeypair: accountKeypair,
domainKeypair: domainKeypair,
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);
}); });
}); });
}); });
}; };