use ACME to create account
This commit is contained in:
		
							parent
							
								
									b81ff7550b
								
							
						
					
					
						commit
						8b2e6e69d0
					
				@ -10,6 +10,7 @@ try {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var pkg = require('../package.json');
 | 
					var pkg = require('../package.json');
 | 
				
			||||||
 | 
					var Keypairs = require('keypairs');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var crypto = require('crypto');
 | 
					var crypto = require('crypto');
 | 
				
			||||||
//var url = require('url');
 | 
					//var url = require('url');
 | 
				
			||||||
@ -422,7 +423,7 @@ controllers.newAccount = function (req, res) {
 | 
				
			|||||||
    // TODO clean up error messages to be similar to ACME
 | 
					    // TODO clean up error messages to be similar to ACME
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // check if there's a public key
 | 
					    // check if there's a public key
 | 
				
			||||||
    if (!req.jws || !req.jws.header.kid || !req.jws.header.jwk) {
 | 
					    if (!req.jws || !req.jws.header.jwk) {
 | 
				
			||||||
      res.statusCode = 422;
 | 
					      res.statusCode = 422;
 | 
				
			||||||
      res.send({ error: { message: "jws body was not present or could not be validated" } });
 | 
					      res.send({ error: { message: "jws body was not present or could not be validated" } });
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
@ -448,7 +449,7 @@ controllers.newAccount = function (req, res) {
 | 
				
			|||||||
    return verifyJws(req.jws.header.jwk, req.jws).then(function (verified) {
 | 
					    return verifyJws(req.jws.header.jwk, req.jws).then(function (verified) {
 | 
				
			||||||
      if (!verified) {
 | 
					      if (!verified) {
 | 
				
			||||||
        res.statusCode = 422;
 | 
					        res.statusCode = 422;
 | 
				
			||||||
        res.send({ error: { message: "jws body was not present or could not be validated" } });
 | 
					        res.send({ error: { message: "jws body failed verification" } });
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -483,7 +484,7 @@ controllers.newAccount = function (req, res) {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
          res.statusCode = 201;
 | 
					          res.statusCode = 201;
 | 
				
			||||||
          account = {};
 | 
					          account = {};
 | 
				
			||||||
          account._id = crypto.randomBytes(16).toString('base64');
 | 
					          account._id = toUrlSafe(crypto.randomBytes(16).toString('base64'));
 | 
				
			||||||
          // TODO be better about this
 | 
					          // TODO be better about this
 | 
				
			||||||
          account.location = myBaseUrl + '/acme/accounts/' + account._id;
 | 
					          account.location = myBaseUrl + '/acme/accounts/' + account._id;
 | 
				
			||||||
          account.thumb = thumb;
 | 
					          account.thumb = thumb;
 | 
				
			||||||
@ -620,12 +621,38 @@ function jwsEggspress(req, res, next) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var ua = req.headers['user-agent'];
 | 
					  var ua = req.headers['user-agent'];
 | 
				
			||||||
 | 
					  var trusted = false;
 | 
				
			||||||
  var vjwk;
 | 
					  var vjwk;
 | 
				
			||||||
  var pubs;
 | 
					  var pubs;
 | 
				
			||||||
 | 
					  var kid = req.jws.header.kid;
 | 
				
			||||||
 | 
					  var p = Promise.resolve();
 | 
				
			||||||
 | 
					  if (!kid && !req.jws.header.jwk) {
 | 
				
			||||||
 | 
					    res.send({ error: { message: "jws protected header must include either 'kid' or 'jwk'" } });
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (req.jws.header.jwk) {
 | 
				
			||||||
 | 
					    if (kid) {
 | 
				
			||||||
 | 
					      // TODO kid and jwk are mutually exclusive
 | 
				
			||||||
 | 
					      //res.send({ error: { message: "jws protected header must not include both 'kid' and 'jwk'" } });
 | 
				
			||||||
 | 
					      //return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    kid = req.jws.header.jwk.kid;
 | 
				
			||||||
 | 
					    p = Keypairs.thumbprint({ jwk: req.jws.header.jwk }).then(function (thumb) {
 | 
				
			||||||
 | 
					      if (kid && kid !== thumb) {
 | 
				
			||||||
 | 
					        res.send({ error: { message: "jwk included 'kid' for key id, but it did not match the key's thumbprint" } });
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      kid = thumb;
 | 
				
			||||||
 | 
					      req.jws.header.jwk.kid = thumb;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Check if this is a key we already trust
 | 
					  // Check if this is a key we already trust
 | 
				
			||||||
  DB.pubs.some(function (jwk) {
 | 
					  DB.pubs.some(function (jwk) {
 | 
				
			||||||
    if (jwk.kid === req.jws.header.kid) {
 | 
					    if (jwk.kid === kid) {
 | 
				
			||||||
 | 
					      trusted = true;
 | 
				
			||||||
      vjwk = jwk;
 | 
					      vjwk = jwk;
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -645,33 +672,32 @@ function jwsEggspress(req, res, next) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Check if there aren't any keys that we trust
 | 
					  p.then(function () {
 | 
				
			||||||
  // and this has signed itself, then make it a key we trust
 | 
					    // Check if there aren't any keys that we trust
 | 
				
			||||||
  // (TODO: move this all to the new account function)
 | 
					    // and this has signed itself, then make it a key we trust
 | 
				
			||||||
  if ((0 === pubs.length && req.jws.header.jwk)) {
 | 
					    // (TODO: move this all to the new account function)
 | 
				
			||||||
    vjwk = req.jws.header.jwk;
 | 
					    if (0 === pubs.length) { trusted = true; }
 | 
				
			||||||
    if (!vjwk.kid) { throw Error("Impossible: no key id"); }
 | 
					    if (!vjwk) { vjwk = req.jws.header.jwk; }
 | 
				
			||||||
  }
 | 
					    // Don't verify if it can't be verified
 | 
				
			||||||
 | 
					    if (!vjwk) { return null; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Don't verify if it can't be verified
 | 
					    // Run the  verification
 | 
				
			||||||
  if (!vjwk) {
 | 
					    return p.then(function () {
 | 
				
			||||||
    next();
 | 
					      return verifyJws(vjwk, req.jws).then(function (verified) {
 | 
				
			||||||
    return;
 | 
					        if (true !== verified) { return null; }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Run the  verification
 | 
					        // Mark as verified
 | 
				
			||||||
  return verifyJws(vjwk, req.jws).then(function (verified) {
 | 
					        req.jws.verified = verified;
 | 
				
			||||||
    if (true !== verified) { return; }
 | 
					        req.jws.trusted = trusted;
 | 
				
			||||||
 | 
					        vjwk.useragent = ua;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Mark as verified
 | 
					        // (double check) DO NOT save if there are existing pubs
 | 
				
			||||||
    req.jws.verified = verified;
 | 
					        if (0 !== pubs.length) { return null; }
 | 
				
			||||||
    vjwk.useragent = ua;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // (double check) DO NOT save if there are existing pubs
 | 
					        DB.pubs.push(vjwk);
 | 
				
			||||||
    if (0 !== pubs.length) { return; }
 | 
					        return keystore.set(vjwk.kid + PUBEXT, vjwk);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    DB.pubs.push(vjwk);
 | 
					    });
 | 
				
			||||||
    return keystore.set(vjwk.kid + PUBEXT, vjwk);
 | 
					 | 
				
			||||||
  }).then(function () {
 | 
					  }).then(function () {
 | 
				
			||||||
    next();
 | 
					    next();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -1001,9 +1027,12 @@ function handleApi() {
 | 
				
			|||||||
    next();
 | 
					    next();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  app.get('/acme/directory', function (req, res) {
 | 
					  app.get('/acme/directory', function (req, res) {
 | 
				
			||||||
 | 
					    var myBaseUrl = (req.connection.encrypted ? 'https' : 'http') + '://' + req.headers.host;
 | 
				
			||||||
    res.send({
 | 
					    res.send({
 | 
				
			||||||
      'new-nonce': '/acme/new-nonce'
 | 
					      'newNonce': '/acme/new-nonce'
 | 
				
			||||||
    , 'new-account': '/acme/new-acct'
 | 
					    , 'newAccount': '/acme/new-acct'
 | 
				
			||||||
 | 
					      // TODO link to the terms that the user selects
 | 
				
			||||||
 | 
					    , 'meta': { 'termsOfService': myBaseUrl + '/acme/terms.html' }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  app.head('/acme/new-nonce', controllers.newNonce);
 | 
					  app.head('/acme/new-nonce', controllers.newNonce);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@
 | 
				
			|||||||
var Vue = window.Vue;
 | 
					var Vue = window.Vue;
 | 
				
			||||||
var Telebit = window.TELEBIT;
 | 
					var Telebit = window.TELEBIT;
 | 
				
			||||||
var Keypairs = window.Keypairs;
 | 
					var Keypairs = window.Keypairs;
 | 
				
			||||||
 | 
					var ACME = window.ACME;
 | 
				
			||||||
var api = {};
 | 
					var api = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@ -516,25 +517,59 @@ function run(key) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TODO protect key with passphrase (or QR code?)
 | 
					// TODO protect key with passphrase (or QR code?)
 | 
				
			||||||
function getKey() {
 | 
					function getKey() {
 | 
				
			||||||
  var key;
 | 
					  var jwk;
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    key = JSON.parse(localStorage.getItem('key'));
 | 
					    jwk = JSON.parse(localStorage.getItem('key'));
 | 
				
			||||||
  } catch(e) {
 | 
					  } catch(e) {
 | 
				
			||||||
    // ignore
 | 
					    // ignore
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (key && key.kid && key.d) {
 | 
					  if (jwk && jwk.kid && jwk.d) {
 | 
				
			||||||
    return Promise.resolve(key);
 | 
					    return Promise.resolve(jwk);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return Keypairs.generate().then(function (pair) {
 | 
					  return Keypairs.generate().then(function (pair) {
 | 
				
			||||||
    key = pair.private;
 | 
					    jwk = pair.private;
 | 
				
			||||||
    localStorage.setItem('key', JSON.stringify(key));
 | 
					    localStorage.setItem('key', JSON.stringify(jwk));
 | 
				
			||||||
    return key;
 | 
					    return jwk;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getEmail() {
 | 
				
			||||||
 | 
					  return Promise.resolve().then(function () {
 | 
				
			||||||
 | 
					    var email = localStorage.getItem('email');
 | 
				
			||||||
 | 
					    if (email) { return email; }
 | 
				
			||||||
 | 
					    while (!email) {
 | 
				
			||||||
 | 
					      email = window.prompt("Email address (device owner)?");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return email;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function requestAccount() {
 | 
				
			||||||
 | 
					  return getKey().then(function (jwk) {
 | 
				
			||||||
 | 
					    return getEmail().then(function(email) {
 | 
				
			||||||
 | 
					      // creates new or returns existing
 | 
				
			||||||
 | 
					      var acme = ACME.create({});
 | 
				
			||||||
 | 
					      var url = window.location.protocol + '//' + window.location.host + '/acme/directory';
 | 
				
			||||||
 | 
					      return acme.init(url).then(function () {
 | 
				
			||||||
 | 
					        return acme.accounts.create({
 | 
				
			||||||
 | 
					          agreeToTerms: function (tos) { return tos; }
 | 
				
			||||||
 | 
					        , accountKeypair: { privateKeyJwk: jwk }
 | 
				
			||||||
 | 
					        , email: email
 | 
				
			||||||
 | 
					        }).then(function (account) {
 | 
				
			||||||
 | 
					          console.log('account:');
 | 
				
			||||||
 | 
					          console.log(account);
 | 
				
			||||||
 | 
					          if (account.id) {
 | 
				
			||||||
 | 
					            localStorage.setItem('email', email);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return jwk;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.api = api;
 | 
					window.api = api;
 | 
				
			||||||
getKey().then(function (key) {
 | 
					requestAccount().then(function (jwk) {
 | 
				
			||||||
  run(key);
 | 
					  run(jwk);
 | 
				
			||||||
  setTimeout(function () {
 | 
					  setTimeout(function () {
 | 
				
			||||||
    document.body.hidden = false;
 | 
					    document.body.hidden = false;
 | 
				
			||||||
  }, 50);
 | 
					  }, 50);
 | 
				
			||||||
 | 
				
			|||||||
@ -2567,7 +2567,7 @@ ACME.create = function create(me) {
 | 
				
			|||||||
  // me.debug = true;
 | 
					  // me.debug = true;
 | 
				
			||||||
  me.challengePrefixes = ACME.challengePrefixes;
 | 
					  me.challengePrefixes = ACME.challengePrefixes;
 | 
				
			||||||
  me.Keypairs = me.Keypairs || exports.Keypairs || require('keypairs').Keypairs;
 | 
					  me.Keypairs = me.Keypairs || exports.Keypairs || require('keypairs').Keypairs;
 | 
				
			||||||
  me.CSR = me.CSR || exports.cSR || require('CSR').CSR;
 | 
					  me.CSR = me.CSR || exports.CSR || require('CSR').CSR;
 | 
				
			||||||
  me._nonces = [];
 | 
					  me._nonces = [];
 | 
				
			||||||
  me._canUse = {};
 | 
					  me._canUse = {};
 | 
				
			||||||
  if (!me._baseUrl) {
 | 
					  if (!me._baseUrl) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user