create acocunt and order, and view challenges
This commit is contained in:
		
							parent
							
								
									5d4f71ba8e
								
							
						
					
					
						commit
						ec9a2606f6
					
				
							
								
								
									
										100
									
								
								js/app.js
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								js/app.js
									
									
									
									
									
								
							| @ -5,6 +5,8 @@ | |||||||
|   var $qsa = function (s) { return window.document.querySelectorAll(s); }; |   var $qsa = function (s) { return window.document.querySelectorAll(s); }; | ||||||
|   var info = {}; |   var info = {}; | ||||||
|   var steps = {}; |   var steps = {}; | ||||||
|  |   var nonce; | ||||||
|  |   var kid; | ||||||
|   var i = 1; |   var i = 1; | ||||||
| 
 | 
 | ||||||
|   //$qs('.js-acme-directory-url').value = 'https://acme-v02.api.letsencrypt.org/directory';
 |   //$qs('.js-acme-directory-url').value = 'https://acme-v02.api.letsencrypt.org/directory';
 | ||||||
| @ -16,11 +18,13 @@ | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   $qs('.js-acme-form-domains').addEventListener('submit', function (ev) { |   $qsa('.js-acme-form').forEach(function ($el) { | ||||||
|  |     $el.addEventListener('submit', function (ev) { | ||||||
|       ev.preventDefault(); |       ev.preventDefault(); | ||||||
|       steps[i].submit(ev); |       steps[i].submit(ev); | ||||||
|       i += 1; |       i += 1; | ||||||
|     }); |     }); | ||||||
|  |   }); | ||||||
| 
 | 
 | ||||||
