/*global Promise*/ (function () { 'use strict'; var Keypairs = require('@root/keypairs'); var Rasha = require('@root/acme/rsa'); var Eckles = require('@root/acme/ecdsa'); var x509 = require('@root/acme/x509'); var CSR = require('@root/csr'); var ACME = require('@root/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); })();