diff --git a/README.md b/README.md index 01539b3..771fede 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,12 @@ -acme-v2.js +acme-v2.js (draft 11) ========== | Sponsored by [ppl](https://ppl.family) -A framework for building letsencrypt clients (and other ACME v2 clients), forked from `le-acme-core.js`. +A framework for building letsencrypt v2 (IETF ACME draft 11), forked from `le-acme-core.js`. Summary of spec that I'm working off of here: https://git.coolaj86.com/coolaj86/greenlock.js/issues/5#issuecomment-8 -In progress - -* Mar 15, 2018 - get directory -* Mar 15, 2018 - get nonce -* Mar 15, 2018 - generate account keypair -* Mar 15, 2018 - create account -* Mar 16, 2018 - new order -* Mar 16, 2018 - get challenges -* Mar 20, 2018 - respond to challenges -* Mar 20, 2018 - generate domain keypair -* Mar 20, 2018 - finalize order (submit csr) -* Mar 20, 2018 - poll for status -* Mar 20, 2018 - download certificate -* Mar 20, 2018 - SUCCESS - got a test certificate (hard-coded) -* Mar 21, 2018 - can now accept values (not hard coded) -* Mar 21, 2018 - *mostly* matches le-acme-core.js API -* Apr 5, 2018 - completely match api for acme v1 (le-acme-core.js) -* Apr 5, 2018 - test wildcard -* Apr 5, 2018 - test two subdomains -* Apr 5, 2018 - test subdomains and its wildcard -* Apr 5, 2018 - test http and dns challenges (success and failure) -* Apr 5, 2018 - export http and dns challenge tests -* Apr 10, 2018 - tested backwards-compatibility using greenlock.js - -Todo - -* support ECDSA keys -* Apr 5, 2018 - appears that sometimes 'pending' status cannot be progressed to 'processing' nor 'deactivated' - ## Let's Encrypt Directory URLs ``` @@ -48,7 +19,48 @@ https://acme-v02.api.letsencrypt.org/directory https://acme-staging-v02.api.letsencrypt.org/directory ``` -## API +## 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 + +``` +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 +// +``` + +See documentation at + +## draft API (dev) + +Status: Almost stable, not locked + +This API is a simple evolution of le-acme-core, +but tries to provide a better mapping to the new draft 11 APIs. ``` var ACME = require('acme-v2').ACME.create({ @@ -110,6 +122,41 @@ Helpers & Stuff ```javascript // Constants -ACME.acmeChallengePrefix // /.well-known/acme-challenge/ +ACME.challengePrefixes['http-01'] // '/.well-known/acme-challenge' +ACME.challengePrefixes['dns-01'] // '_acme-challenge' ``` +Todo +---- + +* support ECDSA keys +* Apr 5, 2018 - appears that sometimes 'pending' status cannot be progressed to 'processing' nor 'deactivated' + * this may be a bug in the staging API as it appears it cannot be cancelled either, but returns success status code + +Changelog +--------- + +* v1.0.0 + * 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 diff --git a/compat.js b/compat.js index 8b35579..8ca1941 100644 --- a/compat.js +++ b/compat.js @@ -50,6 +50,7 @@ function create(deps) { }; acme2.stagingServerUrl = module.exports.defaults.stagingServerUrl; acme2.productionServerUrl = module.exports.defaults.productionServerUrl; + acme2.acmeChallengePrefix = module.exports.defaults.acmeChallengePrefix; return acme2; } @@ -63,11 +64,12 @@ module.exports.defaults = { //, 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; }); +module.exports.ACME.create = create; diff --git a/node.js b/node.js index 5964c44..b23e42f 100644 --- a/node.js +++ b/node.js @@ -8,15 +8,13 @@ var ACME = module.exports.ACME = {}; -ACME.acmeChallengePrefix = '/.well-known/acme-challenge/'; -ACME.acmeChallengeDnsPrefix = '_acme-challenge'; -ACME.acmeChallengePrefixes = { +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.acmeChallengePrefixes['http-01'] + '/' + auth.token; + var url = 'http://' + auth.hostname + ACME.challengePrefixes['http-01'] + '/' + auth.token; return me._request({ url: url }).then(function (resp) { var err; @@ -32,7 +30,7 @@ ACME.challengeTests = { , 'dns-01': function (me, auth) { return me._dig({ type: 'TXT' - , name: ACME.acmeChallengePrefixes['dns-01'] + '.' + auth.hostname + , name: ACME.challengePrefixes['dns-01'] + '.' + auth.hostname }).then(function (ans) { var err; @@ -562,9 +560,7 @@ ACME.create = function create(me) { if (!me) { me = {}; } // me.debug = true; - me.acmeChallengePrefix = ACME.acmeChallengePrefix; - me.acmeChallengeDnsPrefix = ACME.acmeChallengeDnsPrefix; - me.acmeChallengePrefixes = ACME.acmeChallengePrefixes; + me.challengePrefixes = ACME.challengePrefixes; me.RSA = me.RSA || require('rsa-compat').RSA; me.request = me.request || require('request'); me._dig = function (query) { diff --git a/test.cb.js b/test.cb.js index d101435..f2c232c 100644 --- a/test.cb.js +++ b/test.cb.js @@ -31,11 +31,11 @@ module.exports.run = function run(web, chType, email, accountKeypair, domainKeyp console.log(""); if ('http-01' === opts.type) { - pathname = opts.hostname + acme2.acmeChallengePrefixes['http-01'] + "/" + opts.token; + 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.acmeChallengeDnsPrefix + "." + opts.hostname.replace(/^\*\./, ''); + 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 { diff --git a/test.promise.js b/test.promise.js index 326bd43..bf04f60 100644 --- a/test.promise.js +++ b/test.promise.js @@ -33,11 +33,11 @@ module.exports.run = function run(web, chType, email, accountKeypair, domainKeyp console.log(""); if ('http-01' === opts.type) { - pathname = opts.hostname + acme2.acmeChallengePrefixes['http-01'] + "/" + opts.token; + 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.acmeChallengeDnsPrefix + "." + opts.hostname.replace(/^\*\./, '');; + 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 {