mirror of
				https://github.com/therootcompany/acme.js.git
				synced 2024-11-16 17:29:00 +00:00 
			
		
		
		
	initial commit
This commit is contained in:
		
						commit
						4c4eaa83b7
					
				
							
								
								
									
										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