forked from root/acme.js
		
	successful test! yay!
This commit is contained in:
		
							parent
							
								
									df022959e4
								
							
						
					
					
						commit
						afbcef688f
					
				
							
								
								
									
										26
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								README.md
									
									
									
									
									
								
							@ -7,15 +7,21 @@ Summary of spec that I'm working off of here: https://git.coolaj86.com/coolaj86/
 | 
			
		||||
 | 
			
		||||
In progress
 | 
			
		||||
 | 
			
		||||
* get directory
 | 
			
		||||
* get nonce
 | 
			
		||||
* create account
 | 
			
		||||
* new order
 | 
			
		||||
* get challenges
 | 
			
		||||
* 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
 | 
			
		||||
 | 
			
		||||
Not yet tried
 | 
			
		||||
* Mar 20, 2018 - SUCCESS - got a test certificate (hard-coded)
 | 
			
		||||
 | 
			
		||||
* respond to challenges
 | 
			
		||||
* finalize order
 | 
			
		||||
* poll for status
 | 
			
		||||
* download certificates
 | 
			
		||||
Todo
 | 
			
		||||
 | 
			
		||||
* match api for acme v1 (le-acme-core.js)
 | 
			
		||||
* make not hard-coded
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								genkeypair.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								genkeypair.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
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);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										187
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								node.js
									
									
									
									
									
								
							@ -201,12 +201,167 @@ function create(deps) {
 | 
			
		||||
			 "signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
 | 
			
		||||
		 }
 | 
			
		||||
		*/
 | 
			
		||||
  , _getChallenges: function (options, auth) {
 | 
			
		||||
      console.log('\n[DEBUG] getChallenges\n');
 | 
			
		||||
      return request({ method: 'GET', url: auth, json: true }).then(function (resp) {
 | 
			
		||||
        console.log('Authorization:');
 | 
			
		||||
        console.log(resp.body.challenges);
 | 
			
		||||
        return resp.body.challenges;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    // https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
 | 
			
		||||
  , _postChallenge: function (options, ch) {
 | 
			
		||||
			var me = this;
 | 
			
		||||
 | 
			
		||||
      var body = { };
 | 
			
		||||
 | 
			
		||||
			var payload = JSON.stringify(body);
 | 
			
		||||
			//var payload = JSON.stringify(body, null, 2);
 | 
			
		||||
			var jws = RSA.signJws(
 | 
			
		||||
        options.keypair
 | 
			
		||||
      , new Buffer(payload)
 | 
			
		||||
      , { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid }
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
      var thumbprint = RSA.thumbprint(options.keypair);
 | 
			
		||||
      var keyAuthorization = ch.token + '.' + thumbprint;
 | 
			
		||||
      //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 | 
			
		||||
      //   /.well-known/acme-challenge/:token
 | 
			
		||||
      console.log('type:');
 | 
			
		||||
      console.log(ch.type);
 | 
			
		||||
      console.log('ch.token:');
 | 
			
		||||
      console.log(ch.token);
 | 
			
		||||
      console.log('thumbprint:');
 | 
			
		||||
      console.log(thumbprint);
 | 
			
		||||
      console.log('keyAuthorization:');
 | 
			
		||||
      console.log(keyAuthorization);
 | 
			
		||||
      /*
 | 
			
		||||
      options.setChallenge(ch.token, thumbprint, keyAuthorization, function (err) {
 | 
			
		||||
      });
 | 
			
		||||
      */
 | 
			
		||||
      function wait(ms) {
 | 
			
		||||
        return new Promise(function (resolve) {
 | 
			
		||||
          setTimeout(resolve, (ms || 1100));
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      function pollStatus() {
 | 
			
		||||
        console.log('\n[DEBUG] statusChallenge\n');
 | 
			
		||||
        return request({ method: 'GET', url: ch.url, json: true }).then(function (resp) {
 | 
			
		||||
          console.error('poll: resp.body:');
 | 
			
		||||
          console.error(resp.body);
 | 
			
		||||
 | 
			
		||||
          if ('pending' === resp.body.status) {
 | 
			
		||||
            console.log('poll: again');
 | 
			
		||||
            return wait().then(pollStatus);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if ('valid' === resp.body.status) {
 | 
			
		||||
            console.log('poll: valid');
 | 
			
		||||
            return resp.body;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (!resp.body.status) {
 | 
			
		||||
            console.error("[acme-v2] (y) bad challenge state:");
 | 
			
		||||
          }
 | 
			
		||||
          else if ('invalid' === resp.body.status) {
 | 
			
		||||
            console.error("[acme-v2] (x) invalid challenge state:");
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            console.error("[acme-v2] (z) bad challenge state:");
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.log('\n[DEBUG] postChallenge\n');
 | 
			
		||||
      //console.log('\n[DEBUG] stop to fix things\n'); return;
 | 
			
		||||
 | 
			
		||||
      function post() {
 | 
			
		||||
        return request({
 | 
			
		||||
          method: 'POST'
 | 
			
		||||
        , url: ch.url
 | 
			
		||||
        , headers: { 'Content-Type': 'application/jose+json' }
 | 
			
		||||
        , json: jws
 | 
			
		||||
        }).then(function (resp) {
 | 
			
		||||
          me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
          console.log('respond to challenge: resp.body:');
 | 
			
		||||
          console.log(resp.body);
 | 
			
		||||
          return wait().then(pollStatus);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return wait(20 * 1000).then(post);
 | 
			
		||||
    }
 | 
			
		||||
  , _finalizeOrder: function (options, validatedDomains) {
 | 
			
		||||
      console.log('finalizeOrder:');
 | 
			
		||||
			var me = this;
 | 
			
		||||
 | 
			
		||||
      var csr = RSA.generateCsrWeb64(options.certificateKeypair, validatedDomains);
 | 
			
		||||
      var body = { csr: csr };
 | 
			
		||||
      var payload = JSON.stringify(body);
 | 
			
		||||
 | 
			
		||||
      function wait(ms) {
 | 
			
		||||
        return new Promise(function (resolve) {
 | 
			
		||||
          setTimeout(resolve, (ms || 1100));
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function pollCert() {
 | 
			
		||||
        //var payload = JSON.stringify(body, null, 2);
 | 
			
		||||
        var jws = RSA.signJws(
 | 
			
		||||
          options.keypair
 | 
			
		||||
        , new Buffer(payload)
 | 
			
		||||
        , { nonce: me._nonce, alg: 'RS256', url: me._finalize, kid: me._kid }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        console.log('finalize:', me._finalize);
 | 
			
		||||
        return request({
 | 
			
		||||
          method: 'POST'
 | 
			
		||||
        , url: me._finalize
 | 
			
		||||
        , headers: { 'Content-Type': 'application/jose+json' }
 | 
			
		||||
        , json: jws
 | 
			
		||||
        }).then(function (resp) {
 | 
			
		||||
          me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
 | 
			
		||||
          console.log('order finalized: resp.body:');
 | 
			
		||||
          console.log(resp.body);
 | 
			
		||||
 | 
			
		||||
          if ('processing' === resp.body.status) {
 | 
			
		||||
            return wait().then(pollCert);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if ('valid' === resp.body.status) {
 | 
			
		||||
            me._expires = resp.body.expires;
 | 
			
		||||
            me._certificate = resp.body.certificate;
 | 
			
		||||
 | 
			
		||||
            return resp.body;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if ('invalid' === resp.body.status) {
 | 
			
		||||
            console.error('cannot finalize: badness');
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          console.error('(x) cannot finalize: badness');
 | 
			
		||||
          return;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return pollCert();
 | 
			
		||||
    }
 | 
			
		||||
  , _getCertificate: function (auth) {
 | 
			
		||||
      var me = this;
 | 
			
		||||
      return request({ method: 'GET', url: me._certificate, json: true }).then(function (resp) {
 | 
			
		||||
        console.log('Certificate:');
 | 
			
		||||
        console.log(resp.body);
 | 
			
		||||
        return resp.body;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , getCertificate: function (options, cb) {
 | 
			
		||||
			var me = this;
 | 
			
		||||
 | 
			
		||||
      var body = {
 | 
			
		||||
				identifiers: [
 | 
			
		||||
          { type: "dns" , value: "www.ppl.family" }
 | 
			
		||||
          { type: "dns" , value: "test.ppl.family" }
 | 
			
		||||
				/*
 | 
			
		||||
        , {	type: "dns" , value: "example.net" }
 | 
			
		||||
				*/
 | 
			
		||||
@ -223,6 +378,7 @@ function create(deps) {
 | 
			
		||||
      , { nonce: me._nonce, alg: 'RS256', url: me._directoryUrls.newOrder, kid: me._kid }
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
      console.log('\n[DEBUG] newOrder\n');
 | 
			
		||||
      return request({
 | 
			
		||||
        method: 'POST'
 | 
			
		||||
      , url: me._directoryUrls.newOrder
 | 
			
		||||
@ -237,14 +393,32 @@ function create(deps) {
 | 
			
		||||
        me._authorizations = resp.body.authorizations;
 | 
			
		||||
        me._order = location;
 | 
			
		||||
        me._finalize = resp.body.finalize;
 | 
			
		||||
        //console.log('[DEBUG] finalize:', me._finalize); return;
 | 
			
		||||
 | 
			
		||||
        //return resp.body;
 | 
			
		||||
        return Promise.all(me._authorizations.map(function (auth) {
 | 
			
		||||
          return request({ method: 'GET', url: auth, json: true }).then(function (resp) {
 | 
			
		||||
            console.log('Authorization:');
 | 
			
		||||
            console.log(resp.body.challenges);
 | 
			
		||||
          console.log('authz', auth);
 | 
			
		||||
          return me._getChallenges(options, auth).then(function (challenges) {
 | 
			
		||||
            var chp;
 | 
			
		||||
 | 
			
		||||
            challenges.forEach(function (ch) {
 | 
			
		||||
              if ('http-01' !== ch.type) {
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
              chp = me._postChallenge(options, ch);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return chp;
 | 
			
		||||
          });
 | 
			
		||||
        })).then(function () {
 | 
			
		||||
          var validatedDomains = body.identifiers.map(function (ident) {
 | 
			
		||||
            return ident.value;
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          return me._finalizeOrder(options, validatedDomains);
 | 
			
		||||
        }).then(function () {
 | 
			
		||||
          return me._getCertificate();
 | 
			
		||||
        });
 | 
			
		||||
        }));
 | 
			
		||||
      });
 | 
			
		||||
		}
 | 
			
		||||
  };
 | 
			
		||||
@ -260,7 +434,8 @@ acme2.getAcmeUrls().then(function (body) {
 | 
			
		||||
 | 
			
		||||
		var options = {
 | 
			
		||||
			email: 'coolaj86@gmail.com'
 | 
			
		||||
		, keypair: RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/privkey.pem') })
 | 
			
		||||
		, keypair: RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/account.privkey.pem') })
 | 
			
		||||
		, certificateKeypair: RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/privkey.pem') })
 | 
			
		||||
		};
 | 
			
		||||
    acme2.registerNewAccount(options).then(function (account) {
 | 
			
		||||
      console.log(account);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "acme-v2",
 | 
			
		||||
  "version": "0.0.1",
 | 
			
		||||
  "version": "0.0.2",
 | 
			
		||||
  "description": "A framework for building letsencrypt clients (and other ACME v2 clients), forked from le-acme-core.js.",
 | 
			
		||||
  "main": "node.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user