forked from root/acme.js
		
	v1.5.0: perform full test challenge first
This commit is contained in:
		
							parent
							
								
									85a38f7b54
								
							
						
					
					
						commit
						d802fb4957
					
				@ -193,6 +193,8 @@ ACME.challengePrefixes['dns-01']              // '_acme-challenge'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* v1.5
 | 
				
			||||||
 | 
					  * 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!
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										83
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								node.js
									
									
									
									
									
								
							@ -16,6 +16,9 @@ ACME.splitPemChain = function splitPemChain(str) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
 | 
				
			||||||
 | 
					// dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
 | 
				
			||||||
ACME.challengePrefixes = {
 | 
					ACME.challengePrefixes = {
 | 
				
			||||||
  'http-01': '/.well-known/acme-challenge'
 | 
					  'http-01': '/.well-known/acme-challenge'
 | 
				
			||||||
, 'dns-01': '_acme-challenge'
 | 
					, 'dns-01': '_acme-challenge'
 | 
				
			||||||
@ -255,6 +258,37 @@ ACME._wait = function wait(ms) {
 | 
				
			|||||||
    setTimeout(resolve, (ms || 1100));
 | 
					    setTimeout(resolve, (ms || 1100));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ACME._testChallenges = function (me, options) {
 | 
				
			||||||
 | 
					  if (me.skipChallengeTest) {
 | 
				
			||||||
 | 
					    return Promise.resolve();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Promise.all(options.domains.map(function (identifierValue) {
 | 
				
			||||||
 | 
					    // TODO we really only need one to pass, not all to pass
 | 
				
			||||||
 | 
					    return Promise.all(options.challengeTypes.map(function (chType) {
 | 
				
			||||||
 | 
					      var chToken = require('crypto').randomBytes(16).toString('hex');
 | 
				
			||||||
 | 
					      var thumbprint = me.RSA.thumbprint(options.accountKeypair);
 | 
				
			||||||
 | 
					      var keyAuthorization = chToken + '.' + thumbprint;
 | 
				
			||||||
 | 
					      var auth = {
 | 
				
			||||||
 | 
					        identifier: { type: "dns", value: identifierValue }
 | 
				
			||||||
 | 
					      , hostname: identifierValue
 | 
				
			||||||
 | 
					      , type: chType
 | 
				
			||||||
 | 
					      , token: chToken
 | 
				
			||||||
 | 
					      , thumbprint: thumbprint
 | 
				
			||||||
 | 
					      , keyAuthorization: keyAuthorization
 | 
				
			||||||
 | 
					      , dnsAuthorization: me.RSA.utils.toWebsafeBase64(
 | 
				
			||||||
 | 
					          require('crypto').createHash('sha256').update(keyAuthorization).digest('base64')
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return ACME._setChallenge(me, options, auth).then(function () {
 | 
				
			||||||
 | 
					        return ACME.challengeTests[chType](me, auth);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
 | 
					// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
 | 
				
			||||||
ACME._postChallenge = function (me, options, identifier, ch) {
 | 
					ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			||||||
  var RETRY_INTERVAL = me.retryInterval || 1000;
 | 
					  var RETRY_INTERVAL = me.retryInterval || 1000;
 | 
				
			||||||
@ -279,7 +313,6 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return new Promise(function (resolve, reject) {
 | 
					 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
   POST /acme/authz/1234 HTTP/1.1
 | 
					   POST /acme/authz/1234 HTTP/1.1
 | 
				
			||||||
   Host: example.com
 | 
					   Host: example.com
 | 
				
			||||||
@ -333,7 +366,6 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); }
 | 
					    if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); }
 | 
				
			||||||
    return me._request({ method: 'GET', url: ch.url, json: true }).then(function (resp) {
 | 
					    return me._request({ method: 'GET', url: ch.url, json: true }).then(function (resp) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
      if ('processing' === resp.body.status) {
 | 
					      if ('processing' === resp.body.status) {
 | 
				
			||||||
        if (me.debug) { console.debug('poll: again'); }
 | 
					        if (me.debug) { console.debug('poll: again'); }
 | 
				
			||||||
        return ACME._wait(RETRY_INTERVAL).then(pollStatus);
 | 
					        return ACME._wait(RETRY_INTERVAL).then(pollStatus);
 | 
				
			||||||
@ -342,10 +374,10 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
      // This state should never occur
 | 
					      // This state should never occur
 | 
				
			||||||
      if ('pending' === resp.body.status) {
 | 
					      if ('pending' === resp.body.status) {
 | 
				
			||||||
        if (count >= MAX_PEND) {
 | 
					        if (count >= MAX_PEND) {
 | 
				
			||||||
            return ACME._wait(RETRY_INTERVAL).then(deactivate).then(testChallenge);
 | 
					          return ACME._wait(RETRY_INTERVAL).then(deactivate).then(respondToChallenge);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (me.debug) { console.debug('poll: again'); }
 | 
					        if (me.debug) { console.debug('poll: again'); }
 | 
				
			||||||
          return ACME._wait(RETRY_INTERVAL).then(testChallenge);
 | 
					        return ACME._wait(RETRY_INTERVAL).then(respondToChallenge);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if ('valid' === resp.body.status) {
 | 
					      if ('valid' === resp.body.status) {
 | 
				
			||||||
@ -403,48 +435,35 @@ ACME._postChallenge = function (me, options, identifier, ch) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function testChallenge() {
 | 
					  return ACME._setChallenge(me, options, auth).then(respondToChallenge);
 | 
				
			||||||
      // TODO put check dns / http checks here?
 | 
					};
 | 
				
			||||||
      // http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
 | 
					ACME._setChallenge = function (me, options, auth) {
 | 
				
			||||||
      // dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
 | 
					  return new Promise(function (resolve, reject) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (me.debug) {console.debug('\n[DEBUG] postChallenge\n'); }
 | 
					 | 
				
			||||||
      //if (me.debug) console.debug('\n[DEBUG] stop to fix things\n'); return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return ACME._wait(RETRY_INTERVAL).then(function () {
 | 
					 | 
				
			||||||
        if (!me.skipChallengeTest) {
 | 
					 | 
				
			||||||
          return ACME.challengeTests[ch.type](me, auth);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }).then(respondToChallenge);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      if (1 === options.setChallenge.length) {
 | 
					      if (1 === options.setChallenge.length) {
 | 
				
			||||||
        options.setChallenge(auth).then(testChallenge).then(resolve, reject);
 | 
					        options.setChallenge(auth).then(resolve).catch(reject);
 | 
				
			||||||
      } else if (2 === options.setChallenge.length) {
 | 
					      } else if (2 === options.setChallenge.length) {
 | 
				
			||||||
        options.setChallenge(auth, function (err) {
 | 
					        options.setChallenge(auth, function (err) {
 | 
				
			||||||
          if(err) {
 | 
					          if(err) { reject(err); } else { resolve(); }
 | 
				
			||||||
            reject(err);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            testChallenge().then(resolve, reject);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        var challengeCb = function(err) {
 | 
					        var challengeCb = function(err) {
 | 
				
			||||||
          if(err) {
 | 
					          if(err) { reject(err); } else { resolve(); }
 | 
				
			||||||
            reject(err);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            testChallenge().then(resolve, reject);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        // for backwards compat adding extra keys without changing params length
 | 
				
			||||||
        Object.keys(auth).forEach(function (key) {
 | 
					        Object.keys(auth).forEach(function (key) {
 | 
				
			||||||
          challengeCb[key] = auth[key];
 | 
					          challengeCb[key] = auth[key];
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        options.setChallenge(identifier.value, ch.token, keyAuthorization, challengeCb);
 | 
					        options.setChallenge(auth.identifier.value, auth.token, auth.keyAuthorization, challengeCb);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch(e) {
 | 
					    } catch(e) {
 | 
				
			||||||
      reject(e);
 | 
					      reject(e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }).then(function () {
 | 
				
			||||||
 | 
					    // TODO: Do we still need this delay? Or shall we leave it to plugins to account for themselves?
 | 
				
			||||||
 | 
					    var DELAY = me.setChallengeWait || 500;
 | 
				
			||||||
 | 
					    if (me.debug) { console.debug('\n[DEBUG] waitChallengeDelay %s\n', DELAY); }
 | 
				
			||||||
 | 
					    return ACME._wait(DELAY);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
ACME._finalizeOrder = function (me, options, validatedDomains) {
 | 
					ACME._finalizeOrder = function (me, options, validatedDomains) {
 | 
				
			||||||
@ -548,6 +567,7 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return ACME._testChallenges(me, options).then(function () {
 | 
				
			||||||
    if (me.debug) { console.debug('[acme-v2] certificates.create'); }
 | 
					    if (me.debug) { console.debug('[acme-v2] certificates.create'); }
 | 
				
			||||||
    return ACME._getNonce(me).then(function () {
 | 
					    return ACME._getNonce(me).then(function () {
 | 
				
			||||||
      var body = {
 | 
					      var body = {
 | 
				
			||||||
@ -650,6 +670,7 @@ ACME._getCertificate = function (me, options) {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ACME.create = function create(me) {
 | 
					ACME.create = function create(me) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "acme-v2",
 | 
					  "name": "acme-v2",
 | 
				
			||||||
  "version": "1.3.1",
 | 
					  "version": "1.5.0",
 | 
				
			||||||
  "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",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user