Compare commits
	
		
			41 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2fd9678ab5 | |||
|  | 7a6c2ae573 | ||
|  | 4758dc2bd2 | ||
| d28d82130c | |||
|  | 3a41c3006c | ||
| bfe1737b9b | |||
|  | 9172d4c98e | ||
|  | 530b25f691 | ||
|  | d85f4070f3 | ||
|  | 51bcc1f20a | ||
|  | f79c62032c | ||
|  | 3ed2d45d3d | ||
|  | d0e20a44cd | ||
|  | 10978ab99a | ||
|  | 72fb7b7c07 | ||
|  | 72646ced80 | ||
|  | 9f01021948 | ||
|  | f63070ce54 | ||
|  | 6126222e8f | ||
|  | 7288d14fac | ||
|  | 681c0edc71 | ||
|  | f350ae44c1 | ||
|  | 6b1b168e5a | ||
|  | a97c5933d6 | ||
|  | a8b9817415 | ||
|  | fe635a965c | ||
|  | 8436b615cb | ||
|  | fbaa77cb4c | ||
|  | 528cec03a8 | ||
|  | e3d4add0b9 | ||
|  | 218497ab0e | ||
|  | 764c614940 | ||
|  | 80613b98e2 | ||
|  | 4050bd2a82 | ||
|  | 17df564f69 | ||
|  | 60e4ed8f7b | ||
|  | 420351da62 | ||
|  | 903ebf0491 | ||
|  | 4c7c21a751 | ||
|  | 01f283b7fd | ||
|  | c1513fe120 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -29,3 +29,5 @@ build/Release | ||||
| # Dependency directory | ||||
| # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git | ||||
| node_modules | ||||
| .idea | ||||
| .DS_Store | ||||
|  | ||||
							
								
								
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							| @ -1,3 +1,3 @@ | ||||
| ISRG | ||||
| Anatol Sommer <anatol@anatol.at> | ||||
| AJ ONeal <aj@daplie.com> (https://daplie.com/) | ||||
| AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/) | ||||
|  | ||||
							
								
								
									
										102
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								README.md
									
									
									
									
									
								
							| @ -1,4 +1,8 @@ | ||||
| # letiny-core | ||||
| # le-acme-core | ||||
| 
 | ||||