|   steps[1] = function () { |   steps[1] = function () { | ||||||
|     hideForms(); |     hideForms(); | ||||||
| @ -33,8 +37,12 @@ | |||||||
| 
 | 
 | ||||||
|     return BACME.directory($qs('.js-acme-directory-url').value).then(function (directory) { |     return BACME.directory($qs('.js-acme-directory-url').value).then(function (directory) { | ||||||
|       $qs('.js-acme-tos-url').href = directory.meta.termsOfService; |       $qs('.js-acme-tos-url').href = directory.meta.termsOfService; | ||||||
|  |       return BACME.nonce().then(function (_nonce) { | ||||||
|  |         nonce = _nonce; | ||||||
|  | 
 | ||||||
|         steps[i](); |         steps[i](); | ||||||
|       }); |       }); | ||||||
|  |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   steps[2] = function () { |   steps[2] = function () { | ||||||
| @ -42,16 +50,94 @@ | |||||||
|     $qs('.js-acme-form-account').hidden = false; |     $qs('.js-acme-form-account').hidden = false; | ||||||
|   }; |   }; | ||||||
|   steps[2].submit = function () { |   steps[2].submit = function () { | ||||||
|     info.contact = [ 'mailto:' + $qs('.js-acme-account-email').value ]; |     var email = $qs('.js-acme-account-email').value.toLowerCase().trim(); | ||||||
|  | 
 | ||||||
|  |     info.contact = [ 'mailto:' + email ]; | ||||||
|     info.agree = $qs('.js-acme-account-tos').checked; |     info.agree = $qs('.js-acme-account-tos').checked; | ||||||
|     info.greenlockAgree = $qs('.js-gl-tos').checked; |     info.greenlockAgree = $qs('.js-gl-tos').checked; | ||||||
|     // TODO
 |     // TODO
 | ||||||
|     // create account key
 |     // options for
 | ||||||
|     // create account
 |     // * regenerate key
 | ||||||
|     // capture email
 |     // * ECDSA / RSA / bitlength
 | ||||||
|     // submit challenges
 | 
 | ||||||
|     // populate challenges in table
 |     // TODO ping with version and account creation
 | ||||||
|  | 
 | ||||||
|  |     var jwk = JSON.parse(localStorage.getItem('account:' + email) || 'null'); | ||||||
|  |     var p; | ||||||
|  | 
 | ||||||
|  |     function createKeypair() { | ||||||
|  |       return BACME.accounts.generateKeypair({ | ||||||
|  |         type: 'ECDSA' | ||||||
|  |       , bitlength: '256' | ||||||
|  |       }).then(function (jwk) { | ||||||
|  |         localStorage.setItem('account:' + email, JSON.stringify(jwk)); | ||||||
|  |         return jwk; | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (jwk) { | ||||||
|  |       p = Promise.resolve(jwk); | ||||||
|  |     } else { | ||||||
|  |       p = createKeypair(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function createAccount(jwk) { | ||||||
|  |       console.log('account jwk:'); | ||||||
|  |       console.log(jwk); | ||||||
|  |       delete jwk.key_ops; | ||||||
|  |       return BACME.accounts.sign({ | ||||||
|  |         jwk: jwk | ||||||
|  |       , contacts: [ 'mailto:' + email ] | ||||||
|  |       , agree: info.agree | ||||||
|  |       , nonce: nonce | ||||||
|  |       , kid: kid | ||||||
|  |       }).then(function (signedAccount) { | ||||||
|  |         return BACME.accounts.set({ | ||||||
|  |           signedAccount: signedAccount | ||||||
|  |         }).then(function (account) { | ||||||
|  |           console.log('account:'); | ||||||
|  |           console.log(account); | ||||||
|  |           kid = account.kid; | ||||||
|  |           return kid; | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return p.then(function (_jwk) { | ||||||
|  |       jwk = _jwk; | ||||||
|  |       kid = JSON.parse(localStorage.getItem('account-kid:' + email) || 'null'); | ||||||
|  |       var p2 | ||||||
|  | 
 | ||||||
|  |       // TODO save account id rather than always retrieving it
 | ||||||
|  |       if (kid) { | ||||||
|  |         p2 = Promise.resolve(kid); | ||||||
|  |       } else { | ||||||
|  |         p2 = createAccount(jwk); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return p2.then(function (_kid) { | ||||||
|  |         kid = _kid; | ||||||
|  |         return BACME.orders.sign({ | ||||||
|  |           jwk: jwk | ||||||
|  |         , identifiers: info.identifiers | ||||||
|  |         , kid: kid | ||||||
|  |         }).then(function (signedOrder) { | ||||||
|  |           return BACME.orders.create({ | ||||||
|  |             signedOrder: signedOrder | ||||||
|  |           }).then(function (/*challengeIndexes*/) { | ||||||
|  |             return BACME.challenges.all().then(function (challenges) { | ||||||
|  |               console.log('challenges:'); | ||||||
|  |               console.log(challenges); | ||||||
|  |               // TODO populate challenges in table
 | ||||||
|               steps[i](); |               steps[i](); | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     }).catch(function (err) { | ||||||
|  |       console.error('Step \'' + i + '\' Error:'); | ||||||
|  |       console.error(err); | ||||||
|  |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   steps[3] = function () { |   steps[3] = function () { | ||||||
|  | |||||||
							
								
								
									
										284
									
								
								js/bacme.js
									
									
									
									
									
								
							
							
						
						
									
										284
									
								
								js/bacme.js
									
									
									
									
									
								
							| @ -58,11 +58,36 @@ BACME.nonce = function () { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| BACME.accounts = {}; | BACME.accounts = {}; | ||||||
| BACME.accounts.generateKeypair = function () { | 
 | ||||||
|  | // type = ECDSA
 | ||||||
|  | // bitlength = 256
 | ||||||
|  | BACME.accounts.generateKeypair = function (opts) { | ||||||
|  |   var wcOpts = {}; | ||||||
|  | 
 | ||||||
|  |   // ECDSA has only the P curves and an associated bitlength
 | ||||||
|  |   if (/^EC/i.test(opts.type)) { | ||||||
|  |     wcOpts.name = 'ECDSA'; | ||||||
|  |     if (/256/.test(opts.bitlength)) { | ||||||
|  |       wcOpts.namedCurve = 'P-256'; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // RSA-PSS is another option, but I don't think it's used for Let's Encrypt
 | ||||||
|  |   // I think the hash is only necessary for signing, not generation or import
 | ||||||
|  |   if (/^RS/i.test(opts.type)) { | ||||||
|  |     wcOpts.name = 'RSASSA-PKCS1-v1_5'; | ||||||
|  |     wcOpts.modulusLength = opts.bitlength; | ||||||
|  |     if (opts.bitlength < 2048) { | ||||||
|  |       wcOpts.modulusLength = opts.bitlength * 8; | ||||||
|  |     } | ||||||
|  |     wcOpts.publicExponent = new Uint8Array([0x01, 0x00, 0x01]); | ||||||
|  |     wcOpts.hash = { name: "SHA-256" }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
| 	// https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
 | 	// https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
 | ||||||
| 	var extractable = true; | 	var extractable = true; | ||||||
| 	return webCrypto.subtle.generateKey( | 	return webCrypto.subtle.generateKey( | ||||||
| 		{ name: "ECDSA", namedCurve: "P-256" } | 		wcOpts | ||||||
| 	, extractable | 	, extractable | ||||||
| 	, [ 'sign', 'verify' ] | 	, [ 'sign', 'verify' ] | ||||||
| 	).then(function (result) { | 	).then(function (result) { | ||||||
| @ -71,11 +96,11 @@ BACME.accounts.generateKeypair = function () { | |||||||
| 		return webCrypto.subtle.exportKey( | 		return webCrypto.subtle.exportKey( | ||||||
| 			"jwk" | 			"jwk" | ||||||
| 		, result.privateKey | 		, result.privateKey | ||||||
| 		).then(function (jwk) { | 		).then(function (privJwk) { | ||||||
| 
 | 
 | ||||||
| 			accountJwk = jwk; | 			accountJwk = privJwk; | ||||||
| 			console.log('private jwk:'); | 			console.log('private jwk:'); | ||||||
| 			console.log(JSON.stringify(jwk, null, 2)); | 			console.log(JSON.stringify(privJwk, null, 2)); | ||||||
| 
 | 
 | ||||||
| 			return webCrypto.subtle.exportKey( | 			return webCrypto.subtle.exportKey( | ||||||
| 				"pkcs8" | 				"pkcs8" | ||||||
| @ -84,7 +109,8 @@ BACME.accounts.generateKeypair = function () { | |||||||
| 				console.log('pkcs8:'); | 				console.log('pkcs8:'); | ||||||
| 				console.log(Array.from(new Uint8Array(keydata))); | 				console.log(Array.from(new Uint8Array(keydata))); | ||||||
| 
 | 
 | ||||||
|         return accountKeypair; |         return privJwk; | ||||||
|  |         //return accountKeypair;
 | ||||||
| 			}); | 			}); | ||||||
| 		}) | 		}) | ||||||
| 	}); | 	}); | ||||||
| @ -97,63 +123,137 @@ BACME._jsto64 = function (json) { | |||||||
| 
 | 
 | ||||||
| var textEncoder = new TextEncoder(); | var textEncoder = new TextEncoder(); | ||||||
| 
 | 
 | ||||||
| // email = john.doe@gmail.com
 | BACME._importKey = function (jwk) { | ||||||
| BACME.accounts.sign = function (email) { |   var alg; // I think the 256 refers to the hash
 | ||||||
| 	var payload64 = BACME._jsto64( |   var wcOpts = {}; | ||||||
| 		{ termsOfServiceAgreed: true |   var extractable = false; | ||||||
| 		, onlyReturnExisting: false |  | ||||||
| 		, contact: [ 'mailto:' + email ] |  | ||||||
| 		} |  | ||||||
| 	); |  | ||||||
| 
 | 
 | ||||||
| 	var protected64 = BACME._jsto64( |   // ECDSA
 | ||||||
| 		{ nonce: nonce |   if (/^EC/i.test(jwk.kty)) { | ||||||
| 		, url: accountUrl |     wcOpts.name = 'ECDSA'; | ||||||
| 		, alg: 'ES256' |     wcOpts.namedCurve = jwk.crv; | ||||||
| 		, jwk: { |     alg = 'ES256'; | ||||||
| 				kty: accountJwk.kty |  | ||||||
| 			, crv: accountJwk.crv |  | ||||||
| 			, x: accountJwk.x |  | ||||||
| 			, y: accountJwk.y |  | ||||||
|   } |   } | ||||||
| 		} |  | ||||||
| 	); |  | ||||||
| 
 | 
 | ||||||
| 	// Note: this function hashes before signing so send data, not the hash
 |   // RSA
 | ||||||
|  |   if (/^RS/i.test(jwk.kty)) { | ||||||
|  |     wcOpts.name = 'RSASSA-PKCS1-v1_5'; | ||||||
|  |     wcOpts.hash = { name: "SHA-256" }; | ||||||
|  |     alg = 'RS256'; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return window.crypto.subtle.importKey( | ||||||
|  |     "jwk" | ||||||
|  |   , jwk | ||||||
|  | 	, wcOpts | ||||||
|  |   , extractable | ||||||
|  |   , [ "sign"/*, "verify"*/ ] | ||||||
|  |   ).then(function (keypair) { | ||||||
|  |     return { | ||||||
|  |       wcKey: keypair | ||||||
|  |     , meta: { | ||||||
|  |         alg: alg | ||||||
|  |       , name: wcOpts.name | ||||||
|  |       , hash: wcOpts.hash | ||||||
|  |       } | ||||||
|  |     , jwk: jwk | ||||||
|  |     }; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | BACME._sign = function (opts) { | ||||||
|  |   var wcPrivKey = opts.abstractKey.wcKey; | ||||||
|  |   var wcOpts = opts.abstractKey.meta; | ||||||
|  |   var alg = opts.abstractKey.meta.alg; // I think the 256 refers to the hash
 | ||||||
|  |   var signHash; | ||||||
|  | 
 | ||||||
|  |   console.log('kty', opts.abstractKey.jwk.kty); | ||||||
|  |   signHash = { name: "SHA-" + alg.replace(/[a-z]+/ig, '') }; | ||||||
|  | 
 | ||||||
|  |   var msg = textEncoder.encode(opts.protected64 + '.' + opts.payload64); | ||||||
|  |   console.log('msg:', msg); | ||||||
|   return window.crypto.subtle.sign( |   return window.crypto.subtle.sign( | ||||||
| 		{ name: "ECDSA", hash: { name: "SHA-256" } } |     { name: wcOpts.name, hash: signHash } | ||||||
| 	, accountKeypair.privateKey |   , wcPrivKey | ||||||
| 	, textEncoder.encode(protected64 + '.' + payload64) |   , msg | ||||||
|   ).then(function (signature) { |   ).then(function (signature) { | ||||||
| 
 |     //console.log('sig1:', signature);
 | ||||||
|  |     //console.log('sig2:', new Uint8Array(signature));
 | ||||||
|  |     //console.log('sig3:', Array.prototype.slice.call(new Uint8Array(signature)));
 | ||||||
|     // convert buffer to urlsafe base64
 |     // convert buffer to urlsafe base64
 | ||||||
|     var sig64 = btoa(Array.prototype.map.call(new Uint8Array(signature), function (ch) { |     var sig64 = btoa(Array.prototype.map.call(new Uint8Array(signature), function (ch) { | ||||||
|       return String.fromCharCode(ch); |       return String.fromCharCode(ch); | ||||||
|     }).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); |     }).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); | ||||||
| 
 | 
 | ||||||
| 		console.log('URL-safe Base64 Signature:'); |     console.log('[1] URL-safe Base64 Signature:'); | ||||||
|     console.log(sig64); |     console.log(sig64); | ||||||
| 
 | 
 | ||||||
| 		signedAccount = { |     var signedMsg = { | ||||||
| 			protected: protected64 |       protected: opts.protected64 | ||||||
| 		, payload: payload64 |     , payload: opts.payload64 | ||||||
|     , signature: sig64 |     , signature: sig64 | ||||||
|     }; |     }; | ||||||
| 		console.log('Signed Base64 Account:'); | 
 | ||||||
| 		console.log(JSON.stringify(signedAccount, null, 2)); |     console.log('Signed Base64 Msg:'); | ||||||
|  |     console.log(JSON.stringify(signedMsg, null, 2)); | ||||||
|  | 
 | ||||||
|  |     return signedMsg; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | // email = john.doe@gmail.com
 | ||||||
|  | // jwk = { ... }
 | ||||||
|  | // agree = true
 | ||||||
|  | BACME.accounts.sign = function (opts) { | ||||||
|  | 
 | ||||||
|  |   return BACME._importKey(opts.jwk).then(function (abstractKey) { | ||||||
|  | 
 | ||||||
|  |     var payloadJson = | ||||||
|  |       { termsOfServiceAgreed: opts.agree | ||||||
|  |       , onlyReturnExisting: false | ||||||
|  |       , contact: opts.contacts || [ 'mailto:' + opts.email ] | ||||||
|  |       }; | ||||||
|  |     console.log('payload:'); | ||||||
|  |     console.log(payloadJson); | ||||||
|  |     var payload64 = BACME._jsto64( | ||||||
|  |       payloadJson | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     // TODO RSA
 | ||||||
|  |     var protectedJson = | ||||||
|  |       { nonce: opts.nonce | ||||||
|  |       , url: accountUrl | ||||||
|  |       , alg: abstractKey.meta.alg | ||||||
|  |       , jwk: { | ||||||
|  |           kty: opts.jwk.kty | ||||||
|  |         , crv: opts.jwk.crv | ||||||
|  |         , x: opts.jwk.x | ||||||
|  |         , y: opts.jwk.y | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  |     console.log('protected:'); | ||||||
|  |     console.log(protectedJson); | ||||||
|  |     var protected64 = BACME._jsto64( | ||||||
|  |       protectedJson | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | 		// Note: this function hashes before signing so send data, not the hash
 | ||||||
|  | 		return BACME._sign({ | ||||||
|  |       abstractKey: abstractKey | ||||||
|  |     , payload64: payload64 | ||||||
|  |     , protected64: protected64 | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| var account; | var account; | ||||||
| var accountId; | var accountId; | ||||||
| 
 | 
 | ||||||
| BACME.accounts.set = function () { | BACME.accounts.set = function (opts) { | ||||||
| 	nonce = null; | 	nonce = null; | ||||||
| 	return window.fetch(accountUrl, { | 	return window.fetch(accountUrl, { | ||||||
| 		mode: 'cors' | 		mode: 'cors' | ||||||
| 	, method: 'POST' | 	, method: 'POST' | ||||||
| 	, headers: { 'Content-Type': 'application/jose+json' } | 	, headers: { 'Content-Type': 'application/jose+json' } | ||||||
| 	, body: JSON.stringify(signedAccount) | 	, body: JSON.stringify(opts.signedAccount) | ||||||
| 	}).then(function (resp) { | 	}).then(function (resp) { | ||||||
| 		BACME._logHeaders(resp); | 		BACME._logHeaders(resp); | ||||||
| 		nonce = resp.headers.get('replay-nonce'); | 		nonce = resp.headers.get('replay-nonce'); | ||||||
| @ -163,11 +263,18 @@ BACME.accounts.set = function () { | |||||||
| 
 | 
 | ||||||
| 		if (!resp.headers.get('content-type')) { | 		if (!resp.headers.get('content-type')) { | ||||||
| 		 console.log('Body: <none>'); | 		 console.log('Body: <none>'); | ||||||
| 		 return; | 
 | ||||||
|  | 		 return { kid: accountId }; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return resp.json().then(function (result) { | 		return resp.json().then(function (result) { | ||||||
|  |       if (/^Error/i.test(result.detail)) { | ||||||
|  |         return Promise.reject(new Error(result.detail)); | ||||||
|  |       } | ||||||
|  |       result.kid = accountId; | ||||||
|       BACME._logBody(result); |       BACME._logBody(result); | ||||||
|  | 
 | ||||||
|  |       return result; | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| }; | }; | ||||||
| @ -178,36 +285,28 @@ var signedOrder; | |||||||
| BACME.orders = {}; | BACME.orders = {}; | ||||||
| 
 | 
 | ||||||
| // identifiers = [ { type: 'dns', value: 'example.com' }, { type: 'dns', value: '*.example.com' } ]
 | // identifiers = [ { type: 'dns', value: 'example.com' }, { type: 'dns', value: '*.example.com' } ]
 | ||||||
| BACME.orders.sign = function (identifiers) { | // signedAccount
 | ||||||
| 	var payload64 = jsto64({ identifiers: identifiers }); | BACME.orders.sign = function (opts) { | ||||||
|  | 	var payload64 = BACME._jsto64({ identifiers: opts.identifiers }); | ||||||
| 
 | 
 | ||||||
| 	var protected64 = jsto64( | 	var protected64 = BACME._jsto64( | ||||||
| 		{ nonce: nonce, alg: 'ES256', url: orderUrl, kid: accountId } | 		{ nonce: nonce, alg: 'ES256', url: orderUrl, kid: opts.kid } | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	return window.crypto.subtle.sign( | 	return BACME._importKey(opts.jwk).then(function (abstractKey) { | ||||||
| 		{ name: "ECDSA", hash: { name: "SHA-256" } } |     console.log('abstractKey:'); | ||||||
| 	, accountKeypair.privateKey |     console.log(abstractKey); | ||||||
| 	, textEncoder.encode(protected64 + '.' + payload64) |     return BACME._sign({ | ||||||
| 	).then(function (signature) { |       abstractKey: abstractKey | ||||||
| 
 |     , payload64: payload64 | ||||||
| 		// convert buffer to urlsafe base64
 |     , protected64: protected64 | ||||||
| 		var sig64 = btoa(Array.prototype.map.call(new Uint8Array(signature), function (ch) { |     }).then(function (sig) { | ||||||
| 			return String.fromCharCode(ch); |       if (!sig) { | ||||||
| 		}).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); |         throw new Error('sig is undefined... nonsense!'); | ||||||
| 
 |       } | ||||||
| 		console.log('URL-safe Base64 Signature:'); |       console.log('newsig', sig); | ||||||
| 		console.log(sig64); |       return sig; | ||||||
| 
 |     }); | ||||||
| 		signedOrder = { |  | ||||||
| 			protected: protected64 |  | ||||||
| 		, payload: payload64 |  | ||||||
| 		, signature: sig64 |  | ||||||
| 		}; |  | ||||||
| 		console.log('Signed Base64 Order:'); |  | ||||||
| 		console.log(JSON.stringify(signedAccount, null, 2)); |  | ||||||
| 
 |  | ||||||
|     return signedOrder; |  | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -216,25 +315,26 @@ var currentOrderUrl; | |||||||
| var authorizationUrls; | var authorizationUrls; | ||||||
| var finalizeUrl; | var finalizeUrl; | ||||||
| 
 | 
 | ||||||
| BACME.orders.create = function () { | BACME.orders.create = function (opts) { | ||||||
| 	nonce = null; | 	nonce = null; | ||||||
| 	return window.fetch(orderUrl, { | 	return window.fetch(orderUrl, { | ||||||
| 		mode: 'cors' | 		mode: 'cors' | ||||||
| 	, method: 'POST' | 	, method: 'POST' | ||||||
| 	, headers: { 'Content-Type': 'application/jose+json' } | 	, headers: { 'Content-Type': 'application/jose+json' } | ||||||
| 	, body: JSON.stringify(signedOrder) | 	, body: JSON.stringify(opts.signedOrder) | ||||||
| 	}).then(function (resp) { | 	}).then(function (resp) { | ||||||
| 		console.log('Headers:'); |     BACME._logHeaders(resp); | ||||||
| 		Array.from(resp.headers.entries()).forEach(function (h) { console.log(h[0] + ': ' + h[1]); }); |  | ||||||
| 		currentOrderUrl = resp.headers.get('location'); | 		currentOrderUrl = resp.headers.get('location'); | ||||||
| 		nonce = resp.headers.get('replay-nonce'); | 		nonce = resp.headers.get('replay-nonce'); | ||||||
| 		console.log('Next nonce:', nonce); | 		console.log('Next nonce:', nonce); | ||||||
| 
 | 
 | ||||||
| 		return resp.json().then(function (result) { | 		return resp.json().then(function (result) { | ||||||
|  |       if (/^Error/i.test(result.detail)) { | ||||||
|  |         return Promise.reject(new Error(result.detail)); | ||||||
|  |       } | ||||||
| 			authorizationUrls = result.authorizations; | 			authorizationUrls = result.authorizations; | ||||||
| 			finalizeUrl = result.finalize; | 			finalizeUrl = result.finalize; | ||||||
| 			console.log('Body:'); |       BACME._logBody(result); | ||||||
| 			console.log(JSON.stringify(result, null, 2)); |  | ||||||
| 
 | 
 | ||||||
|       return result; |       return result; | ||||||
| 		}); | 		}); | ||||||
| @ -242,6 +342,22 @@ BACME.orders.create = function () { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| BACME.challenges = {}; | BACME.challenges = {}; | ||||||
|  | BACME.challenges.all = function () { | ||||||
|  |   var challenges = []; | ||||||
|  | 
 | ||||||
|  |   function next() { | ||||||
|  |     if (!authorizationUrls.length) { | ||||||
|  |       return challenges; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return BACME.challenges.view().then(function (challenge) { | ||||||
|  |       challenges.push(challenge); | ||||||
|  |       return next(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return next(); | ||||||
|  | }; | ||||||
| BACME.challenges.view = function () { | BACME.challenges.view = function () { | ||||||
| 	var authzUrl = authorizationUrls.pop(); | 	var authzUrl = authorizationUrls.pop(); | ||||||
| 	var token; | 	var token; | ||||||
| @ -273,10 +389,19 @@ var httpPath; | |||||||
| var dnsAuth; | var dnsAuth; | ||||||
| var dnsRecord; | var dnsRecord; | ||||||
| 
 | 
 | ||||||
| BACME.thumbprint = function () { | BACME.thumbprint = function (opts) { | ||||||
| 	// https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
 | 	// https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
 | ||||||
| 
 | 
 | ||||||
| 	var accountPublicStr = '{' + ['crv', 'kty', 'x', 'y'].map(function (key) { |   var accountJwk = opts.jwk; | ||||||
|  |   var keys; | ||||||
|  | 
 | ||||||
|  |   if (/^EC/i.test(opts.jwk.kty)) { | ||||||
|  |     keys = [ 'e', 'kty', 'n' ]; | ||||||
|  |   } else if (/^RS/i.test(opts.jwk.kty)) { | ||||||
|  |     keys = [ 'crv', 'kty', 'x', 'y' ]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 	var accountPublicStr = '{' + keys.map(function (key) { | ||||||
| 		return '"' + key + '":"' + accountJwk[key] + '"'; | 		return '"' + key + '":"' + accountJwk[key] + '"'; | ||||||
| 	}).join(',') + '}'; | 	}).join(',') + '}'; | ||||||
| 
 | 
 | ||||||
| @ -338,11 +463,11 @@ BACME.challenges['dns-01'] = function () { | |||||||
| var challengePollUrl; | var challengePollUrl; | ||||||
| 
 | 
 | ||||||
| BACME.challenges.accept = function () { | BACME.challenges.accept = function () { | ||||||
|   var payload64 = jsto64( |   var payload64 = BACME._jsto64( | ||||||
| 		{} | 		{} | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	var protected64 = jsto64( | 	var protected64 = BACME._jsto64( | ||||||
| 		{ nonce: nonce, alg: 'ES256', url: challengeUrl, kid: accountId } | 		{ nonce: nonce, alg: 'ES256', url: challengeUrl, kid: accountId } | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| @ -371,8 +496,7 @@ BACME.challenges.accept = function () { | |||||||
| 			, body: JSON.stringify(body) | 			, body: JSON.stringify(body) | ||||||
| 			} | 			} | ||||||
| 		).then(function (resp) { | 		).then(function (resp) { | ||||||
| 			console.log('Headers:'); |       BACME._logHeaders(resp); | ||||||
| 			Array.from(resp.headers.entries()).forEach(function (h) { console.log(h[0] + ': ' + h[1]); }); |  | ||||||
| 			nonce = resp.headers.get('replay-nonce'); | 			nonce = resp.headers.get('replay-nonce'); | ||||||
| 
 | 
 | ||||||
| 			return resp.json().then(function (reply) { | 			return resp.json().then(function (reply) { | ||||||
| @ -435,11 +559,11 @@ BACME.orders.generateCsr = function (keypair, domains) { | |||||||
| var certificateUrl; | var certificateUrl; | ||||||
| 
 | 
 | ||||||
| BACME.orders.finalize = function () { | BACME.orders.finalize = function () { | ||||||
| 	var payload64 = jsto64( | 	var payload64 = BACME._jsto64( | ||||||
| 		{ csr: csr } | 		{ csr: csr } | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	var protected64 = jsto64( | 	var protected64 = BACME._jsto64( | ||||||
| 		{ nonce: nonce, alg: 'ES256', url: finalizeUrl, kid: accountId } | 		{ nonce: nonce, alg: 'ES256', url: finalizeUrl, kid: accountId } | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user