275 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*global Promise*/
 | |
| (function () {
 | |
|   'use strict';
 | |
| 
 | |
|   var Keypairs = window.Keypairs;
 | |
|   var Rasha = window.Rasha;
 | |
|   var Eckles = window.Eckles;
 | |
|   var x509 = window.x509;
 | |
|   var CSR = window.CSR;
 | |
|   var ACME = window.ACME;
 | |
|   var accountStuff = {};
 | |
| 
 | |
|   function $(sel) {
 | |
|     return document.querySelector(sel);
 | |
|   }
 | |
|   function $$(sel) {
 | |
|     return Array.prototype.slice.call(document.querySelectorAll(sel));
 | |
|   }
 | |
| 
 | |
|   function checkTos(tos) {
 | |
|     if ($('input[name="tos"]:checked')) {
 | |
|       return tos;
 | |
|     } else {
 | |
|       return '';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function run() {
 | |
|     console.log('hello');
 | |
| 
 | |
|     // Show different options for ECDSA vs RSA
 | |
|     $$('input[name="kty"]').forEach(function ($el) {
 | |
|       $el.addEventListener('change', function (ev) {
 | |
|         console.log(this);
 | |
|         console.log(ev);
 | |
|         if ("RSA" === ev.target.value) {
 | |
|           $('.js-rsa-opts').hidden = false;
 | |
|           $('.js-ec-opts').hidden = true;
 | |
|         } else {
 | |
|           $('.js-rsa-opts').hidden = true;
 | |
|           $('.js-ec-opts').hidden = false;
 | |
|         }
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     // Generate a key on submit
 | |
|     $('form.js-keygen').addEventListener('submit', function (ev) {
 | |
|       ev.preventDefault();
 | |
|       ev.stopPropagation();
 | |
|       $('.js-loading').hidden = false;
 | |
|       $('.js-jwk').hidden = true;
 | |
|       $('.js-toc-der-public').hidden = true;
 | |
|       $('.js-toc-der-private').hidden = true;
 | |
|       $$('.js-toc-pem').forEach(function ($el) {
 | |
|         $el.hidden = true;
 | |
|       });
 | |
|       $$('input').map(function ($el) { $el.disabled = true; });
 | |
|       $$('button').map(function ($el) { $el.disabled = true; });
 | |
|       var opts = {
 | |
|         kty: $('input[name="kty"]:checked').value
 | |
|       , namedCurve: $('input[name="ec-crv"]:checked').value
 | |
|       , modulusLength: $('input[name="rsa-len"]:checked').value
 | |
|       };
 | |
|       var then = Date.now();
 | |
|       console.log('opts', opts);
 | |
|       Keypairs.generate(opts).then(function (results) {
 | |
|         console.log("Key generation time:", (Date.now() - then) + "ms");
 | |
|         var pubDer;
 | |
|         var privDer;
 | |
|         if (/EC/i.test(opts.kty)) {
 | |
|           privDer = x509.packPkcs8(results.private);
 | |
|           pubDer = x509.packSpki(results.public);
 | |
|           Eckles.export({ jwk: results.private, format: 'sec1' }).then(function (pem) {
 | |
|             $('.js-input-pem-sec1-private').innerText = pem;
 | |
|             $('.js-toc-pem-sec1-private').hidden = false;
 | |
|           });
 | |
|           Eckles.export({ jwk: results.private, format: 'pkcs8' }).then(function (pem) {
 | |
|             $('.js-input-pem-pkcs8-private').innerText = pem;
 | |
|             $('.js-toc-pem-pkcs8-private').hidden = false;
 | |
|           });
 | |
|           Eckles.export({ jwk: results.public, public: true }).then(function (pem) {
 | |
|             $('.js-input-pem-spki-public').innerText = pem;
 | |
|             $('.js-toc-pem-spki-public').hidden = false;
 | |
|           });
 | |
|         } else {
 | |
|           privDer = x509.packPkcs8(results.private);
 | |
|           pubDer = x509.packSpki(results.public);
 | |
|           Rasha.export({ jwk: results.private, format: 'pkcs1' }).then(function (pem) {
 | |
|             $('.js-input-pem-pkcs1-private').innerText = pem;
 | |
|             $('.js-toc-pem-pkcs1-private').hidden = false;
 | |
|           });
 | |
|           Rasha.export({ jwk: results.private, format: 'pkcs8' }).then(function (pem) {
 | |
|             $('.js-input-pem-pkcs8-private').innerText = pem;
 | |
|             $('.js-toc-pem-pkcs8-private').hidden = false;
 | |
|           });
 | |
|           Rasha.export({ jwk: results.public, format: 'pkcs1' }).then(function (pem) {
 | |
|             $('.js-input-pem-pkcs1-public').innerText = pem;
 | |
|             $('.js-toc-pem-pkcs1-public').hidden = false;
 | |
|           });
 | |
|           Rasha.export({ jwk: results.public, format: 'spki' }).then(function (pem) {
 | |
|             $('.js-input-pem-spki-public').innerText = pem;
 | |
|             $('.js-toc-pem-spki-public').hidden = false;
 | |
|           });
 | |
|         }
 | |
| 
 | |
|         $('.js-der-public').innerText = pubDer;
 | |
|         $('.js-toc-der-public').hidden = false;
 | |
|         $('.js-der-private').innerText = privDer;
 | |
|         $('.js-toc-der-private').hidden = false;
 | |
|         $('.js-jwk').innerText = JSON.stringify(results, null, 2);
 | |
|         $('.js-loading').hidden = true;
 | |
|         $('.js-jwk').hidden = false;
 | |
|         $$('input').map(function ($el) { $el.disabled = false; });
 | |
|         $$('button').map(function ($el) { $el.disabled = false; });
 | |
|         $('.js-toc-jwk').hidden = false;
 | |
| 
 | |
|         $('.js-create-account').hidden = false;
 | |
|         $('.js-create-csr').hidden = false;
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     $('form.js-acme-account').addEventListener('submit', function (ev) {
 | |
|       ev.preventDefault();
 | |
|       ev.stopPropagation();
 | |
|       $('.js-loading').hidden = false;
 | |
|       var acme = ACME.create({
 | |
|         Keypairs: Keypairs
 | |
|       , CSR: CSR
 | |
|       });
 | |
|       acme.init('https://acme-staging-v02.api.letsencrypt.org/directory').then(function (result) {
 | |
|         console.log('acme result', result);
 | |
|         var privJwk = JSON.parse($('.js-jwk').innerText).private;
 | |
|         var email = $('.js-email').value;
 | |
|         return acme.accounts.create({
 | |
|           email: email
 | |
|         , agreeToTerms: checkTos
 | |
|         , accountKeypair: { privateKeyJwk: privJwk }
 | |
|         }).then(function (account) {
 | |
|           console.log("account created result:", account);
 | |
|           accountStuff.account = account;
 | |
|           accountStuff.privateJwk = privJwk;
 | |
|           accountStuff.email = email;
 | |
|           accountStuff.acme = acme;
 | |
|           $('.js-create-order').hidden = false;
 | |
|           $('.js-toc-acme-account-response').hidden = false;
 | |
|           $('.js-acme-account-response').innerText = JSON.stringify(account, null, 2);
 | |
|         }).catch(function (err) {
 | |
|           console.error("A bad thing happened:");
 | |
|           console.error(err);
 | |
|           window.alert(err.message || JSON.stringify(err, null, 2));
 | |
|         });
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     $('form.js-csr').addEventListener('submit', function (ev) {
 | |
|       ev.preventDefault();
 | |
|       ev.stopPropagation();
 | |
|       generateCsr();
 | |
|     });
 | |
| 
 | |
|     $('form.js-acme-order').addEventListener('submit', function (ev) {
 | |
|       ev.preventDefault();
 | |
|       ev.stopPropagation();
 | |
|       var account = accountStuff.account;
 | |
|       var privJwk = accountStuff.privateJwk;
 | |
|       var email = accountStuff.email;
 | |
|       var acme = accountStuff.acme;
 | |
| 
 | |
| 
 | |
|       var domains = ($('.js-domains').value||'example.com').split(/[, ]+/g);
 | |
|       return getDomainPrivkey().then(function (domainPrivJwk) {
 | |
|         console.log('Has CSR already?');
 | |
|         console.log(accountStuff.csr);
 | |
|         return acme.certificates.create({
 | |
|           accountKeypair: { privateKeyJwk: privJwk }
 | |
|         , account: account
 | |
|         , serverKeypair: { privateKeyJwk: domainPrivJwk }
 | |
|         , csr: accountStuff.csr
 | |
|         , domains: domains
 | |
|         , skipDryRun: $('input[name="skip-dryrun"]:checked') && true
 | |
|         , agreeToTerms: checkTos
 | |
|         , challenges: {
 | |
|             'dns-01': {
 | |
|               set: function (opts) {
 | |
|                 console.info('dns-01 set challenge:');
 | |
|                 console.info('TXT', opts.dnsHost);
 | |
|                 console.info(opts.dnsAuthorization);
 | |
|                 return new Promise(function (resolve) {
 | |
|                   while (!window.confirm("Did you set the challenge?")) {}
 | |
|                   resolve();
 | |
|                 });
 | |
|               }
 | |
|             , remove: function (opts) {
 | |
|                 console.log('dns-01 remove challenge:');
 | |
|                 console.info('TXT', opts.dnsHost);
 | |
|                 console.info(opts.dnsAuthorization);
 | |
|                 return new Promise(function (resolve) {
 | |
|                   while (!window.confirm("Did you delete the challenge?")) {}
 | |
|                   resolve();
 | |
|                 });
 | |
|               }
 | |
|             }
 | |
|           , 'http-01': {
 | |
|               set: function (opts) {
 | |
|                 console.info('http-01 set challenge:');
 | |
|                 console.info(opts.challengeUrl);
 | |
|                 console.info(opts.keyAuthorization);
 | |
|                 return new Promise(function (resolve) {
 | |
|                   while (!window.confirm("Did you set the challenge?")) {}
 | |
|                   resolve();
 | |
|                 });
 | |
|               }
 | |
|             , remove: function (opts) {
 | |
|                 console.log('http-01 remove challenge:');
 | |
|                 console.info(opts.challengeUrl);
 | |
|                 console.info(opts.keyAuthorization);
 | |
|                 return new Promise(function (resolve) {
 | |
|                   while (!window.confirm("Did you delete the challenge?")) {}
 | |
|                   resolve();
 | |
|                 });
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         , challengeTypes: [$('input[name="acme-challenge-type"]:checked').value]
 | |
|         }).then(function (results) {
 | |
|           console.log('Got Certificates:');
 | |
|           console.log(results);
 | |
|           $('.js-toc-acme-order-response').hidden = false;
 | |
|           $('.js-acme-order-response').innerText = JSON.stringify(results, null, 2);
 | |
|         }).catch(function (err) {
 | |
|           console.error("challenge failed:");
 | |
|           console.error(err);
 | |
|           window.alert("failed! " + err.message || JSON.stringify(err));
 | |
|         });
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     $('.js-generate').hidden = false;
 | |
|   }
 | |
| 
 | |
|   function getDomainPrivkey() {
 | |
|     if (accountStuff.domainPrivateJwk) { return Promise.resolve(accountStuff.domainPrivateJwk); }
 | |
|     return Keypairs.generate({
 | |
|       kty: $('input[name="kty"]:checked').value
 | |
|     , namedCurve: $('input[name="ec-crv"]:checked').value
 | |
|     , modulusLength: $('input[name="rsa-len"]:checked').value
 | |
|     }).then(function (pair) {
 | |
|       console.log('domain keypair:', pair);
 | |
|       accountStuff.domainPrivateJwk = pair.private;
 | |
|       return pair.private;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   function generateCsr() {
 | |
|     var domains = ($('.js-domains').value||'example.com').split(/[, ]+/g);
 | |
|     //var privJwk = JSON.parse($('.js-jwk').innerText).private;
 | |
|     return getDomainPrivkey().then(function (privJwk) {
 | |
|       accountStuff.domainPrivateJwk = privJwk;
 | |
|       return CSR({ jwk: privJwk, domains: domains }).then(function (pem) {
 | |
|         // Verify with https://www.sslshopper.com/csr-decoder.html
 | |
|         accountStuff.csr = pem;
 | |
|         console.log('Created CSR:');
 | |
|         console.log(pem);
 | |
| 
 | |
|         console.log('CSR info:');
 | |
|         console.log(CSR._info(pem));
 | |
| 
 | |
|         return pem;
 | |
|       });
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   window.addEventListener('load', run);
 | |
| }());
 |