diff --git a/browserid/lib/ca.js b/browserid/lib/ca.js
new file mode 100644
index 0000000000000000000000000000000000000000..78be143f98ec69443dc7dc66a3b0ad67c46ef372
--- /dev/null
+++ b/browserid/lib/ca.js
@@ -0,0 +1,94 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *     Ben Adida <benadida@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// certificate authority
+
+var jwcert = require('../../lib/jwcrypto/jwcert'),
+    jws = require('../../lib/jwcrypto/jws'),
+    configuration = require('../../libs/configuration'),
+    path = require("path"),
+    fs = require("fs");
+
+function loadSecretKey(name, dir) {
+  var p = path.join(dir, name + ".secretkey");
+  var fileExists = false;
+  var secret = undefined;
+
+  try{ secret = fs.readFileSync(p).toString(); } catch(e) {};
+
+  if (secret === undefined) {
+    return null;
+  }
+
+  // parse it
+  // it should be a JSON structure with alg and serialized key
+  // {alg: <ALG>, value: <SERIALIZED_KEY>}
+  var key = JSON.parse(secret);
+  return jws.getByAlg(key.alg).SecretKey.deserialize(key.value);
+}
+
+function loadPublicKey(name, dir) {
+  var p = path.join(dir, name + ".publickey");
+  var fileExists = false;
+  var secret = undefined;
+
+  try{ secret = fs.readFileSync(p).toString(); } catch(e) {};
+
+  if (secret === undefined) {
+    return null;
+  }
+
+  // parse it
+  // it should be a JSON structure with alg and serialized key
+  // {alg: <ALG>, value: <SERIALIZED_KEY>}
+  var key = JSON.parse(secret);
+  return jws.getByAlg(key.alg).PublicKey.deserialize(key.value);
+}
+
+var SECRET_KEY = loadSecretKey('root', configuration.get('var_path'));
+var PUBLIC_KEY = loadPublicKey('root', configuration.get('var_path'));
+
+function certify(email, serializedPublicKey) {
+  return "cert";
+}
+
+function verifyChain(certChain, serializedPublicKey) {
+  return true;
+}
+
+// exports, not the key stuff
+exports.certify = certify;
+exports.verifyChain = verifyChain;
diff --git a/browserid/lib/wsapi.js b/browserid/lib/wsapi.js
index 43cb858fd26c3489eb066aec19e0c1527b576d09..36665a045036020f51a0db0e6edc02902a93cf03 100644
--- a/browserid/lib/wsapi.js
+++ b/browserid/lib/wsapi.js
@@ -298,8 +298,10 @@ function setup(app) {
       // not same account? big fat error
       if (!sameAccount) return httputils.badRequest(resp, "that email does not belong to you");
 
+      // FIXME: make sure it's truly a public key
+      
       // same account, we certify the key
-      var cert = ca.certify(email, pubkey);
+      var cert = ca.certify(email, req.body.pubkey);
       resp.json(cert);
     });
   });
