mirror of
				https://github.com/therootcompany/acme.js.git
				synced 2024-11-16 17:29:00 +00:00 
			
		
		
		
	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