forked from root/acme.js
		
	v1.0.2
This commit is contained in:
		
							parent
							
								
									52406344ed
								
							
						
					
					
						commit
						28f5baf3d3
					
				
							
								
								
									
										58
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								README.md
									
									
									
									
									
								
							@ -1,11 +1,59 @@
 | 
			
		||||
acme-v2.js (draft 11)
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
| [acme-v2.js](https://git.coolaj86.com/coolaj86/acme-v2.js)
 | 
			
		||||
| [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)
 | 
			
		||||
 | 
			
		||||
| Sponsored by [ppl](https://ppl.family)
 | 
			
		||||
 | 
			
		||||
A framework for building letsencrypt v2 (IETF ACME draft 11) clients, successor to `le-acme-core.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).
 | 
			
		||||
 | 
			
		||||
Summary of spec that I'm working off of here: 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
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Let's Encrypt Directory URLs
 | 
			
		||||
 | 
			
		||||
@ -136,7 +184,11 @@ Todo
 | 
			
		||||
Changelog
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
* v1.0.0
 | 
			
		||||
* 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
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@ var rl = readline.createInterface({
 | 
			
		||||
  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);
 | 
			
		||||
@ -35,12 +37,31 @@ function getEmail(web, chType) {
 | 
			
		||||
    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 accountKeypair = RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/account.privkey.pem') });
 | 
			
		||||
    var domainKeypair = RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/privkey.pem') });
 | 
			
		||||
    //require('./test.compat.js').run(web, chType, email, accountKeypair, domainKeypair);
 | 
			
		||||
    //require('./test.cb.js').run(web, chType, email, accountKeypair, domainKeypair);
 | 
			
		||||
    require('./test.promise.js').run(web, chType, email, accountKeypair, domainKeypair);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/genkeypair.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/genkeypair.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
var RSA = require('rsa-compat').RSA;
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
 | 
			
		||||
if (!fs.existsSync(__dirname + '/../tests/account.privkey.pem')) {
 | 
			
		||||
  RSA.generateKeypair(2048, 65537, {}, function (err, keypair) {
 | 
			
		||||
    console.log(keypair);
 | 
			
		||||
    var privkeyPem = RSA.exportPrivatePem(keypair)
 | 
			
		||||
    console.log(privkeyPem);
 | 
			
		||||
 | 
			
		||||
    fs.writeFileSync(__dirname + '/../tests/account.privkey.pem', privkeyPem);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!fs.existsSync(__dirname + '/../tests/privkey.pem')) {
 | 
			
		||||
  RSA.generateKeypair(2048, 65537, {}, function (err, keypair) {
 | 
			
		||||
    console.log(keypair);
 | 
			
		||||
    var privkeyPem = RSA.exportPrivatePem(keypair)
 | 
			
		||||
    console.log(privkeyPem);
 | 
			
		||||
 | 
			
		||||
    fs.writeFileSync(__dirname + '/../tests/privkey.pem', privkeyPem);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/http-server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/http-server.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var http = require('http');
 | 
			
		||||
var express = require('express');
 | 
			
		||||
var server = http.createServer(express.static('../tests')).listen(80, function () {
 | 
			
		||||
  console.log('Listening on', this.address());
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										11
									
								
								examples/https-server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								examples/https-server.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var https = require('https');
 | 
			
		||||
var server = https.createServer({
 | 
			
		||||
  key: require('fs').readFileSync('../tests/privkey.pem')
 | 
			
		||||
, cert: require('fs').readFileSync('../tests/fullchain.pem')
 | 
			
		||||
}, function (req, res) {
 | 
			
		||||
  res.end("Hello, World!");
 | 
			
		||||
}).listen(443, function () {
 | 
			
		||||
  console.log('Listening on', this.address());
 | 
			
		||||
});
 | 
			
		||||
@ -1,18 +0,0 @@
 | 
			
		||||
var RSA = require('rsa-compat').RSA;
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
 | 
			
		||||
RSA.generateKeypair(2048, 65537, {}, function (err, keypair) {
 | 
			
		||||
	console.log(keypair);
 | 
			
		||||
	var privkeyPem = RSA.exportPrivatePem(keypair)
 | 
			
		||||
	console.log(privkeyPem);
 | 
			
		||||
 | 
			
		||||
	fs.writeFileSync(__dirname + '/account.privkey.pem', privkeyPem);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
RSA.generateKeypair(2048, 65537, {}, function (err, keypair) {
 | 
			
		||||
	console.log(keypair);
 | 
			
		||||
	var privkeyPem = RSA.exportPrivatePem(keypair)
 | 
			
		||||
	console.log(privkeyPem);
 | 
			
		||||
 | 
			
		||||
	fs.writeFileSync(__dirname + '/privkey.pem', privkeyPem);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										30
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								node.js
									
									
									
									
									
								
							@ -112,10 +112,16 @@ ACME._registerAccount = function (me, options) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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: [ 'mailto:' + options.email ]
 | 
			
		||||
        , contact: contact
 | 
			
		||||
        };
 | 
			
		||||
        if (options.externalAccount) {
 | 
			
		||||
          body.externalAccountBinding = me.RSA.signJws(
 | 
			
		||||
@ -150,6 +156,8 @@ ACME._registerAccount = function (me, options) {
 | 
			
		||||
        , headers: { 'Content-Type': 'application/jose+json' }
 | 
			
		||||
        , json: jws
 | 
			
		||||
        }).then(function (resp) {
 | 
			
		||||
          var account = resp.body;
 | 
			
		||||
 | 
			
		||||
          me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
          var location = resp.toJSON().headers.location;
 | 
			
		||||
          // the account id url
 | 
			
		||||
@ -157,15 +165,33 @@ ACME._registerAccount = function (me, options) {
 | 
			
		||||
          if (me.debug) console.debug('[DEBUG] new account location:');
 | 
			
		||||
          if (me.debug) console.debug(location);
 | 
			
		||||
          if (me.debug) console.debug(resp.toJSON());
 | 
			
		||||
          return resp.body;
 | 
			
		||||
          
 | 
			
		||||
          /*
 | 
			
		||||
          {
 | 
			
		||||
            id: 5925245,
 | 
			
		||||
            key:
 | 
			
		||||
             { kty: 'RSA',
 | 
			
		||||
               n: 'tBr7m1hVaUNQjUeakznGidnrYyegVUQrsQjNrcipljI9Vxvxd0baHc3vvRZWFyFO5BlS7UDl-KHQdbdqb-MQzfP6T2sNXsOHARQ41pCGY5BYzIPRJF0nD48-CY717is-7BKISv8rf9yx5iSjvK1wZ3Ke3YIpxzK2fWRqccVxXQ92VYioxOfGObACgEUSvdoEttWV2B0Uv4Sdi6zZbk5eo2zALvyGb1P4fKVfQycGLXC41AyhHOAuTqzNCyIkiWEkbfh2lZNcYClP2epS0pHRFXYyjJN6-c8InfM3PISo4k6Qew65HZ-oqUow0tTIgNwuen9q5O6Hc73GvU-2npGJVQ',
 | 
			
		||||
               e: 'AQAB' },
 | 
			
		||||
            contact: [],
 | 
			
		||||
            initialIp: '198.199.82.211',
 | 
			
		||||
            createdAt: '2018-04-16T00:41:00.720584972Z',
 | 
			
		||||
            status: 'valid'
 | 
			
		||||
          }
 | 
			
		||||
          */
 | 
			
		||||
          if (!account) { account = { _emptyResponse: true, 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);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "acme-v2",
 | 
			
		||||
  "version": "1.0.1",
 | 
			
		||||
  "version": "1.0.2",
 | 
			
		||||
  "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",
 | 
			
		||||
  "main": "node.js",
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,8 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports.run = function run(web, chType, email, accountKeypair, domainKeypair) {
 | 
			
		||||
  var RSA = require('rsa-compat').RSA;
 | 
			
		||||
  var directoryUrl = 'https://acme-staging-v02.api.letsencrypt.org/directory';
 | 
			
		||||
  var acme2 = require('./').ACME.create({ RSA: RSA });
 | 
			
		||||
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) {
 | 
			
		||||
@ -1,11 +1,9 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var RSA = require('rsa-compat').RSA;
 | 
			
		||||
 | 
			
		||||
module.exports.run = function (web, chType, email, accountKeypair, domainKeypair) {
 | 
			
		||||
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 });
 | 
			
		||||
  var acme2 = require('../compat.js').ACME.create({ RSA: RSA });
 | 
			
		||||
  acme2.getAcmeUrls(acme2.stagingServerUrl, function (err/*, directoryUrls*/) {
 | 
			
		||||
    if (err) { console.log('err 1'); throw err; }
 | 
			
		||||
 | 
			
		||||
@ -44,8 +42,8 @@ module.exports.run = function (web, chType, email, accountKeypair, domainKeypair
 | 
			
		||||
 | 
			
		||||
    acme2.registerNewAccount(options, function (err, account) {
 | 
			
		||||
      if (err) { console.log('err 2'); throw err; }
 | 
			
		||||
      console.log('account:');
 | 
			
		||||
      console.log(account);
 | 
			
		||||
      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; }
 | 
			
		||||
@ -1,10 +1,8 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
/* global Promise */
 | 
			
		||||
module.exports.run = function run(web, chType, email, accountKeypair, domainKeypair) {
 | 
			
		||||
  var RSA = require('rsa-compat').RSA;
 | 
			
		||||
  var directoryUrl = 'https://acme-staging-v02.api.letsencrypt.org/directory';
 | 
			
		||||
  var acme2 = require('./').ACME.create({ RSA: RSA });
 | 
			
		||||
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 = {
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user