mirror of
				https://github.com/therootcompany/acme.js.git
				synced 2024-11-16 17:29:00 +00:00 
			
		
		
		
	more testing
This commit is contained in:
		
							parent
							
								
									1647818326
								
							
						
					
					
						commit
						33e10c77d8
					
				| @ -27,11 +27,12 @@ In progress | |||||||
| * Apr  5, 2018 - test wildcard | * Apr  5, 2018 - test wildcard | ||||||
| * Apr  5, 2018 - test two subdomains | * Apr  5, 2018 - test two subdomains | ||||||
| * Apr  5, 2018 - test subdomains and its wildcard | * 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 | ||||||
| 
 | 
 | ||||||
| Todo | Todo | ||||||
| 
 | 
 | ||||||
| * test http and dns challenges | * Apr  5, 2018 - appears that sometimes 'pending' status cannot be progressed to 'processing' nor 'deactivated' | ||||||
| * export http and dns challenge tests |  | ||||||
| * support ECDSA keys | * support ECDSA keys | ||||||
| 
 | 
 | ||||||
| ## Let's Encrypt Directory URLs | ## Let's Encrypt Directory URLs | ||||||
| @ -63,6 +64,9 @@ var ACME = require('acme-v2').ACME.create({ | |||||||
|   // used for overriding the default user-agent |   // used for overriding the default user-agent | ||||||
| , userAgent: 'My custom UA String' | , userAgent: 'My custom UA String' | ||||||
| , getUserAgentString: function (deps) { return 'My custom UA String'; } | , getUserAgentString: function (deps) { return 'My custom UA String'; } | ||||||
|  | 
 | ||||||
|  |   // don't try to validate challenges locally | ||||||
|  | , skipChallengeTest: false | ||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										222
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										222
									
								
								node.js
									
									
									
									
									
								
							| @ -11,9 +11,43 @@ var ACME = module.exports.ACME = {}; | |||||||
| ACME.acmeChallengePrefix = '/.well-known/acme-challenge/'; | ACME.acmeChallengePrefix = '/.well-known/acme-challenge/'; | ||||||
| ACME.acmeChallengeDnsPrefix = '_acme-challenge'; | ACME.acmeChallengeDnsPrefix = '_acme-challenge'; | ||||||
| ACME.acmeChallengePrefixes = { | ACME.acmeChallengePrefixes = { | ||||||
|   'http-01': '/.well-known/acme-challenge/' |   'http-01': '/.well-known/acme-challenge' | ||||||
| , 'dns-01': '_acme-challenge' | , 'dns-01': '_acme-challenge' | ||||||
| }; | }; | ||||||
|  | ACME.challengeTests = { | ||||||
|  |   'http-01': function (me, auth) { | ||||||
|  |     var url = 'http://' + auth.hostname + ACME.acmeChallengePrefixes['http-01'] + '/' + auth.token; | ||||||
|  |     return me._request({ url: url }).then(function (resp) { | ||||||
|  |       var err; | ||||||
|  | 
 | ||||||
|  |       if (auth.keyAuthorization === resp.body.toString('utf8').trim()) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       err = new Error("self check does not pass"); | ||||||
|  |       err.code = 'E_RETRY'; | ||||||
|  |       return Promise.reject(err); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | , 'dns-01': function (me, auth) { | ||||||
|  |     return me._dig({ | ||||||
|  |       type: 'TXT' | ||||||
|  |     , name: ACME.acmeChallengePrefixes['dns-01'] + '.' + auth.hostname | ||||||
|  |     }).then(function (ans) { | ||||||
|  |       var err; | ||||||
|  | 
 | ||||||
|  |       if (ans.answer.some(function (txt) { | ||||||
|  |         return auth.dnsAuthorization === txt.data[0]; | ||||||
|  |       })) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       err = new Error("self check does not pass"); | ||||||
|  |       err.code = 'E_RETRY'; | ||||||
|  |       return Promise.reject(err); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| ACME._getUserAgentString = function (deps) { | ACME._getUserAgentString = function (deps) { | ||||||
|   var uaDefaults = { |   var uaDefaults = { | ||||||
| @ -181,67 +215,103 @@ ACME._wait = function wait(ms) { | |||||||
| }; | }; | ||||||
| // 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 body = { }; |   var count = 0; | ||||||
| 
 |  | ||||||
|   var payload = JSON.stringify(body); |  | ||||||
| 
 | 
 | ||||||
|   var thumbprint = me.RSA.thumbprint(options.accountKeypair); |   var thumbprint = me.RSA.thumbprint(options.accountKeypair); | ||||||
|   var keyAuthorization = ch.token + '.' + thumbprint; |   var keyAuthorization = ch.token + '.' + thumbprint; | ||||||
|   //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 |   //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 | ||||||
|   //   /.well-known/acme-challenge/:token
 |   //   /.well-known/acme-challenge/:token
 | ||||||
|  |   var auth = { | ||||||
|  |     identifier: identifier | ||||||
|  |   , hostname: identifier.value | ||||||
|  |   , type: ch.type | ||||||
|  |   , token: ch.token | ||||||
|  |   , thumbprint: thumbprint | ||||||
|  |   , keyAuthorization: keyAuthorization | ||||||
|  |   , dnsAuthorization: me.RSA.utils.toWebsafeBase64( | ||||||
|  |       require('crypto').createHash('sha256').update(keyAuthorization).digest('base64') | ||||||
|  |     ) | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   return new Promise(function (resolve, reject) { |   return new Promise(function (resolve, reject) { | ||||||
|     function failChallenge(err) { |     /* | ||||||
|       if (err) { reject(err); return; } |      POST /acme/authz/1234 HTTP/1.1 | ||||||
|       testChallenge(); |      Host: example.com | ||||||
|  |      Content-Type: application/jose+json | ||||||
|  | 
 | ||||||
|  |      { | ||||||
|  |        "protected": base64url({ | ||||||
|  |          "alg": "ES256", | ||||||
|  |          "kid": "https://example.com/acme/acct/1", | ||||||
|  |          "nonce": "xWCM9lGbIyCgue8di6ueWQ", | ||||||
|  |          "url": "https://example.com/acme/authz/1234" | ||||||
|  |        }), | ||||||
|  |        "payload": base64url({ | ||||||
|  |          "status": "deactivated" | ||||||
|  |        }), | ||||||
|  |        "signature": "srX9Ji7Le9bjszhu...WTFdtujObzMtZcx4" | ||||||
|  |      } | ||||||
|  |      */ | ||||||
|  |     function deactivate() { | ||||||
|  |       var jws = me.RSA.signJws( | ||||||
|  |         options.accountKeypair | ||||||
|  |       , undefined | ||||||
|  |       , { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid } | ||||||
|  |       , new Buffer(JSON.stringify({ "status": "deactivated" })) | ||||||
|  |       ); | ||||||
|  |       me._nonce = null; | ||||||
|  |       return me._request({ | ||||||
|  |         method: 'POST' | ||||||
|  |       , url: ch.url | ||||||
|  |       , headers: { 'Content-Type': 'application/jose+json' } | ||||||
|  |       , json: jws | ||||||
|  |       }).then(function (resp) { | ||||||
|  |         console.log('[acme-v2.js] deactivate:'); | ||||||
|  |         console.log(resp.headers); | ||||||
|  |         console.log(resp.body); | ||||||
|  |         console.log(); | ||||||
|  | 
 | ||||||
|  |         me._nonce = resp.toJSON().headers['replay-nonce']; | ||||||
|  |         if (me.debug) { console.log('deactivate challenge: resp.body:'); } | ||||||
|  |         if (me.debug) { console.log(resp.body); } | ||||||
|  |         return ACME._wait(10 * 1000); | ||||||
|  |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function testChallenge() { |  | ||||||
|       // 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 pollStatus() { |     function pollStatus() { | ||||||
|  |       if (count >= 5) { | ||||||
|  |         return Promise.reject(new Error("[acme-v2] stuck in bad pending/processing state")); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       count += 1; | ||||||
|  | 
 | ||||||
|       if (me.debug) { console.log('\n[DEBUG] statusChallenge\n'); } |       if (me.debug) { console.log('\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) { | ||||||
|         console.error('poll: resp.body:'); |         console.error('poll: resp.body:'); | ||||||
|         console.error(resp.body); |         console.error(resp.body); | ||||||
| 
 | 
 | ||||||
|           if ('pending' === resp.body.status) { |         if ('processing' === resp.body.status) { | ||||||
|           if (me.debug) { console.log('poll: again'); } |           if (me.debug) { console.log('poll: again'); } | ||||||
|           return ACME._wait(1 * 1000).then(pollStatus); |           return ACME._wait(1 * 1000).then(pollStatus); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // This state should never occur
 | ||||||
|  |         if ('pending' === resp.body.status) { | ||||||
|  |           if (count >= 4) { | ||||||
|  |             return ACME._wait(1 * 1000).then(deactivate).then(testChallenge); | ||||||
|  |           } | ||||||
|  |           if (me.debug) { console.log('poll: again'); } | ||||||
|  |           return ACME._wait(1 * 1000).then(testChallenge); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if ('valid' === resp.body.status) { |         if ('valid' === resp.body.status) { | ||||||
|           if (me.debug) { console.log('poll: valid'); } |           if (me.debug) { console.log('poll: valid'); } | ||||||
|  | 
 | ||||||
|           try { |           try { | ||||||
|             if (1 === options.removeChallenge.length) { |             if (1 === options.removeChallenge.length) { | ||||||
|                 options.removeChallenge( |               options.removeChallenge(auth).then(function () {}, function () {}); | ||||||
|                   { identifier: identifier |  | ||||||
|                   , hostname: identifier.value |  | ||||||
|                   , type: ch.type |  | ||||||
|                   , token: ch.token |  | ||||||
|                   , thumbprint: thumbprint |  | ||||||
|                   , keyAuthorization: keyAuthorization |  | ||||||
|                   , dnsAuthorization: me.RSA.utils.toWebsafeBase64( |  | ||||||
|                       require('crypto').createHash('sha256').update(keyAuthorization).digest('base64') |  | ||||||
|                     ) |  | ||||||
|                   } |  | ||||||
|                 ).then(function () {}, function () {}); |  | ||||||
|             } else if (2 === options.removeChallenge.length) { |             } else if (2 === options.removeChallenge.length) { | ||||||
|                 options.removeChallenge( |               options.removeChallenge(auth, function (err) { return err; }); | ||||||
|                   { identifier: identifier |  | ||||||
|                   , hostname: identifier.value |  | ||||||
|                   , type: ch.type |  | ||||||
|                   , token: ch.token |  | ||||||
|                   , thumbprint: thumbprint |  | ||||||
|                   , keyAuthorization: keyAuthorization |  | ||||||
|                   , dnsAuthorization: me.RSA.utils.toWebsafeBase64( |  | ||||||
|                       require('crypto').createHash('sha256').update(keyAuthorization).digest('base64') |  | ||||||
|                     ) |  | ||||||
|                   } |  | ||||||
|                 , function (err) { return err; } |  | ||||||
|                 ); |  | ||||||
|             } else { |             } else { | ||||||
|               options.removeChallenge(identifier.value, ch.token, function () {}); |               options.removeChallenge(identifier.value, ch.token, function () {}); | ||||||
|             } |             } | ||||||
| @ -263,15 +333,12 @@ ACME._postChallenge = function (me, options, identifier, ch) { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|       if (me.debug) {console.log('\n[DEBUG] postChallenge\n'); } |     function respondToChallenge() { | ||||||
|       //console.log('\n[DEBUG] stop to fix things\n'); return;
 |  | ||||||
| 
 |  | ||||||
|       function post() { |  | ||||||
|       var jws = me.RSA.signJws( |       var jws = me.RSA.signJws( | ||||||
|         options.accountKeypair |         options.accountKeypair | ||||||
|       , undefined |       , undefined | ||||||
|       , { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid } |       , { nonce: me._nonce, alg: 'RS256', url: ch.url, kid: me._kid } | ||||||
|         , new Buffer(payload) |       , new Buffer(JSON.stringify({ })) | ||||||
|       ); |       ); | ||||||
|       me._nonce = null; |       me._nonce = null; | ||||||
|       return me._request({ |       return me._request({ | ||||||
| @ -280,6 +347,11 @@ ACME._postChallenge = function (me, options, identifier, ch) { | |||||||
|       , headers: { 'Content-Type': 'application/jose+json' } |       , headers: { 'Content-Type': 'application/jose+json' } | ||||||
|       , json: jws |       , json: jws | ||||||
|       }).then(function (resp) { |       }).then(function (resp) { | ||||||
|  |         console.log('[acme-v2.js] challenge accepted!'); | ||||||
|  |         console.log(resp.headers); | ||||||
|  |         console.log(resp.body); | ||||||
|  |         console.log(); | ||||||
|  | 
 | ||||||
|         me._nonce = resp.toJSON().headers['replay-nonce']; |         me._nonce = resp.toJSON().headers['replay-nonce']; | ||||||
|         if (me.debug) { console.log('respond to challenge: resp.body:'); } |         if (me.debug) { console.log('respond to challenge: resp.body:'); } | ||||||
|         if (me.debug) { console.log(resp.body); } |         if (me.debug) { console.log(resp.body); } | ||||||
| @ -287,37 +359,31 @@ ACME._postChallenge = function (me, options, identifier, ch) { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|       return ACME._wait(1 * 1000).then(post); |     function failChallenge(err) { | ||||||
|  |       if (err) { reject(err); return; } | ||||||
|  |       return testChallenge(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function testChallenge() { | ||||||
|  |       // 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))}}"
 | ||||||
|  | 
 | ||||||
|  |       if (me.debug) {console.log('\n[DEBUG] postChallenge\n'); } | ||||||
|  |       //console.log('\n[DEBUG] stop to fix things\n'); return;
 | ||||||
|  | 
 | ||||||
|  |       return ACME._wait(1 * 1000).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( |         options.setChallenge(auth).then(testChallenge, reject); | ||||||
|           { identifier: identifier |  | ||||||
|           , hostname: identifier.value |  | ||||||
|           , type: ch.type |  | ||||||
|           , token: ch.token |  | ||||||
|           , thumbprint: thumbprint |  | ||||||
|           , keyAuthorization: keyAuthorization |  | ||||||
|           , dnsAuthorization: me.RSA.utils.toWebsafeBase64( |  | ||||||
|               require('crypto').createHash('sha256').update(keyAuthorization).digest('base64') |  | ||||||
|             ) |  | ||||||
|           } |  | ||||||
|         ).then(testChallenge, reject); |  | ||||||
|       } else if (2 === options.setChallenge.length) { |       } else if (2 === options.setChallenge.length) { | ||||||
|         options.setChallenge( |         options.setChallenge(auth, failChallenge); | ||||||
|           { identifier: identifier |  | ||||||
|           , hostname: identifier.value |  | ||||||
|           , type: ch.type |  | ||||||
|           , token: ch.token |  | ||||||
|           , thumbprint: thumbprint |  | ||||||
|           , keyAuthorization: keyAuthorization |  | ||||||
|           , dnsAuthorization: me.RSA.utils.toWebsafeBase64( |  | ||||||
|               require('crypto').createHash('sha256').update(keyAuthorization).digest('base64') |  | ||||||
|             ) |  | ||||||
|           } |  | ||||||
|         , failChallenge |  | ||||||
|         ); |  | ||||||
|       } else { |       } else { | ||||||
|         options.setChallenge(identifier.value, ch.token, keyAuthorization, failChallenge); |         options.setChallenge(identifier.value, ch.token, keyAuthorization, failChallenge); | ||||||
|       } |       } | ||||||
| @ -388,7 +454,6 @@ ACME._getCertificate = function (me, options) { | |||||||
| 
 | 
 | ||||||
|   if (me.debug) { console.log('[acme-v2] certificates.create'); } |   if (me.debug) { console.log('[acme-v2] certificates.create'); } | ||||||
|   return ACME._getNonce(me).then(function () { |   return ACME._getNonce(me).then(function () { | ||||||
|     if (me.debug) { console.log("27 &#&#&#&#&#&#&&##&#&#&#&#&#&#&#&"); } |  | ||||||
|     var body = { |     var body = { | ||||||
|       identifiers: options.domains.map(function (hostname) { |       identifiers: options.domains.map(function (hostname) { | ||||||
|         return { type: "dns" , value: hostname }; |         return { type: "dns" , value: hostname }; | ||||||
| @ -484,6 +549,23 @@ ACME.create = function create(me) { | |||||||
|   me.acmeChallengePrefixes = ACME.acmeChallengePrefixes; |   me.acmeChallengePrefixes = ACME.acmeChallengePrefixes; | ||||||
|   me.RSA = me.RSA || require('rsa-compat').RSA; |   me.RSA = me.RSA || require('rsa-compat').RSA; | ||||||
|   me.request = me.request || require('request'); |   me.request = me.request || require('request'); | ||||||
|  |   me._dig = function (query) { | ||||||
|  |     // TODO use digd.js
 | ||||||
|  |     return new Promise(function (resolve, reject) { | ||||||
|  |       var dns = require('dns'); | ||||||
|  |       dns.resolveTxt(query.name, function (err, records) { | ||||||
|  |         if (err) { reject(err); return; } | ||||||
|  | 
 | ||||||
|  |         resolve({ | ||||||
|  |           answer: records.map(function (rr) { | ||||||
|  |             return { | ||||||
|  |               data: rr | ||||||
|  |             }; | ||||||
|  |           }) | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|   me.promisify = me.promisify || require('util').promisify; |   me.promisify = me.promisify || require('util').promisify; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ module.exports.run = function run(web, chType, email, accountKeypair, domainKeyp | |||||||
|         console.log(""); |         console.log(""); | ||||||
| 
 | 
 | ||||||
|         if ('http-01' === opts.type) { |         if ('http-01' === opts.type) { | ||||||
|           pathname = opts.hostname + acme2.acmeChallengePrefix + "/" + opts.token; |           pathname = opts.hostname + acme2.acmeChallengePrefixes['http-01'] + "/" + opts.token; | ||||||
|           console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'"); |           console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'"); | ||||||
|           console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'"); |           console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'"); | ||||||
|         } else if ('dns-01' === opts.type) { |         } else if ('dns-01' === opts.type) { | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ module.exports.run = function (web, chType, email, accountKeypair, domainKeypair | |||||||
|         agree(null, tosUrl); |         agree(null, tosUrl); | ||||||
|       } |       } | ||||||
|     , setChallenge: function (hostname, token, val, cb) { |     , setChallenge: function (hostname, token, val, cb) { | ||||||
|         var pathname = hostname + acme2.acmeChallengePrefix + "/" + token; |         var pathname = hostname + acme2.acmeChallengePrefix + token; | ||||||
|         console.log("Put the string '" + val + "' into a file at '" + pathname + "'"); |         console.log("Put the string '" + val + "' into a file at '" + pathname + "'"); | ||||||
|         console.log("echo '" + val + "' > '" + pathname + "'"); |         console.log("echo '" + val + "' > '" + pathname + "'"); | ||||||
|         console.log("\nThen hit the 'any' key to continue..."); |         console.log("\nThen hit the 'any' key to continue..."); | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ module.exports.run = function run(web, chType, email, accountKeypair, domainKeyp | |||||||
|           console.log(""); |           console.log(""); | ||||||
| 
 | 
 | ||||||
|           if ('http-01' === opts.type) { |           if ('http-01' === opts.type) { | ||||||
|             pathname = opts.hostname + acme2.acmeChallengePrefix + "/" + opts.token; |             pathname = opts.hostname + acme2.acmeChallengePrefixes['http-01'] + "/" + opts.token; | ||||||
|             console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'"); |             console.log("Put the string '" + opts.keyAuthorization + "' into a file at '" + pathname + "'"); | ||||||
|             console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'"); |             console.log("echo '" + opts.keyAuthorization + "' > '" + pathname + "'"); | ||||||
|           } else if ('dns-01' === opts.type) { |           } else if ('dns-01' === opts.type) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user