| Looking for **letiny-core**? Check the [v1.x branch](https://git.coolaj86.com/coolaj86/le-acme-core.js/tree/v1.x). | ||||
| 
 | ||||
| <!-- rename to le-acme-core --> | ||||
| 
 | ||||
| A framework for building letsencrypt clients, forked from `letiny`. | ||||
| 
 | ||||
| @ -9,19 +13,44 @@ Supports all of: | ||||
|   * browser WebCrypto (not implemented, but... Let's Encrypt over WebRTC anyone?) | ||||
|   * any javascript implementation | ||||
| 
 | ||||
| # NEW: Let's Encrypt v2 Support | ||||
| Let's Encrypt v2 (aka ACME v2 or ACME draft 11) is available in [acme-v2.js](https://git.coolaj86.com/coolaj86/acme-v2.js) | ||||
| 
 | ||||
| ### These aren't the droids you're looking for | ||||
| 
 | ||||
| This is a library / framework for building letsencrypt clients. | ||||
| You probably want one of these pre-built clients instead: | ||||
| 
 | ||||
|   * [`letsencrypt`](https://github.com/Daplie/node-letsencrypt) (compatible with the official client) | ||||
|   * [`letsencrypt`](https://git.coolaj86.com/coolaj86/greenlock.js) (compatible with the official client) | ||||
|   * `letiny` (lightweight client cli) | ||||
|   * [`letsencrypt-express`](https://github.com/Daplie/letsencrypt-express) (automatic https for express) | ||||
|   * [`letsencrypt-express`](https://git.coolaj86.com/coolaj86/greenlock-express.js) (automatic https for express) | ||||
| 
 | ||||
| ## Install & Usage: | ||||
| 
 | ||||
| ```bash | ||||
| npm install --save letiny-core | ||||
| npm install --save le-acme-core | ||||
| ``` | ||||
| 
 | ||||
| To use the default dependencies: | ||||
| 
 | ||||
| ```javascript | ||||
| 'use strict'; | ||||
| 
 | ||||
| var ACME = require('le-acme-core').ACME.create(); | ||||
| ``` | ||||
| 
 | ||||
| For **testing** and **development**, you can also inject the dependencies you want to use: | ||||
| 
 | ||||
| ```javascript | ||||
| 'use strict'; | ||||
| 
 | ||||
| var ACME = require('le-acme-core').ACME.create({ | ||||
| , RSA: require('rsa-compat').RSA | ||||
| }); | ||||
| 
 | ||||
| ACME.getAcmeUrls(discoveryUrl, function (err, urls) { | ||||
|   console.log(urls); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| You will follow these steps to obtain certificates: | ||||
| @ -49,12 +78,12 @@ Note: use **YOUR EMAIL** and accept the terms of service (run `ddns --help` to s | ||||
| 
 | ||||
| <!-- TODO tutorial on ddns --> | ||||
| 
 | ||||
| Install letiny-core and its dependencies. **Note**: it's okay if you're on windows | ||||
| Install le-acme-core and its dependencies. **Note**: it's okay if you're on windows | ||||
| and `ursa` fails to compile. It'll still work. | ||||
| 
 | ||||
| ```bash | ||||
| git clone https://github.com/Daplie/letiny-core.git ~/letiny-core | ||||
| pushd ~/letiny-core | ||||
| git clone https://git.coolaj86.com/coolaj86/le-acme-core.js.git ~/le-acme-core | ||||
| pushd ~/le-acme-core | ||||
| 
 | ||||
| npm install | ||||
| ``` | ||||
| @ -73,7 +102,7 @@ The Goodies | ||||
| 
 | ||||
| ```javascript | ||||
| // Accounts | ||||
| LeCore.registerNewAccount(options, cb)        // returns "regr" registration data | ||||
| ACME.registerNewAccount(options, cb)        // returns "regr" registration data | ||||
| 
 | ||||
|     { newRegUrl: '<url>'                      //    no defaults, specify acmeUrls.newAuthz | ||||
|     , email: '<email>'                        //    valid email (server checks MX records) | ||||
| @ -84,7 +113,7 @@ LeCore.registerNewAccount(options, cb)        // returns "regr" registration dat | ||||
|     } | ||||
| 
 | ||||
| // Registration | ||||
| LeCore.getCertificate(options, cb)            // returns (err, pems={ privkey (key), cert, chain (ca) }) | ||||
| ACME.getCertificate(options, cb)            // returns (err, pems={ privkey (key), cert, chain (ca) }) | ||||
| 
 | ||||
|     { newAuthzUrl: '<url>'                    //    specify acmeUrls.newAuthz | ||||
|     , newCertUrl: '<url>'                     //    specify acmeUrls.newCert | ||||
| @ -102,49 +131,32 @@ LeCore.getCertificate(options, cb)            // returns (err, pems={ privkey (k | ||||
|     } | ||||
| 
 | ||||
| // Discovery URLs | ||||
| LeCore.getAcmeUrls(acmeDiscoveryUrl, cb)      // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert}) | ||||
| ACME.getAcmeUrls(acmeDiscoveryUrl, cb)      // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert}) | ||||
| ``` | ||||
| 
 | ||||
| Helpers & Stuff | ||||
| 
 | ||||
| ```javascript | ||||
| // Constants | ||||
| LeCore.productionServerUrl                // https://acme-v01.api.letsencrypt.org/directory | ||||
| LeCore.stagingServerUrl                   // https://acme-staging.api.letsencrypt.org/directory | ||||
| LeCore.acmeChallengePrefix                // /.well-known/acme-challenge/ | ||||
| LeCore.configDir                          // /etc/letsencrypt/ | ||||
| LeCore.logsDir                            // /var/log/letsencrypt/ | ||||
| LeCore.workDir                            // /var/lib/letsencrypt/ | ||||
| LeCore.knownEndpoints                     // new-authz, new-cert, new-reg, revoke-cert | ||||
| ACME.productionServerUrl                // https://acme-v01.api.letsencrypt.org/directory | ||||
| ACME.stagingServerUrl                   // https://acme-staging.api.letsencrypt.org/directory | ||||
| ACME.acmeChallengePrefix                // /.well-known/acme-challenge/ | ||||
| ACME.knownEndpoints                     // new-authz, new-cert, new-reg, revoke-cert | ||||
| 
 | ||||
| 
 | ||||
| // HTTP Client Helpers | ||||
| LeCore.Acme                               // Signs requests with JWK | ||||
| ACME.Acme                               // Signs requests with JWK | ||||
|     acme = new Acme(keypair)                // 'keypair' is an object with `privateKeyPem` and/or `privateKeyJwk` | ||||
|     acme.post(url, body, cb)                // POST with signature | ||||
|     acme.parseLinks(link)                   // (internal) parses 'link' header | ||||
|     acme.getNonce(url, cb)                  // (internal) HEAD request to get 'replay-nonce' strings | ||||
| ``` | ||||
| 
 | ||||
| For testing and development, you can also inject the dependencies you want to use: | ||||
| 
 | ||||
| ```javascript | ||||
| LeCore = LeCore.create({ | ||||
|   request: require('request') | ||||
| , RSA: rquire('rsa-compat').RSA | ||||
| }); | ||||
| 
 | ||||
| // now uses node `request` (could also use jQuery or Angular in the browser) | ||||
| LeCore.getAcmeUrls(discoveryUrl, function (err, urls) { | ||||
|   console.log(urls); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| ## Example | ||||
| 
 | ||||
| Below you'll find a stripped-down example. You can see the full example in the example folder. | ||||
| 
 | ||||
| * [example/](https://github.com/Daplie/letiny-core/blob/master/example/) | ||||
| * [example/](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/example/) | ||||
| 
 | ||||
| #### Register Account & Domain | ||||
| 
 | ||||
| @ -153,12 +165,12 @@ This is how you **register an ACME account** and **get an HTTPS certificate** | ||||
| ```javascript | ||||
| 'use strict'; | ||||
| 
 | ||||
| var LeCore = require('letiny-core'); | ||||
| var ACME = require('le-acme-core').ACME.create(); | ||||
| var RSA = require('rsa-compat').RSA; | ||||
| 
 | ||||
| var email = 'user@example.com';                   // CHANGE TO YOUR EMAIL | ||||
| var domains = 'example.com';                      // CHANGE TO YOUR DOMAIN | ||||
| var acmeDiscoveryUrl = LeCore.stagingServerUrl;   // CHANGE to production, when ready | ||||
| var acmeDiscoveryUrl = ACME.stagingServerUrl;   // CHANGE to production, when ready | ||||
| 
 | ||||
| var accountKeypair = null;                        // { privateKeyPem: null, privateKeyJwk: null }; | ||||
| var domainKeypair = null;                         // same as above | ||||
| @ -167,14 +179,14 @@ var acmeUrls = null; | ||||
| RSA.generateKeypair(2048, 65537, function (err, keypair) { | ||||
|     accountKeypair = keypair; | ||||
|     // ... | ||||
|     LeCore.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) { | ||||
|     ACME.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) { | ||||
|         // ... | ||||
|         runDemo(); | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| function runDemo() { | ||||
|     LeCore.registerNewAccount( | ||||
|     ACME.registerNewAccount( | ||||
|         { newRegUrl: acmeUrls.newReg | ||||
|         , email: email | ||||
|         , accountKeypair: accountKeypair | ||||
| @ -186,7 +198,7 @@ function runDemo() { | ||||
|         } | ||||
|       , function (err, regr) { | ||||
| 
 | ||||
|             LeCore.getCertificate( | ||||
|             ACME.getCertificate( | ||||
|                 { newAuthzUrl: acmeUrls.newAuthz | ||||
|                 , newCertUrl: acmeUrls.newCert | ||||
| 
 | ||||
| @ -214,7 +226,7 @@ function runDemo() { | ||||
| ``` | ||||
| 
 | ||||
| **But wait**, there's more! | ||||
| See [example/letsencrypt.js](https://github.com/Daplie/letiny-core/blob/master/example/letsencrypt.js) | ||||
| See [example/letsencrypt.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/example/letsencrypt.js) | ||||
| 
 | ||||
| #### Run a Server on 80, 443, and 5001 (https/tls) | ||||
| 
 | ||||
| @ -227,7 +239,7 @@ var http = require('http'); | ||||
| 
 | ||||
| 
 | ||||
| var LeCore = deps.LeCore; | ||||
| var httpsOptions = deps.httpsOptions; | ||||
| var tlsOptions = deps.tlsOptions; | ||||
| var challengeStore = deps.challengeStore; | ||||
| var certStore = deps.certStore; | ||||
| 
 | ||||
| @ -252,7 +264,7 @@ function acmeResponder(req, res) { | ||||
| // | ||||
| // Server | ||||
| // | ||||
| https.createServer(httpsOptions, acmeResponder).listen(5001, function () { | ||||
| https.createServer(tlsOptions, acmeResponder).listen(5001, function () { | ||||
|   console.log('Listening https on', this.address()); | ||||
| }); | ||||
| http.createServer(acmeResponder).listen(80, function () { | ||||
| @ -261,7 +273,7 @@ http.createServer(acmeResponder).listen(80, function () { | ||||
| ``` | ||||
| 
 | ||||
| **But wait**, there's more! | ||||
| See [example/serve.js](https://github.com/Daplie/letiny-core/blob/master/example/serve.js) | ||||
| See [example/serve.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/example/serve.js) | ||||
| 
 | ||||
| #### Put some storage in place | ||||
| 
 | ||||
| @ -302,14 +314,14 @@ var certStore = { | ||||
| **But wait**, there's more! | ||||
| See | ||||
| 
 | ||||
| * [example/challenge-store.js](https://github.com/Daplie/letiny-core/blob/master/challenge-store.js) | ||||
| * [example/cert-store.js](https://github.com/Daplie/letiny-core/blob/master/cert-store.js) | ||||
| * [example/challenge-store.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/challenge-store.js) | ||||
| * [example/cert-store.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/cert-store.js) | ||||
| 
 | ||||
| ## Authors | ||||
| 
 | ||||
|   * ISRG | ||||
|   * Anatol Sommer  (https://github.com/anatolsommer) | ||||
|   * AJ ONeal <aj@daplie.com> (https://daplie.com) | ||||
|   * AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com) | ||||
| 
 | ||||
| ## Licence | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| //var LeCore = require('letiny-core');
 | ||||
| var LeCore = require('../'); | ||||
| var LeCore = require('../').ACME.create(); | ||||
| 
 | ||||
| var email = process.argv[2] || 'user@example.com';    // CHANGE TO YOUR EMAIL
 | ||||
| var domains = [process.argv[3] || 'example.com'];     // CHANGE TO YOUR DOMAIN
 | ||||
| @ -17,8 +17,8 @@ var certStore = require('./cert-store'); | ||||
| var serve = require('./serve'); | ||||
| var closer; | ||||
| 
 | ||||
| var accountPrivateKeyPem = null; | ||||
| var domainPrivateKeyPem = null; | ||||
| var accountKeypair = null; | ||||
| var domainKeypair = null; | ||||
| var acmeUrls = null; | ||||
| 
 | ||||
| 
 | ||||
| @ -44,14 +44,14 @@ function init() { | ||||
| 
 | ||||
| function getPrivateKeys(cb) { | ||||
|     console.log('Generating Account Keypair'); | ||||
|     console.log("(Note: if you're using forge and not ursa, this will take a long time"); | ||||
|     LeCore.leCrypto.generateRsaKeypair(2048, 65537, function (err, pems) { | ||||
|     const RSA = require('rsa-compat').RSA; | ||||
|     RSA.generateKeypair(2048, 65537, {}, function (err, pems) { | ||||
| 
 | ||||
|         accountPrivateKeyPem = pems.privateKeyPem; | ||||
|         accountKeypair = pems; | ||||
|         console.log('Generating Domain Keypair'); | ||||
|         LeCore.leCrypto.generateRsaKeypair(2048, 65537, function (err, pems) { | ||||
|         RSA.generateKeypair(2048, 65537, {}, function (err, pems2) { | ||||
| 
 | ||||
|             domainPrivateKeyPem = pems.privateKeyPem; | ||||
|             domainKeypair = pems2; | ||||
|             cb(); | ||||
|         }); | ||||
|     }); | ||||
| @ -62,7 +62,7 @@ function runDemo() { | ||||
|     LeCore.registerNewAccount( | ||||
|         { newRegUrl: acmeUrls.newReg | ||||
|         , email: email | ||||
|         , accountPrivateKeyPem: accountPrivateKeyPem | ||||
|         , accountKeypair: accountKeypair | ||||
|         , agreeToTerms: function (tosUrl, done) { | ||||
| 
 | ||||
|               // agree to the exact version of these terms
 | ||||
| @ -82,8 +82,8 @@ function runDemo() { | ||||
|                 { newAuthzUrl: acmeUrls.newAuthz | ||||
|                 , newCertUrl: acmeUrls.newCert | ||||
| 
 | ||||
|                 , domainPrivateKeyPem: domainPrivateKeyPem | ||||
|                 , accountPrivateKeyPem: accountPrivateKeyPem | ||||
|                 , domainKeypair: domainKeypair | ||||
|                 , accountKeypair: accountKeypair | ||||
|                 , domains: domains | ||||
| 
 | ||||
|                 , setChallenge: challengeStore.set | ||||
| @ -111,8 +111,7 @@ function runDemo() { | ||||
| //
 | ||||
| closer = serve.init({ | ||||
|   LeCore: LeCore | ||||
|   // needs a default key and cert chain, anything will do
 | ||||
| , httpsOptions: require('localhost.daplie.com-certificates') | ||||
| , tlsOptions: {} | ||||
| , challengeStore: challengeStore | ||||
| , certStore: certStore | ||||
| }); | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| @ -15,7 +15,7 @@ module.exports.init = function (deps) { | ||||
| 
 | ||||
| 
 | ||||
|   var LeCore = deps.LeCore; | ||||
|   var httpsOptions = deps.httpsOptions; | ||||
|   var tlsOptions = deps.tlsOptions || deps.httpsOptions; | ||||
|   var challengeStore = deps.challengeStore; | ||||
|   var certStore = deps.certStore; | ||||
| 
 | ||||
| @ -63,11 +63,11 @@ module.exports.init = function (deps) { | ||||
|   //
 | ||||
|   // Server
 | ||||
|   //
 | ||||
|   httpsOptions.SNICallback = certGetter; | ||||
|   https.createServer(httpsOptions, acmeResponder).listen(443, function () { | ||||
|   tlsOptions.SNICallback = certGetter; | ||||
|   https.createServer(tlsOptions, acmeResponder).listen(443, function () { | ||||
|     console.log('Listening https on', this.address()); | ||||
|   }); | ||||
|   https.createServer(httpsOptions, acmeResponder).listen(5001, function () { | ||||
|   https.createServer(tlsOptions, acmeResponder).listen(5001, function () { | ||||
|     console.log('Listening https on', this.address()); | ||||
|   }); | ||||
|   http.createServer(acmeResponder).listen(80, function () { | ||||
|  | ||||
| @ -8,10 +8,10 @@ | ||||
| 
 | ||||
| module.exports.create = function (deps) { | ||||
| 
 | ||||
|   var NOOP=function () { | ||||
|   var NOOP = function () { | ||||
|   }; | ||||
|   var log=NOOP; | ||||
|   var request=require('request'); | ||||
|   var log = NOOP; | ||||
|   var acmeRequest = deps.acmeRequest; | ||||
|   var RSA = deps.RSA; | ||||
|   var generateSignature = RSA.signJws; | ||||
| 
 | ||||
| @ -30,7 +30,7 @@ module.exports.create = function (deps) { | ||||
|   Acme.prototype.getNonce=function(url, cb) { | ||||
|     var self=this; | ||||
| 
 | ||||
|     request.head({ | ||||
|     acmeRequest.create().head({ | ||||
|       url:url, | ||||
|     }, function(err, res/*, body*/) { | ||||
|       if (err) { | ||||
| @ -73,10 +73,10 @@ module.exports.create = function (deps) { | ||||
| 
 | ||||
| //process.exit(1);
 | ||||
| //return;
 | ||||
|     return request.post({ | ||||
|       url:url, | ||||
|       body:signed, | ||||
|       encoding:null | ||||
|     return acmeRequest.create().post({ | ||||
|       url: url | ||||
|     , body: signed | ||||
|     , encoding: null | ||||
|     }, function(err, res, body) { | ||||
|       var parsed; | ||||
| 
 | ||||
|  | ||||
| @ -1,17 +0,0 @@ | ||||
| /*! | ||||
|  * letiny | ||||
|  * Copyright(c) 2015 Anatol Sommer <anatol@anatol.at> | ||||
|  * Some code used from https://github.com/letsencrypt/boulder/tree/master/test/js
 | ||||
|  * MPL 2.0 | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| exports.Acme = require('./acme-client'); | ||||
| exports.registerNewAccount = require('./register-new-account'); | ||||
| exports.getCertificate = require('./get-certificate'); | ||||
| exports.getCert=function (options, cb) { | ||||
|   exports.registerNewAccount(options, function () { | ||||
|     exports.getCertificate(options, cb); | ||||
|   }); | ||||
| }; | ||||
| @ -1,12 +1,12 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| module.exports.create = function (deps) { | ||||
|   var request = deps.request; | ||||
|   var acmeRequest = deps.acmeRequest; | ||||
|   var knownUrls = deps.LeCore.knownEndpoints; | ||||
| 
 | ||||
|   function getAcmeUrls(acmeDiscoveryUrl, cb) { | ||||
| @ -15,7 +15,7 @@ module.exports.create = function (deps) { | ||||
|     } | ||||
| 
 | ||||
|     // TODO check response header on request for cache time
 | ||||
|     return request({ | ||||
|     return acmeRequest.create()({ | ||||
|       url: acmeDiscoveryUrl | ||||
|     , encoding: 'utf8' | ||||
|     }, function (err, resp) { | ||||
| @ -30,18 +30,15 @@ module.exports.create = function (deps) { | ||||
|         try { | ||||
|           data = JSON.parse(data); | ||||
|         } catch(e) { | ||||
|           err.raw = data; | ||||
|           err.stack += '\n' + data; | ||||
|           e.raw = data; | ||||
|           e.url = acmeDiscoveryUrl; | ||||
|           e.stack += '\n\nresponse data:\n' | ||||
|             + data + '\n\nacmeDiscoveryUrl:' + acmeDiscoveryUrl; | ||||
|           cb(e); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       if (4 !== Object.keys(data).length) { | ||||
|         console.warn("This Let's Encrypt / ACME server has been updated with urls that this client doesn't understand"); | ||||
|         console.warn(data); | ||||
|       } | ||||
| 
 | ||||
|       if (!knownUrls.every(function (url) { | ||||
|         return data[url]; | ||||
|       })) { | ||||
| @ -54,6 +51,7 @@ module.exports.create = function (deps) { | ||||
|       , newCert: data['new-cert'] | ||||
|       , newReg: data['new-reg'] | ||||
|       , revokeCert: data['revoke-cert'] | ||||
|       , keyChange: data['key-change'] | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @ -17,8 +17,14 @@ function _toStandardBase64(str) { | ||||
|   return b64; | ||||
| } | ||||
| 
 | ||||
| function certBufferToPem(cert) { | ||||
|   cert = _toStandardBase64(cert.toString('base64')); | ||||
|   cert = cert.match(/.{1,64}/g).join('\r\n'); | ||||
|   return '-----BEGIN CERTIFICATE-----\r\n'+cert+'\r\n-----END CERTIFICATE-----\r\n'; | ||||
| } | ||||
| 
 | ||||
| module.exports.create = function (deps) { | ||||
|   var request=deps.request; | ||||
|   var acmeRequest = deps.acmeRequest; | ||||
|   var Acme = deps.Acme; | ||||
|   var RSA = deps.RSA; | ||||
| 
 | ||||
| @ -187,7 +193,7 @@ module.exports.create = function (deps) { | ||||
| 
 | ||||
|       if (authz.status==='pending') { | ||||
|         setTimeout(function() { | ||||
|           request({ | ||||
|           acmeRequest.create()({ | ||||
|             method: 'GET' | ||||
|           , url: state.authorizationUrl | ||||
|           }, function(err, res, body) { | ||||
| @ -272,7 +278,7 @@ module.exports.create = function (deps) { | ||||
| 
 | ||||
|       state.certificate=body; | ||||
|       certUrl=res.headers.location; | ||||
|       request({ | ||||
|       acmeRequest.create()({ | ||||
|         method: 'GET' | ||||
|       , url: certUrl | ||||
|       , encoding: null | ||||
| @ -304,7 +310,7 @@ module.exports.create = function (deps) { | ||||
| 
 | ||||
|     function downloadIssuerCert(links) { | ||||
|       log('Requesting issuer certificate...'); | ||||
|       request({ | ||||
|       acmeRequest.create()({ | ||||
|         method: 'GET' | ||||
|       , url: links.up | ||||
|       , encoding: null | ||||
| @ -321,7 +327,7 @@ module.exports.create = function (deps) { | ||||
|           return handleErr(err, 'Failed to fetch issuer certificate'); | ||||
|         } | ||||
| 
 | ||||
|         state.chainPem=certBufferToPem(body); | ||||
|         state.chainPem = certBufferToPem(body); | ||||
|         log('Requesting issuer certificate: done'); | ||||
|         done(); | ||||
|       }); | ||||
| @ -402,11 +408,5 @@ module.exports.create = function (deps) { | ||||
|     nextDomain(); | ||||
|   } | ||||
| 
 | ||||
|   function certBufferToPem(cert) { | ||||
|     cert=_toStandardBase64(cert.toString('base64')); | ||||
|     cert=cert.match(/.{1,64}/g).join('\r\n'); | ||||
|     return '-----BEGIN CERTIFICATE-----\r\n'+cert+'\r\n-----END CERTIFICATE-----\r\n'; | ||||
|   } | ||||
| 
 | ||||
|   return getCert; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										72
									
								
								lib/le-acme-request.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib/le-acme-request.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| /*! | ||||
|  * le-acme-core | ||||
|  * Author: Kelly Johnson | ||||
|  * Copyright 2017 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
|  */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| const request = require('request'); | ||||
| const pkgJSON = require('../package.json'); | ||||
| const version = pkgJSON.version; | ||||
| const os = require('os'); | ||||
| 
 | ||||
| const uaDefaults = { | ||||
|   pkg: `Greenlock/${version}` | ||||
|   , os: ` (${os.type()}; ${process.arch} ${os.platform()} ${os.release()})` | ||||
|   , node: ` Node.js/${process.version}` | ||||
|   , user: '' | ||||
| } | ||||
| 
 | ||||
| let currentUAProps; | ||||
| 
 | ||||
| function getUaString() { | ||||
|   let userAgent = ''; | ||||
|   for (let key in currentUAProps) { | ||||
|     userAgent += currentUAProps[key]; | ||||
|   } | ||||
|   return userAgent.trim(); | ||||
| } | ||||
| 
 | ||||
| function getRequest() { | ||||
|   return request.defaults({ | ||||
|     headers: { | ||||
|       'User-Agent': getUaString() | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function resetUa() { | ||||
|   currentUAProps = {}; | ||||
|   for (let key in uaDefaults) { | ||||
|     currentUAProps[key] = uaDefaults[key]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function addUaString(string) { | ||||
|   currentUAProps.user += ` ${string}`; | ||||
| } | ||||
| 
 | ||||
| function omitUaProperties(opts) { | ||||
|   if (opts.all) { | ||||
|     currentUAProps = {}; | ||||
|   } else { | ||||
|     for (let key in opts) { | ||||
|       currentUAProps[key] = ''; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // Set our UA to begin with
 | ||||
| resetUa(); | ||||
| 
 | ||||
| module.exports = { | ||||
|   create: function create() { | ||||
|     // get deps and modify here if need be
 | ||||
|     return getRequest(); | ||||
|   } | ||||
|   , addUaString: addUaString | ||||
|   , omitUaProperties: omitUaProperties | ||||
|   , resetUa: resetUa | ||||
|   , getUaString: getUaString | ||||
| }; | ||||
							
								
								
									
										12
									
								
								lib/node.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								lib/node.js
									
									
									
									
									
								
							| @ -1,12 +0,0 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var request = require('request'); | ||||
| var RSA = require('rsa-compat').RSA; | ||||
| 
 | ||||
| module.exports.request = request; | ||||
| module.exports.RSA = RSA; | ||||
| @ -8,7 +8,7 @@ | ||||
| 'use strict'; | ||||
| module.exports.create = function (deps) { | ||||
|   var NOOP=function () {}, log=NOOP; | ||||
|   var request=deps.request; | ||||
|   var acmeRequest = deps.acmeRequest; | ||||
|   var RSA = deps.RSA; | ||||
|   var Acme = deps.Acme; | ||||
| 
 | ||||
| @ -24,7 +24,11 @@ module.exports.create = function (deps) { | ||||
|     function getTerms(err, res) { | ||||
|       var links; | ||||
| 
 | ||||
|       if (err || Math.floor(res.statusCode/100)!==2) { | ||||
|       if (err) { | ||||
|         return handleErr(err, 'Registration request failed: ' + err.toString()); | ||||
|       } | ||||
| 
 | ||||
|       if (Math.floor(res.statusCode/100)!==2) { | ||||
|         return handleErr(err, 'Registration request failed: ' + res.body.toString('utf8')); | ||||
|       } | ||||
| 
 | ||||
| @ -51,7 +55,7 @@ module.exports.create = function (deps) { | ||||
|           state.agreeTerms = agree; | ||||
|           state.termsUrl=links['terms-of-service']; | ||||
|           log(state.termsUrl); | ||||
|           request.get(state.termsUrl, getAgreement); | ||||
|           acmeRequest.create().get(state.termsUrl, getAgreement); | ||||
|         }); | ||||
|       } else { | ||||
|         cb(null, null); | ||||
|  | ||||
							
								
								
									
										57
									
								
								node.js
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								node.js
									
									
									
									
									
								
							| @ -1,30 +1,53 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var defaults = { | ||||
|   productionServerUrl:    'https://acme-v01.api.letsencrypt.org/directory' | ||||
| , stagingServerUrl:       'https://acme-staging.api.letsencrypt.org/directory' | ||||
| , acmeChallengePrefix:    '/.well-known/acme-challenge/' | ||||
| , knownEndpoints:         [ 'new-authz', 'new-cert', 'new-reg', 'revoke-cert', 'key-change' ] | ||||
| , challengeType:          'http-01' | ||||
| , rsaKeySize:             2048 | ||||
| }; | ||||
| 
 | ||||
| function create(deps) { | ||||
|   var LeCore = {}; | ||||
|   deps = deps || {}; | ||||
|   deps.LeCore = {}; | ||||
| 
 | ||||
|   // Note: these are NOT DEFAULTS
 | ||||
|   // They are de facto standards that you may
 | ||||
|   // or may not use in your implementation
 | ||||
|   LeCore.productionServerUrl                = "https://acme-v01.api.letsencrypt.org/directory"; | ||||
|   LeCore.stagingServerUrl                   = "https://acme-staging.api.letsencrypt.org/directory"; | ||||
|   LeCore.acmeChallengePrefix                = "/.well-known/acme-challenge/"; | ||||
|   LeCore.knownEndpoints                     = [ 'new-authz', 'new-cert', 'new-reg', 'revoke-cert' ]; | ||||
|   Object.keys(defaults).forEach(function (key) { | ||||
|     deps[key] = defaults[key]; | ||||
|     deps.LeCore[key] = defaults[key]; | ||||
|   }); | ||||
| 
 | ||||
|   deps.LeCore = LeCore; | ||||
|   deps.Acme = LeCore.Acme = require('./lib/acme-client').create(deps); | ||||
|   deps.RSA = deps.RSA || require('rsa-compat').RSA; | ||||
|   deps.acmeRequest = require('./lib/le-acme-request'); | ||||
|   deps.Acme = require('./lib/acme-client').create(deps); | ||||
| 
 | ||||
|   LeCore.getAcmeUrls = require('./lib/get-acme-urls').create(deps); | ||||
|   LeCore.registerNewAccount = require('./lib/register-new-account').create(deps); | ||||
|   LeCore.getCertificate = require('./lib/get-certificate').create(deps); | ||||
|   deps.LeCore.Acme = deps.Acme; | ||||
|   deps.LeCore.acmeRequest = deps.acmeRequest; | ||||
|   deps.LeCore.getAcmeUrls = require('./lib/get-acme-urls').create(deps); | ||||
|   deps.LeCore.registerNewAccount = require('./lib/register-new-account').create(deps); | ||||
|   deps.LeCore.getCertificate = require('./lib/get-certificate').create(deps); | ||||
|   deps.LeCore.getOptions = function () { | ||||
|     var defs = {}; | ||||
| 
 | ||||
|   return LeCore; | ||||
|     Object.keys(defaults).forEach(function (key) { | ||||
|       defs[key] = defs[deps] || defaults[key]; | ||||
|     }); | ||||
| 
 | ||||
|     return defs; | ||||
|   }; | ||||
| 
 | ||||
|   return deps.LeCore; | ||||
| } | ||||
| 
 | ||||
| module.exports = create(require('./lib/node')); | ||||
| module.exports.create = create; | ||||
| // TODO make this the official usage
 | ||||
| module.exports.ACME = { create: create }; | ||||
| 
 | ||||
| Object.keys(defaults).forEach(function (key) { | ||||
|   module.exports.ACME[key] = defaults[key]; | ||||
| }); | ||||
|  | ||||
							
								
								
									
										30
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								package.json
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "letiny-core", | ||||
|   "version": "2.0.3", | ||||
|   "name": "le-acme-core", | ||||
|   "version": "2.1.4", | ||||
|   "description": "A framework for building letsencrypt clients, forked from letiny", | ||||
|   "main": "node.js", | ||||
|   "browser": "browser.js", | ||||
| @ -8,36 +8,34 @@ | ||||
|     "example": "example", | ||||
|     "test": "test" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "test": "node example/letsencrypt.js" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git+https://github.com/Daplie/letiny-core.git" | ||||
|     "url": "git+https://git.coolaj86.com/coolaj86/le-acme-core.js.git" | ||||
|   }, | ||||
|   "license": "MPL-2.0", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/Daplie/letiny-core/issues" | ||||
|     "url": "https://git.coolaj86.com/coolaj86/le-acme-core.js/issues" | ||||
|   }, | ||||
|   "homepage": "https://github.com/Daplie/letiny-core#readme", | ||||
|   "homepage": "https://git.coolaj86.com/coolaj86/le-acme-core.js#readme", | ||||
|   "keywords": [ | ||||
|     "le-acme", | ||||
|     "le-acme-", | ||||
|     "tiny", | ||||
|     "acme", | ||||
|     "letsencrypt", | ||||
|     "client", | ||||
|     "pem", | ||||
|     "jwk", | ||||
|     "pfx" | ||||
|   ], | ||||
|   "dependencies": { | ||||
|     "node-forge": "^0.6.38", | ||||
|     "request": "^2.55.0", | ||||
|     "rsa-compat": "^1.2.3" | ||||
|   }, | ||||
|   "optionalDependencies": { | ||||
|     "ursa": "^0.9.1" | ||||
|     "request": "^2.74.0", | ||||
|     "rsa-compat": "^1.3.2" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "mocha": "^2.3.3", | ||||
|     "better-assert": "^1.0.2" | ||||
|     "better-assert": "^1.0.2", | ||||
|     "chai": "^3.5.0", | ||||
|     "chai-string": "^1.3.0", | ||||
|     "request-debug": "^0.2.0" | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										74
									
								
								test/test-request.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								test/test-request.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /*! | ||||
|  * le-acme-core | ||||
|  * Author: Kelly Johnson | ||||
|  * Copyright 2017 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
|  */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| const acmeRequest = require('../lib/le-acme-request'); | ||||
| const debugRequest = require('request-debug'); | ||||
| const chai = require('chai'); | ||||
| chai.use(require('chai-string')); | ||||
| const expect = chai.expect; | ||||
| 
 | ||||
| const productId = 'Greenlock'; | ||||
| const UA = 'User-Agent'; | ||||
| 
 | ||||
| function checkRequest(req, done, tester) { | ||||
|   debugRequest(req, function dbg(type, data, r) { | ||||
|     if (type !== 'request') return;  // Only interested in the request
 | ||||
|     expect(data.headers).to.have.property(UA); | ||||
|     let uaString = data.headers[UA]; | ||||
|     tester(uaString); | ||||
|     req.stopDebugging(); | ||||
|     done(); | ||||
|   }); | ||||
|   req('http://www.google.com', function (error, response, body) { | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| describe('le-acme-request', function () { | ||||
| 
 | ||||
|   beforeEach(function () { | ||||
|     acmeRequest.resetUa(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should build User-Agent string', function () { | ||||
|     let uaString = acmeRequest.getUaString(); | ||||
|     expect(uaString).to.startsWith(productId); | ||||
|   }); | ||||
| 
 | ||||
|   it('should have proper User-Agent in request', function (done) { | ||||
|     let request = acmeRequest.create(); | ||||
|     checkRequest(request, done, function (uaString) { | ||||
|       expect(uaString).to.startsWith(productId); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should add custom string to User Agent', function (done) { | ||||
|     let testStr = 'check it'; | ||||
|     acmeRequest.addUaString(testStr); | ||||
|     let request = acmeRequest.create(); | ||||
|     checkRequest(request, done, function (uaString) { | ||||
|       // Added space to ensure str was properly appended
 | ||||
|       expect(uaString).to.endsWith(` ${testStr}`); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should remove all items from User Agent', function (done) { | ||||
|     acmeRequest.omitUaProperties({all: true}); | ||||
|     let request = acmeRequest.create(); | ||||
|     checkRequest(request, done, function (uaString) { | ||||
|       expect(uaString).to.be.empty; | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should remove one item from User Agent', function (done) { | ||||
|     acmeRequest.omitUaProperties({pkg: true}); | ||||
|     const request = acmeRequest.create(); | ||||
|     checkRequest(request, done, function (uaString) { | ||||
|       expect(uaString).to.not.have.string(productId); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @ -1,5 +1,5 @@ | ||||
| var forge=require('node-forge'), assert=require('better-assert'), fs=require('fs'), | ||||
|   letiny=require('../lib/client'), config=require('./config.json'), | ||||
|   letiny=require('../'), config=require('./config.json'), | ||||
|   res, newReg='https://acme-staging.api.letsencrypt.org/acme/new-reg'; | ||||
| 
 | ||||
| config.newReg=config.newReg || newReg; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user