initial commit
This commit is contained in:
		
						commit
						df34cbf6c1
					
				
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | acme-v2.js | ||||||
|  | ========== | ||||||
|  | 
 | ||||||
|  | A framework for building letsencrypt clients (and other ACME v2 clients), forked from `le-acme-core.js`. | ||||||
|  | 
 | ||||||
|  | In progress | ||||||
|  | 
 | ||||||
|  | * get directory | ||||||
|  | * get nonce | ||||||
|  | * create account | ||||||
							
								
								
									
										187
									
								
								node.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								node.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | |||||||
|  | /*! | ||||||
|  |  * 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'; | ||||||
|  | 
 | ||||||
|  | var defaults = { | ||||||
|  |   productionServerUrl:    'https://acme-v02.api.letsencrypt.org/directory' | ||||||
|  | , stagingServerUrl:       'https://acme-staging-v02.api.letsencrypt.org/directory' | ||||||
|  | , acmeChallengePrefix:    '/.well-known/acme-challenge/' | ||||||
|  | , knownEndpoints:         [ 'keyChange', 'meta', 'newAccount', 'newNonce', 'newOrder', 'revokeCert' ] | ||||||
|  | , challengeType:          'http-01' // dns-01
 | ||||||
|  | , keyType:                'rsa' // ecdsa
 | ||||||
|  | , keySize:                2048 // 256
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function create(deps) { | ||||||
|  |   if (!deps) { deps = {}; } | ||||||
|  |   deps.LeCore = {}; | ||||||
|  |   deps.pkg = deps.pkg || require('./package.json'); | ||||||
|  |   deps.os = deps.os || require('os'); | ||||||
|  |   deps.process = deps.process || require('process'); | ||||||
|  | 
 | ||||||
|  |   var uaDefaults = { | ||||||
|  |       pkg: "Greenlock/" + deps.pkg.version | ||||||
|  |     , os: " (" + deps.os.type() + "; " + deps.process.arch + " " + deps.os.platform() + " " + deps.os.release() + ")" | ||||||
|  |     , node: " Node.js/" + deps.process.version | ||||||
|  |     , user: '' | ||||||
|  |   }; | ||||||
|  |   //var currentUAProps;
 | ||||||
|  | 
 | ||||||
|  |   function getUaString() { | ||||||
|  |     var userAgent = ''; | ||||||
|  | 
 | ||||||
|  |     //Object.keys(currentUAProps)
 | ||||||
|  |     Object.keys(uaDefaults).forEach(function (key) { | ||||||
|  |       userAgent += uaDefaults[key]; | ||||||
|  |       //userAgent += currentUAProps[key];
 | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return userAgent.trim(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function getRequest(opts) { | ||||||
|  |     if (!opts) { opts = {}; } | ||||||
|  | 
 | ||||||
|  |     return deps.request.defaults({ | ||||||
|  |       headers: { | ||||||
|  |         'User-Agent': opts.userAgent || getUaString() | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   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 acme2 = { | ||||||
|  |     getAcmeUrls: function () { | ||||||
|  |       var me = this; | ||||||
|  |       return request({ url: directoryUrl }).then(function (resp) { | ||||||
|  |         me._directoryUrls = JSON.parse(resp.body); | ||||||
|  |         me._tos = me._directoryUrls.meta.termsOfService; | ||||||
|  |         return me._directoryUrls; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   , getNonce: function () { | ||||||
|  |       var me = this; | ||||||
|  |       return request({ method: 'HEAD', url: me._directoryUrls.newNonce }).then(function (resp) { | ||||||
|  |         me._nonce = resp.toJSON().headers['replay-nonce']; | ||||||
|  |         return me._nonce; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 		// ACME RFC Section 7.3 Account Creation
 | ||||||
|  | 		/* | ||||||
|  | 		 { | ||||||
|  | 			 "protected": base64url({ | ||||||
|  | 				 "alg": "ES256", | ||||||
|  | 				 "jwk": {...}, | ||||||
|  | 				 "nonce": "6S8IqOGY7eL2lsGoTZYifg", | ||||||
|  | 				 "url": "https://example.com/acme/new-account" | ||||||
|  | 			 }), | ||||||
|  | 			 "payload": base64url({ | ||||||
|  | 				 "termsOfServiceAgreed": true, | ||||||
|  | 				 "onlyReturnExisting": false, | ||||||
|  | 				 "contact": [ | ||||||
|  | 					 "mailto:cert-admin@example.com", | ||||||
|  | 					 "mailto:admin@example.com" | ||||||
|  | 				 ] | ||||||
|  | 			 }), | ||||||
|  | 			 "signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I" | ||||||
|  | 		 } | ||||||
|  | 		*/ | ||||||
|  |   , registerNewAccount: function () { | ||||||
|  |       var me = this; | ||||||
|  | 			var RSA = require('rsa-compat').RSA; | ||||||
|  | 			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 options = { | ||||||
|  |         email: 'coolaj86@gmail.com' | ||||||
|  |       , keypair: RSA.import({ privateKeyPem: require('fs').readFileSync(__dirname + '/privkey.pem') }) | ||||||
|  |       }; | ||||||
|  |       var body = { | ||||||
|  |         termsOfServiceAgreed: true | ||||||
|  |       , onlyReturnExisting: false | ||||||
|  |       , contact: [ 'mailto:' + options.email ] | ||||||
|  |       }; | ||||||
|  | 			var payload = JSON.stringify(body, null, 2); | ||||||
|  | 			var jws = RSA.signJws( | ||||||
|  |         options.keypair | ||||||
|  |       , new Buffer(payload) | ||||||
|  |       , { nonce: me._nonce, alg: 'RS256', url: me._directoryUrls.newAccount, jwk: RSA.exportPublicJwk(options.keypair) } | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  |       console.log('jws:'); | ||||||
|  |       console.log(jws); | ||||||
|  |       return request({ | ||||||
|  |         method: 'POST' | ||||||
|  |       , url: me._directoryUrls.newAccount | ||||||
|  |       , headers: { 'Content-Type': 'application/jose+json' } | ||||||
|  |       , json: jws | ||||||
|  |       }).then(function (resp) { | ||||||
|  |         me._nonce = resp.toJSON().headers['replay-nonce']; | ||||||
|  |         console.log(resp.toJSON()); | ||||||
|  |         return resp.body; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   return acme2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var acme2 = create(); | ||||||
|  | acme2.getAcmeUrls().then(function (body) { | ||||||
|  |   console.log(body); | ||||||
|  |   acme2.getNonce().then(function (nonce) { | ||||||
|  |     console.log(nonce); | ||||||
|  |     acme2.registerNewAccount().then(function (account) { | ||||||
|  |       console.log(account); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										28
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | { | ||||||
|  |   "name": "acme-v2", | ||||||
|  |   "version": "1.0.0", | ||||||
|  |   "description": "A framework for building letsencrypt clients (and other ACME v2 clients), forked from le-acme-core.js.", | ||||||
|  |   "main": "node.js", | ||||||
|  |   "scripts": { | ||||||
|  |     "test": "echo \"Error: no test specified\" && exit 1" | ||||||
|  |   }, | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "ssh://gitea@git.coolaj86.com:22042/coolaj86/acme-v2.js.git" | ||||||
|  |   }, | ||||||
|  |   "keywords": [ | ||||||
|  |     "acmev2", | ||||||
|  |     "acme-v2", | ||||||
|  |     "acme", | ||||||
|  |     "letsencrypt-v2", | ||||||
|  |     "letsencryptv2", | ||||||
|  |     "greenlock", | ||||||
|  |     "greenlock2" | ||||||
|  |   ], | ||||||
|  |   "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", | ||||||
|  |   "license": "(MIT OR Apache-2.0)", | ||||||
|  |   "dependencies": { | ||||||
|  |     "request": "^2.85.0", | ||||||
|  |     "rsa-compat": "^1.2.7" | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user