forked from root/acme.js
		
	not hard-coded, almost backwards compatible
This commit is contained in:
		
							parent
							
								
									afbcef688f
								
							
						
					
					
						commit
						08e6fdc1a7
					
				
							
								
								
									
										60
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								README.md
									
									
									
									
									
								
							@ -18,10 +18,64 @@ In progress
 | 
			
		||||
* 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
 | 
			
		||||
 | 
			
		||||
Todo
 | 
			
		||||
 | 
			
		||||
* match api for acme v1 (le-acme-core.js)
 | 
			
		||||
* make not hard-coded
 | 
			
		||||
* completely match api for acme v1 (le-acme-core.js)
 | 
			
		||||
* test http and dns challenges
 | 
			
		||||
* export http and dns challenge tests
 | 
			
		||||
* support ECDSA keys
 | 
			
		||||
 | 
			
		||||
## API
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
var ACME = require('acme-v2.js').ACME.create({
 | 
			
		||||
  RSA: require('rsa-compat').RSA
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// Accounts
 | 
			
		||||
ACME.registerNewAccount(options, cb)        // returns "regr" registration data
 | 
			
		||||
 | 
			
		||||
    { email: '<email>'                        //    valid email (server checks MX records)
 | 
			
		||||
    , accountKeypair: {                       //    privateKeyPem or privateKeyJwt
 | 
			
		||||
        privateKeyPem: '<ASCII PEM>'
 | 
			
		||||
      }
 | 
			
		||||
    , agreeToTerms: fn (tosUrl, cb) {}        //    must specify agree=tosUrl to continue (or falsey to end)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// Registration
 | 
			
		||||
ACME.getCertificate(options, cb)            // returns (err, pems={ privkey (key), cert, chain (ca) })
 | 
			
		||||
 | 
			
		||||
    { newAuthzUrl: '<url>'                    //    specify acmeUrls.newAuthz
 | 
			
		||||
    , newCertUrl: '<url>'                     //    specify acmeUrls.newCert
 | 
			
		||||
 | 
			
		||||
    , domainKeypair: {
 | 
			
		||||
        privateKeyPem: '<ASCII PEM>'
 | 
			
		||||
      }
 | 
			
		||||
    , accountKeypair: {
 | 
			
		||||
        privateKeyPem: '<ASCII PEM>'
 | 
			
		||||
      }
 | 
			
		||||
    , domains: [ 'example.com' ]
 | 
			
		||||
 | 
			
		||||
    , setChallenge: fn (hostname, key, val, cb)
 | 
			
		||||
    , removeChallenge: fn (hostname, key, cb)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// Discovery URLs
 | 
			
		||||
ACME.getAcmeUrls(acmeDiscoveryUrl, cb)      // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Helpers & Stuff
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// Constants
 | 
			
		||||
ACME.productionServerUrl                // https://acme-v02.api.letsencrypt.org/directory
 | 
			
		||||
ACME.stagingServerUrl                   // https://acme-staging-v02.api.letsencrypt.org/directory
 | 
			
		||||
ACME.acmeChallengePrefix                // /.well-known/acme-challenge/
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										280
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										280
									
								
								node.js
									
									
									
									
									
								
							@ -2,8 +2,9 @@
 | 
			
		||||
 * acme-v2.js
 | 
			
		||||
 * Copyright(c) 2018 AJ ONeal <aj@ppl.family> https://ppl.family
 | 
			
		||||
 * Apache-2.0 OR MIT (and hence also MPL 2.0)
 | 
			
		||||
*/
 | 
			
		||||
 */
 | 
			
		||||
'use strict';
 | 
			
		||||
/* globals Promise */
 | 
			
		||||
 | 
			
		||||
var defaults = {
 | 
			
		||||
  productionServerUrl:    'https://acme-v02.api.letsencrypt.org/directory'
 | 
			
		||||
@ -52,57 +53,13 @@ function create(deps) {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var RSA = deps.RSA || require('rsa-compat').RSA;
 | 
			
		||||
  deps.request = deps.request || require('request');
 | 
			
		||||
  deps.promisify = deps.promisify || require('util').promisify;
 | 
			
		||||
 | 
			
		||||
  var directoryUrl = deps.directoryUrl || defaults.stagingServerUrl;
 | 
			
		||||
  var request = deps.promisify(getRequest({}));
 | 
			
		||||
 | 
			
		||||
			var crypto = require('crypto');
 | 
			
		||||
			RSA.signJws = RSA.generateJws = RSA.generateSignatureJws = RSA.generateSignatureJwk =
 | 
			
		||||
			function (keypair, payload, nonce) {
 | 
			
		||||
        var prot = {};
 | 
			
		||||
        if (nonce) {
 | 
			
		||||
          if ('string' === typeof nonce) {
 | 
			
		||||
            prot.nonce = nonce;
 | 
			
		||||
          } else {
 | 
			
		||||
            prot = nonce;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
				keypair = RSA._internal.import(keypair);
 | 
			
		||||
				keypair = RSA._internal.importForge(keypair);
 | 
			
		||||
				keypair.publicKeyJwk = RSA.exportPublicJwk(keypair);
 | 
			
		||||
 | 
			
		||||
				// Compute JWS signature
 | 
			
		||||
				var protectedHeader = "";
 | 
			
		||||
				if (Object.keys(prot).length) {
 | 
			
		||||
					protectedHeader = JSON.stringify(prot); // { alg: prot.alg, nonce: prot.nonce, url: prot.url });
 | 
			
		||||
				}
 | 
			
		||||
				var protected64 = RSA.utils.toWebsafeBase64(new Buffer(protectedHeader).toString('base64'));
 | 
			
		||||
				var payload64 = RSA.utils.toWebsafeBase64(payload.toString('base64'));
 | 
			
		||||
				var raw = protected64 + "." + payload64;
 | 
			
		||||
				var sha256Buf = crypto.createHash('sha256').update(raw).digest();
 | 
			
		||||
				var sig64;
 | 
			
		||||
 | 
			
		||||
				if (RSA._URSA) {
 | 
			
		||||
					sig64 = RSA._ursaGenerateSig(keypair, sha256Buf);
 | 
			
		||||
				} else {
 | 
			
		||||
					sig64 = RSA._forgeGenerateSig(keypair, sha256Buf);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return {
 | 
			
		||||
          /*
 | 
			
		||||
					header: {
 | 
			
		||||
						alg: "RS256"
 | 
			
		||||
					, jwk: keypair.publicKeyJwk
 | 
			
		||||
					}
 | 
			
		||||
          */
 | 
			
		||||
				  protected: protected64
 | 
			
		||||
				, payload: payload64
 | 
			
		||||
				, signature: sig64
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
  var acme2 = {
 | 
			
		||||
    getAcmeUrls: function () {
 | 
			
		||||
      var me = this;
 | 
			
		||||
@ -114,6 +71,7 @@ function create(deps) {
 | 
			
		||||
    }
 | 
			
		||||
  , getNonce: function () {
 | 
			
		||||
      var me = this;
 | 
			
		||||
      if (me._nonce) { return new Promise(function (resolve) { resolve(me._nonce); return; }); }
 | 
			
		||||
      return request({ method: 'HEAD', url: me._directoryUrls.newNonce }).then(function (resp) {
 | 
			
		||||
        me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
        return me._nonce;
 | 
			
		||||
@ -142,31 +100,53 @@ function create(deps) {
 | 
			
		||||
  , registerNewAccount: function (options) {
 | 
			
		||||
      var me = this;
 | 
			
		||||
 | 
			
		||||
      console.log('[acme-v2] registerNewAccount');
 | 
			
		||||
 | 
			
		||||
      return me.getNonce().then(function () {
 | 
			
		||||
        return new Promise(function (resolve, reject) {
 | 
			
		||||
 | 
			
		||||
          function agree(err, tosUrl) {
 | 
			
		||||
            if (err) { reject(err); return; }
 | 
			
		||||
            if (me._tos !== tosUrl) {
 | 
			
		||||
              err = new Error("You must agree to the ToS at '" + me._tos + "'");
 | 
			
		||||
              err.code = "E_AGREE_TOS";
 | 
			
		||||
              reject(err);
 | 
			
		||||
              return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var jwk = RSA.exportPublicJwk(options.accountKeypair);
 | 
			
		||||
            var body = {
 | 
			
		||||
        termsOfServiceAgreed: true
 | 
			
		||||
              termsOfServiceAgreed: tosUrl === me._tos
 | 
			
		||||
            , onlyReturnExisting: false
 | 
			
		||||
            , contact: [ 'mailto:' + options.email ]
 | 
			
		||||
      /*
 | 
			
		||||
       "externalAccountBinding": {
 | 
			
		||||
         "protected": base64url({
 | 
			
		||||
           "alg": "HS256",
 | 
			
		||||
           "kid": /* key identifier from CA *//*,
 | 
			
		||||
           "url": "https://example.com/acme/new-account"
 | 
			
		||||
         }),
 | 
			
		||||
         "payload": base64url(/* same as in "jwk" above *//*),
 | 
			
		||||
         "signature": /* MAC using MAC key from CA *//*
 | 
			
		||||
       }
 | 
			
		||||
      */
 | 
			
		||||
            };
 | 
			
		||||
			var payload = JSON.stringify(body, null, 2);
 | 
			
		||||
            if (options.externalAccount) {
 | 
			
		||||
              body.externalAccountBinding = RSA.signJws(
 | 
			
		||||
                options.externalAccount.secret
 | 
			
		||||
              , undefined
 | 
			
		||||
              , { alg: "HS256"
 | 
			
		||||
                , kid: options.externalAccount.id
 | 
			
		||||
                , url: me._directoryUrls.newAccount
 | 
			
		||||
                }
 | 
			
		||||
              , new Buffer(JSON.stringify(jwk))
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            var payload = JSON.stringify(body);
 | 
			
		||||
            var jws = RSA.signJws(
 | 
			
		||||
        options.keypair
 | 
			
		||||
              options.accountKeypair
 | 
			
		||||
            , undefined
 | 
			
		||||
            , { nonce: me._nonce
 | 
			
		||||
              , alg: 'RS256'
 | 
			
		||||
              , url: me._directoryUrls.newAccount
 | 
			
		||||
              , jwk: jwk
 | 
			
		||||
              }
 | 
			
		||||
            , new Buffer(payload)
 | 
			
		||||
      , { nonce: me._nonce, alg: 'RS256', url: me._directoryUrls.newAccount, jwk: RSA.exportPublicJwk(options.keypair) }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
      console.log('jws:');
 | 
			
		||||
            console.log('[acme-v2] registerNewAccount JSON body:');
 | 
			
		||||
            delete jws.header;
 | 
			
		||||
            console.log(jws);
 | 
			
		||||
            me._nonce = null;
 | 
			
		||||
            return request({
 | 
			
		||||
              method: 'POST'
 | 
			
		||||
            , url: me._directoryUrls.newAccount
 | 
			
		||||
@ -174,11 +154,18 @@ function create(deps) {
 | 
			
		||||
            , json: jws
 | 
			
		||||
            }).then(function (resp) {
 | 
			
		||||
              me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
        var location = resp.toJSON().headers['location'];
 | 
			
		||||
              var location = resp.toJSON().headers.location;
 | 
			
		||||
              console.log('[DEBUG] new account location:'); // the account id url
 | 
			
		||||
              console.log(location); // the account id url
 | 
			
		||||
              console.log(resp.toJSON());
 | 
			
		||||
              me._kid = location;
 | 
			
		||||
              return resp.body;
 | 
			
		||||
            }).then(resolve);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          console.log('[acme-v2] agreeToTerms');
 | 
			
		||||
          options.agreeToTerms(me._tos, agree);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    /*
 | 
			
		||||
@ -204,26 +191,18 @@ function create(deps) {
 | 
			
		||||
  , _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;
 | 
			
		||||
        return resp.body;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    // https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
 | 
			
		||||
  , _postChallenge: function (options, ch) {
 | 
			
		||||
  , _postChallenge: function (options, identifier, 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 thumbprint = RSA.thumbprint(options.accountKeypair);
 | 
			
		||||
      var keyAuthorization = ch.token + '.' + thumbprint;
 | 
			
		||||
      //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 | 
			
		||||
      //   /.well-known/acme-challenge/:token
 | 
			
		||||
@ -235,15 +214,39 @@ function create(deps) {
 | 
			
		||||
      console.log(thumbprint);
 | 
			
		||||
      console.log('keyAuthorization:');
 | 
			
		||||
      console.log(keyAuthorization);
 | 
			
		||||
      /*
 | 
			
		||||
      options.setChallenge(ch.token, thumbprint, keyAuthorization, function (err) {
 | 
			
		||||
      });
 | 
			
		||||
      */
 | 
			
		||||
 | 
			
		||||
      return new Promise(function (resolve, reject) {
 | 
			
		||||
        if (options.setupChallenge) {
 | 
			
		||||
          options.setupChallenge(
 | 
			
		||||
            { identifier: identifier
 | 
			
		||||
            , hostname: identifier.value
 | 
			
		||||
            , type: ch.type
 | 
			
		||||
            , token: ch.token
 | 
			
		||||
            , thumbprint: thumbprint
 | 
			
		||||
            , keyAuthorization: keyAuthorization
 | 
			
		||||
            , dnsAuthorization: RSA.utils.toWebsafeBase64(
 | 
			
		||||
                require('crypto').createHash('sha256').update(keyAuthorization).digest('base64')
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
          , testChallenge
 | 
			
		||||
          );
 | 
			
		||||
        } else {
 | 
			
		||||
          options.setChallenge(identifier.value, ch.token, keyAuthorization, testChallenge);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function testChallenge(err) {
 | 
			
		||||
          if (err) { reject(err); return; }
 | 
			
		||||
 | 
			
		||||
          // TODO put check dns / http checks here?
 | 
			
		||||
          // http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
 | 
			
		||||
          // dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
 | 
			
		||||
 | 
			
		||||
          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) {
 | 
			
		||||
@ -257,6 +260,19 @@ function create(deps) {
 | 
			
		||||
 | 
			
		||||
              if ('valid' === resp.body.status) {
 | 
			
		||||
                console.log('poll: valid');
 | 
			
		||||
                try {
 | 
			
		||||
                  if (options.teardownChallenge) {
 | 
			
		||||
                    options.teardownChallenge(
 | 
			
		||||
                      { identifier: identifier
 | 
			
		||||
                      , type: ch.type
 | 
			
		||||
                      , token: ch.token
 | 
			
		||||
                      }
 | 
			
		||||
                    , function () {}
 | 
			
		||||
                    );
 | 
			
		||||
                  } else {
 | 
			
		||||
                    options.removeChallenge(identifier.value, ch.token, function () {});
 | 
			
		||||
                  }
 | 
			
		||||
                } catch(e) {}
 | 
			
		||||
                return resp.body;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
@ -269,6 +285,8 @@ function create(deps) {
 | 
			
		||||
              else {
 | 
			
		||||
                console.error("[acme-v2] (z) bad challenge state:");
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              return Promise.reject(new Error("[acme-v2] bad challenge state"));
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -276,6 +294,13 @@ function create(deps) {
 | 
			
		||||
          //console.log('\n[DEBUG] stop to fix things\n'); return;
 | 
			
		||||
 | 
			
		||||
          function post() {
 | 
			
		||||
            var jws = RSA.signJws(
 | 
			
		||||
              options.accountKeypair
 | 
			
		||||
            , undefined
 | 
			
		||||
            , { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid }
 | 
			
		||||
            , new Buffer(payload)
 | 
			
		||||
            );
 | 
			
		||||
            me._nonce = null;
 | 
			
		||||
            return request({
 | 
			
		||||
              method: 'POST'
 | 
			
		||||
            , url: ch.url
 | 
			
		||||
@ -285,17 +310,19 @@ function create(deps) {
 | 
			
		||||
              me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
              console.log('respond to challenge: resp.body:');
 | 
			
		||||
              console.log(resp.body);
 | 
			
		||||
          return wait().then(pollStatus);
 | 
			
		||||
              return wait().then(pollStatus).then(resolve, reject);
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return wait(20 * 1000).then(post);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , _finalizeOrder: function (options, validatedDomains) {
 | 
			
		||||
      console.log('finalizeOrder:');
 | 
			
		||||
      var me = this;
 | 
			
		||||
 | 
			
		||||
      var csr = RSA.generateCsrWeb64(options.certificateKeypair, validatedDomains);
 | 
			
		||||
      var csr = RSA.generateCsrWeb64(options.domainKeypair, validatedDomains);
 | 
			
		||||
      var body = { csr: csr };
 | 
			
		||||
      var payload = JSON.stringify(body);
 | 
			
		||||
 | 
			
		||||
@ -306,14 +333,15 @@ function create(deps) {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function pollCert() {
 | 
			
		||||
        //var payload = JSON.stringify(body, null, 2);
 | 
			
		||||
        var jws = RSA.signJws(
 | 
			
		||||
          options.keypair
 | 
			
		||||
        , new Buffer(payload)
 | 
			
		||||
          options.accountKeypair
 | 
			
		||||
        , undefined
 | 
			
		||||
        , { nonce: me._nonce, alg: 'RS256', url: me._finalize, kid: me._kid }
 | 
			
		||||
        , new Buffer(payload)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        console.log('finalize:', me._finalize);
 | 
			
		||||
        me._nonce = null;
 | 
			
		||||
        return request({
 | 
			
		||||
          method: 'POST'
 | 
			
		||||
        , url: me._finalize
 | 
			
		||||
@ -348,7 +376,7 @@ function create(deps) {
 | 
			
		||||
 | 
			
		||||
      return pollCert();
 | 
			
		||||
    }
 | 
			
		||||
  , _getCertificate: function (auth) {
 | 
			
		||||
  , _getCertificate: function () {
 | 
			
		||||
      var me = this;
 | 
			
		||||
      return request({ method: 'GET', url: me._certificate, json: true }).then(function (resp) {
 | 
			
		||||
        console.log('Certificate:');
 | 
			
		||||
@ -357,28 +385,37 @@ function create(deps) {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , getCertificate: function (options, cb) {
 | 
			
		||||
      console.log('[acme-v2] DEBUG get cert 1');
 | 
			
		||||
      var me = this;
 | 
			
		||||
 | 
			
		||||
      if (!options.challengeTypes) {
 | 
			
		||||
        if (!options.challengeType) {
 | 
			
		||||
          cb(new Error("challenge type must be specified"));
 | 
			
		||||
          return Promise.reject(new Error("challenge type must be specified"));
 | 
			
		||||
        }
 | 
			
		||||
        options.challengeTypes = [ options.challengeType ];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.log('[acme-v2] getCertificate');
 | 
			
		||||
      return me.getNonce().then(function () {
 | 
			
		||||
        var body = {
 | 
			
		||||
				identifiers: [
 | 
			
		||||
          { type: "dns" , value: "test.ppl.family" }
 | 
			
		||||
				/*
 | 
			
		||||
        , {	type: "dns" , value: "example.net" }
 | 
			
		||||
				*/
 | 
			
		||||
        ]
 | 
			
		||||
          identifiers: options.domains.map(function (hostname) {
 | 
			
		||||
            return { type: "dns" , value: hostname };
 | 
			
		||||
          })
 | 
			
		||||
          //, "notBefore": "2016-01-01T00:00:00Z"
 | 
			
		||||
          //, "notAfter": "2016-01-08T00:00:00Z"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var payload = JSON.stringify(body);
 | 
			
		||||
			//var payload = JSON.stringify(body, null, 2);
 | 
			
		||||
        var jws = RSA.signJws(
 | 
			
		||||
        options.keypair
 | 
			
		||||
      , new Buffer(payload)
 | 
			
		||||
          options.accountKeypair
 | 
			
		||||
        , undefined
 | 
			
		||||
        , { nonce: me._nonce, alg: 'RS256', url: me._directoryUrls.newOrder, kid: me._kid }
 | 
			
		||||
        , new Buffer(payload)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        console.log('\n[DEBUG] newOrder\n');
 | 
			
		||||
        me._nonce = null;
 | 
			
		||||
        return request({
 | 
			
		||||
          method: 'POST'
 | 
			
		||||
        , url: me._directoryUrls.newOrder
 | 
			
		||||
@ -386,7 +423,7 @@ function create(deps) {
 | 
			
		||||
        , json: jws
 | 
			
		||||
        }).then(function (resp) {
 | 
			
		||||
          me._nonce = resp.toJSON().headers['replay-nonce'];
 | 
			
		||||
        var location = resp.toJSON().headers['location'];
 | 
			
		||||
          var location = resp.toJSON().headers.location;
 | 
			
		||||
          console.log(location); // the account id url
 | 
			
		||||
          console.log(resp.toJSON());
 | 
			
		||||
          //var body = JSON.parse(resp.body);
 | 
			
		||||
@ -396,19 +433,25 @@ function create(deps) {
 | 
			
		||||
          //console.log('[DEBUG] finalize:', me._finalize); return;
 | 
			
		||||
 | 
			
		||||
          //return resp.body;
 | 
			
		||||
        return Promise.all(me._authorizations.map(function (auth) {
 | 
			
		||||
          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 Promise.all(me._authorizations.map(function (authUrl) {
 | 
			
		||||
            return me._getChallenges(options, authUrl).then(function (results) {
 | 
			
		||||
              // var domain = options.domains[i]; // results.identifier.value
 | 
			
		||||
              var chType = options.challengeTypes.filter(function (chType) {
 | 
			
		||||
                return results.challenges.some(function (ch) {
 | 
			
		||||
                  return ch.type === chType;
 | 
			
		||||
                });
 | 
			
		||||
              })[0];
 | 
			
		||||
              var challenge = results.challenges.filter(function (ch) {
 | 
			
		||||
                if (chType === ch.type) {
 | 
			
		||||
                  return ch;
 | 
			
		||||
                }
 | 
			
		||||
              })[0];
 | 
			
		||||
 | 
			
		||||
            return chp;
 | 
			
		||||
              if (!challenge) {
 | 
			
		||||
                return Promise.reject(new Error("Server didn't offer any challenge we can handle."));
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              return me._postChallenge(options, results.identifier, challenge);
 | 
			
		||||
            });
 | 
			
		||||
          })).then(function () {
 | 
			
		||||
            var validatedDomains = body.identifiers.map(function (ident) {
 | 
			
		||||
@ -417,7 +460,8 @@ function create(deps) {
 | 
			
		||||
 | 
			
		||||
            return me._finalizeOrder(options, validatedDomains);
 | 
			
		||||
          }).then(function () {
 | 
			
		||||
          return me._getCertificate();
 | 
			
		||||
            return me._getCertificate().then(function (result) { cb(null, result); return result; }, cb);
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
@ -425,23 +469,9 @@ function create(deps) {
 | 
			
		||||
  return acme2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var RSA = require('rsa-compat').RSA;
 | 
			
		||||
var acme2 = create();
 | 
			
		||||
acme2.getAcmeUrls().then(function (body) {
 | 
			
		||||
  console.log(body);
 | 
			
		||||
  acme2.getNonce().then(function (nonce) {
 | 
			
		||||
    console.log(nonce);
 | 
			
		||||
 | 
			
		||||
		var options = {
 | 
			
		||||
			email: 'coolaj86@gmail.com'
 | 
			
		||||
		, 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);
 | 
			
		||||
    	acme2.getCertificate(options, function () {
 | 
			
		||||
				console.log('got cert');
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
module.exports.ACME = {
 | 
			
		||||
  create: create
 | 
			
		||||
};
 | 
			
		||||
Object.keys(defaults).forEach(function (key) {
 | 
			
		||||
  module.exports.ACME[key] = defaults[key];
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "acme-v2",
 | 
			
		||||
  "version": "0.0.2",
 | 
			
		||||
  "version": "0.6.0",
 | 
			
		||||
  "description": "A framework for building letsencrypt clients (and other ACME v2 clients), forked from le-acme-core.js.",
 | 
			
		||||
  "main": "node.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@ -23,6 +23,6 @@
 | 
			
		||||
  "license": "(MIT OR Apache-2.0)",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "request": "^2.85.0",
 | 
			
		||||
    "rsa-compat": "^1.2.7"
 | 
			
		||||
    "rsa-compat": "^1.3.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user