diff --git a/exec/load/acmev2.js b/exec/load/acmev2.js index 7e40f9dd60caf2b7649c2736b81ea9e806e80906..c2aeb1a7f6136094db0eef4456bf4767cdec8a40 100644 --- a/exec/load/acmev2.js +++ b/exec/load/acmev2.js @@ -12,27 +12,27 @@ * * // Save the key_id somewhere so it can be reused. * - * var order = acme->create_new_order({identifiers:[{type:'dns',value:'*.example.com'}]); - * var authz = acme->get_authorization(order->authorizations()[0]); - * var challenges = authz->challenges(); + * var order = acme.create_new_order({identifiers:[{type:"dns",value:"example.com"}]}); + * var authz = acme.get_authorization(order.authorizations[0]); + * var challenges = authz.challenges; * * // Pick a challenge, and satisfy it. * - * acme->accept_challenge(challenge); + * acme.accept_challenge(challenge); * - * while (!acme->poll_authorization(authz)) + * while (!acme->poll_authorization(order.authorizations[0])) * mswait(1000); * * // Make a key and CSR for *.example.com * - * acme->finalize_order(order, csr); + * order = acme.finalize_order(order, csr); * * while (order.status !== 'valid') { * mswait(1000); - * acme->poll_order(order); + * order = acme.poll_order(order); * } * - * var certificate_url = order->certificate(); + * var cert = acme.get_cert(order); */ require("http.js", "HTTPRequest"); @@ -87,6 +87,70 @@ ACMEv2.prototype.create_new_account = function(opts) return this.post('newAccount', opts); } +ACMEv2.prototype.create_new_order = function(opts) +{ + var ret; + + if (opts.identifiers === undefined) + throw("create_new_order() requires an identifier in opts"); + ret = JSON.parse(this.post('newOrder', opts)); + if (this.last_headers.Location !== undefined && this.last_headers.Location[0] !== undefined) { + ret.Location=this.last_headers.Location[0]; + print("Location: "+ret.Location); + } + return ret; +} + +ACMEv2.prototype.accept_challenge = function(challenge) +{ + var opts={keyAuthorization:challenge.token+"."+this.thumbprint()}; + + return JSON.parse(this.post_url(challenge.url, opts)); +} + +ACMEv2.prototype.poll_authorization = function(auth) +{ + var ret = JSON.parse(this.ua.Get(auth)); + + for (var challenge in ret.challenges) { + if (ret.challenges[challenge].status == 'valid') + return true; + } + return false; +} + +ACMEv2.prototype.finalize_order = function(order, csr) +{ + var opts = {}; + + if (order === undefined) + throw("Missing order"); + if (csr === undefined) + throw("Missing csr"); + if (typeof(csr) != 'object' || csr.export === undefined) + throw("Invalid csr"); + opts.csr = this.base64url(csr.export(CryptCert.FORMAT.CERTIFICATE)); + return JSON.parse(this.post_url(order.finalize, opts)); +} + +ACMEv2.prototype.poll_order = function(order) +{ + var loc = order.Location; + if (loc === undefined) + throw("No order location!"); + var ret = JSON.parse(this.ua.Get(loc)); + ret.Location = loc; + return ret; +} + +ACMEv2.prototype.get_cert = function(order) +{ + if (order.certificate === undefined) + throw("Order has no certificate!"); + var cert = this.ua.Get(order.certificate); + return new CryptCert(cert); +} + ACMEv2.prototype.post = function(link, data) { var post_method; @@ -97,7 +161,7 @@ ACMEv2.prototype.post = function(link, data) url = this.get_directory(link)[link]; if (url === undefined) throw('Unknown link name: "'+link+'"'); - return this.post_url(url, data, post_method); + return this.post_url(url, data); } ACMEv2.prototype.get_nonce = function() @@ -114,6 +178,11 @@ ACMEv2.prototype.get_nonce = function() return ret; } +ACMEv2.prototype.get_authorization = function(url) +{ + return JSON.parse(this.ua.Get(url)); +} + ACMEv2.prototype.base64url = function(string) { string = base64_encode(string); @@ -168,6 +237,18 @@ ACMEv2.prototype.store_headers = function(update_nonce) this.last_headers = h; } +ACMEv2.prototype.thumbprint = function() +{ + var jwk = JSON.stringify({e:this.key.public_key.e, kty:'RSA', n:this.key.public_key.n}); + var shactx; + + shactx = new CryptContext(CryptContext.ALGO.SHA2); + shactx.encrypt(jwk); + shactx.encrypt(''); + var MD = shactx.hashvalue; + return this.base64url(MD); +} + ACMEv2.prototype.post_url = function(url, data, post_method) { var protected = {}; @@ -202,7 +283,7 @@ ACMEv2.prototype.post_url = function(url, data, post_method) body = body.replace(/,"/g, ', "'); ret = this.ua.Post(url, body); this.store_headers(true); - if (this.last_headers['Location'] !== undefined) + if (this.last_headers['Location'] !== undefined && this.key_id === undefined) this.key_id = this.last_headers['Location'][0]; return ret; }