diff --git a/browserid/tests/cert-emails-test.js b/browserid/tests/cert-emails-test.js
new file mode 100755
index 0000000000000000000000000000000000000000..fb010398ccdb8d25120668c7aba811ac90cfe1f6
--- /dev/null
+++ b/browserid/tests/cert-emails-test.js
@@ -0,0 +1,137 @@
+#!/usr/bin/env node
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+require('./lib/test_env.js');
+
+const assert = require('assert'),
+vows = require('vows'),
+start_stop = require('./lib/start-stop.js'),
+wsapi = require('./lib/wsapi.js'),
+email = require('../lib/email.js'),
+ca = require('../lib/ca.js'),
+jwcert = require('../../lib/jwcrypto/jwcert');
+
+var suite = vows.describe('cert-emails');
+
+// disable vows (often flakey?) async error behavior
+suite.options.error = false;
+
+start_stop.addStartupBatches(suite);
+
+// ever time a new token is sent out, let's update the global
+// var 'token'
+var token = undefined;
+email.setInterceptor(function(email, site, secret) { token = secret; });
+
+// INFO: some of these tests are repeat of sync-emails... to set
+// things up properly for key certification
+
+// create a new account via the api with (first address)
+suite.addBatch({
+  "stage an account": {
+    topic: wsapi.post('/wsapi/stage_user', {
+      email: 'syncer@somehost.com',
+      pass: 'fakepass',
+      pubkey: 'fakekey',
+      site:'fakesite.com'
+    }),
+    "yields a sane token": function(r, err) {
+      assert.strictEqual(typeof token, 'string');
+    }
+  }
+});
+
+suite.addBatch({
+  "verifying account ownership": {
+    topic: function() {
+      wsapi.get('/wsapi/prove_email_ownership', { token: token }).call(this);
+    },
+    "works": function(r, err) {
+      assert.equal(r.code, 200);
+      assert.strictEqual(true, JSON.parse(r.body));
+    }
+  }
+});
+
+suite.addBatch({
+  "calling registration_status after a registration is complete": {
+    topic: wsapi.get("/wsapi/registration_status"),
+    "yields a HTTP 200": function (r, err) {
+      assert.strictEqual(r.code, 200);
+    },
+    "returns a json encoded string - `complete`": function (r, err) {
+      assert.strictEqual(JSON.parse(r.body), "complete");
+    }
+  }
+});
+
+var cert_key_url = "/wsapi/cert_key";
+
+suite.addBatch({
+  "cert key with no parameters": {
+    topic: wsapi.post(cert_key_url, {}),
+    "fails with HTTP 400" : function(r, err) {
+      assert.strictEqual(r.code, 400);
+    }
+  },
+  "cert key invoked with just an email": {  
+    topic: wsapi.post(cert_key_url, { email: 'syncer@somehost.com' }),
+    "returns a 400" : function(r, err) {
+      assert.strictEqual(r.code, 400);
+    }
+  },
+  "cert key invoked with proper argument": {  
+    topic: wsapi.post(cert_key_url, { email: 'syncer@somehost.com', pubkey: 'fakepubkey' }),
+    "returns a response with a proper content-type" : function(r, err) {
+      assert.strictEqual(r.code, 200);
+      assert.isTrue(r.headers['content-type'].indexOf('application/json; charset=utf-8') > -1);
+    },
+    "returns a proper cert": function(r, err) {
+      var cert = new jwcert.JWCert();
+      cert.parse(r.body);
+
+      assert.isTrue(ca.verifyChain([cert], 'fakepubkey'));
+    }
+  }
+  // NOTE: db-test has more thorough tests of the algorithm behind the sync_emails API
+});
+
+start_stop.addShutdownBatches(suite);
+
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);
diff --git a/browserid/tests/set-key-wsapi-test.js b/browserid/tests/set-key-wsapi-test.js
index b04579c6860ad7f9d129121b711c07def98245cc..1ab26b7a53b913dbeafc11345aa1e5b458d21689 100755
--- a/browserid/tests/set-key-wsapi-test.js
+++ b/browserid/tests/set-key-wsapi-test.js
@@ -100,7 +100,7 @@ suite.addBatch({
       email: 'setkeyabuser@somehost.com',
       pubkey: 'fakekey'
     }),
-    "fails with a false return0" : function(r, err) {
+    "fails with a false return" : function(r, err) {
       assert.strictEqual(r.code, 200);
       assert.strictEqual(r.body, 'false');
     }
diff --git a/libs/secrets.js b/libs/secrets.js
index b57bf0ea3a0e57fcba6487ec13e118348bec6336..d6cc4db5ee3f652b2afeb8170b816dc1708ec867 100644
--- a/libs/secrets.js
+++ b/libs/secrets.js
@@ -35,7 +35,8 @@
 
 const
 path = require('path'),
-fs = require('fs');
+fs = require('fs'),
+jws = require('../lib/jwcrypto/jws');
 
 exports.generate = function(chars) {
   var str = "";
@@ -59,3 +60,4 @@ exports.hydrateSecret = function(name, dir) {
   }
   return secret;
 };
+