Bug 956816 - Implement (de)serializing Android BrowserIDKeyPair instances. r=rnewman
authorNick Alexander <nalexander@mozilla.com>
Tue, 07 Jan 2014 18:27:45 -0800
changeset 162513 19bd00ed4b48c58e9a6cd920d304c0a485f4872d
parent 162512 65d443d32bf6fabe6b10ee116da8a068546c7fa8
child 162514 6fbf42e15bb1803e21f92de66ab63a3fc6b3892b
push id25956
push usernalexander@mozilla.com
push dateWed, 08 Jan 2014 18:09:17 +0000
treeherdermozilla-central@a30925f7f28e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs956816
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 956816 - Implement (de)serializing Android BrowserIDKeyPair instances. r=rnewman
mobile/android/base/browserid/BrowserIDKeyPair.java
mobile/android/base/browserid/DSACryptoImplementation.java
mobile/android/base/browserid/JSONWebTokenUtils.java
mobile/android/base/browserid/RSACryptoImplementation.java
mobile/android/base/browserid/SigningPrivateKey.java
mobile/android/base/browserid/VerifyingPublicKey.java
mobile/android/base/fxa/sync/FxAccount.java
mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
mobile/android/tests/background/junit3/src/fxa/TestBrowserIDKeyPairGeneration.java
--- a/mobile/android/base/browserid/BrowserIDKeyPair.java
+++ b/mobile/android/base/browserid/BrowserIDKeyPair.java
@@ -1,23 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.browserid;
 
+import org.mozilla.gecko.sync.ExtendedJSONObject;
+
 public class BrowserIDKeyPair {
+  public static final String JSON_KEY_PRIVATEKEY = "privateKey";
+  public static final String JSON_KEY_PUBLICKEY = "publicKey";
+
   protected final SigningPrivateKey privateKey;
   protected final VerifyingPublicKey publicKey;
 
   public BrowserIDKeyPair(SigningPrivateKey privateKey, VerifyingPublicKey publicKey) {
     this.privateKey = privateKey;
     this.publicKey = publicKey;
   }
 
   public SigningPrivateKey getPrivate() {
     return this.privateKey;
   }
 
   public VerifyingPublicKey getPublic() {
     return this.publicKey;
   }
+
+  public ExtendedJSONObject toJSONObject() {
+    ExtendedJSONObject o = new ExtendedJSONObject();
+    o.put(JSON_KEY_PRIVATEKEY, privateKey.toJSONObject());
+    o.put(JSON_KEY_PUBLICKEY, publicKey.toJSONObject());
+    return o;
+  }
 }
--- a/mobile/android/base/browserid/DSACryptoImplementation.java
+++ b/mobile/android/base/browserid/DSACryptoImplementation.java
@@ -15,39 +15,54 @@ import java.security.interfaces.DSAParam
 import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAPublicKey;
 import java.security.spec.DSAPrivateKeySpec;
 import java.security.spec.DSAPublicKeySpec;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.Utils;
 
 public class DSACryptoImplementation {
   public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
   public static final int SIGNATURE_LENGTH_BYTES = 40; // DSA signatures are always 40 bytes long.
 
+  /**
+   * Parameters are serialized as hex strings. Hex-versus-decimal was
+   * reverse-engineered from what the Persona public verifier accepted. We
+   * expect to follow the JOSE/JWT spec as it solidifies, and that will probably
+   * mean unifying this base.
+   */
+  protected static final int SERIALIZATION_BASE = 16;
+
   protected static class DSAVerifyingPublicKey implements VerifyingPublicKey {
     protected final DSAPublicKey publicKey;
 
     public DSAVerifyingPublicKey(DSAPublicKey publicKey) {
       this.publicKey = publicKey;
     }
 
+    /**
+     * Serialize to a JSON object.
+     * <p>
+     * Parameters are serialized as hex strings. Hex-versus-decimal was
+     * reverse-engineered from what the Persona public verifier accepted.
+     */
     @Override
-    public String serialize() {
+    public ExtendedJSONObject toJSONObject() {
       DSAParams params = publicKey.getParams();
       ExtendedJSONObject o = new ExtendedJSONObject();
       o.put("algorithm", "DS");
-      o.put("y", publicKey.getY().toString(16));
-      o.put("g", params.getG().toString(16));
-      o.put("p", params.getP().toString(16));
-      o.put("q", params.getQ().toString(16));
-      return o.toJSONString();
+      o.put("y", publicKey.getY().toString(SERIALIZATION_BASE));
+      o.put("g", params.getG().toString(SERIALIZATION_BASE));
+      o.put("p", params.getP().toString(SERIALIZATION_BASE));
+      o.put("q", params.getQ().toString(SERIALIZATION_BASE));
+      return o;
     }
 
     @Override
     public boolean verifyMessage(byte[] bytes, byte[] signature)
         throws GeneralSecurityException {
       if (bytes == null) {
         throw new IllegalArgumentException("bytes must not be null");
       }
@@ -82,26 +97,32 @@ public class DSACryptoImplementation {
       this.privateKey = privateKey;
     }
 
     @Override
     public String getAlgorithm() {
       return "DS" + (privateKey.getParams().getP().bitLength() + 7)/8;
     }
 
+    /**
+     * Serialize to a JSON object.
+     * <p>
+     * Parameters are serialized as decimal strings. Hex-versus-decimal was
+     * reverse-engineered from what the Persona public verifier accepted.
+     */
     @Override
-    public String serialize() {
+    public ExtendedJSONObject toJSONObject() {
       DSAParams params = privateKey.getParams();
       ExtendedJSONObject o = new ExtendedJSONObject();
       o.put("algorithm", "DS");
-      o.put("x", privateKey.getX().toString(16));
-      o.put("g", params.getG().toString(16));
-      o.put("p", params.getP().toString(16));
-      o.put("q", params.getQ().toString(16));
-      return o.toJSONString();
+      o.put("x", privateKey.getX().toString(SERIALIZATION_BASE));
+      o.put("g", params.getG().toString(SERIALIZATION_BASE));
+      o.put("p", params.getP().toString(SERIALIZATION_BASE));
+      o.put("q", params.getQ().toString(SERIALIZATION_BASE));
+      return o;
     }
 
     @Override
     public byte[] signMessage(byte[] bytes)
         throws GeneralSecurityException {
       if (bytes == null) {
         throw new IllegalArgumentException("bytes must not be null");
       }
@@ -116,17 +137,17 @@ public class DSACryptoImplementation {
       // This is awful, but signatures are always 40 bytes long.
       byte[] decoded = Utils.concatAll(
           Utils.hex2Byte(r.toString(16), SIGNATURE_LENGTH_BYTES / 2),
           Utils.hex2Byte(s.toString(16), SIGNATURE_LENGTH_BYTES / 2));
       return decoded;
     }
   }
 
-  public static BrowserIDKeyPair generateKeypair(int keysize)
+  public static BrowserIDKeyPair generateKeyPair(int keysize)
       throws NoSuchAlgorithmException {
     final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
     keyPairGenerator.initialize(keysize);
     final KeyPair keyPair = keyPairGenerator.generateKeyPair();
     DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
     DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
     return new BrowserIDKeyPair(new DSASigningPrivateKey(privateKey), new DSAVerifyingPublicKey(publicKey));
   }
@@ -163,9 +184,61 @@ public class DSACryptoImplementation {
     if (g == null) {
       throw new IllegalArgumentException("g must not be null");
     }
     KeySpec keySpec = new DSAPublicKeySpec(y, p, q, g);
     KeyFactory keyFactory = KeyFactory.getInstance("DSA");
     DSAPublicKey publicKey = (DSAPublicKey) keyFactory.generatePublic(keySpec);
     return new DSAVerifyingPublicKey(publicKey);
   }
+
+  public static SigningPrivateKey createPrivateKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
+    String algorithm = o.getString("algorithm");
+    if (!"DS".equals(algorithm)) {
+      throw new InvalidKeySpecException("algorithm must equal DS, was " + algorithm);
+    }
+    try {
+      BigInteger x = new BigInteger(o.getString("x"), SERIALIZATION_BASE);
+      BigInteger p = new BigInteger(o.getString("p"), SERIALIZATION_BASE);
+      BigInteger q = new BigInteger(o.getString("q"), SERIALIZATION_BASE);
+      BigInteger g = new BigInteger(o.getString("g"), SERIALIZATION_BASE);
+      return createPrivateKey(x, p, q, g);
+    } catch (NullPointerException e) {
+      throw new InvalidKeySpecException("x, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    } catch (NumberFormatException e) {
+      throw new InvalidKeySpecException("x, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    }
+  }
+
+  public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
+    String algorithm = o.getString("algorithm");
+    if (!"DS".equals(algorithm)) {
+      throw new InvalidKeySpecException("algorithm must equal DS, was " + algorithm);
+    }
+    try {
+      BigInteger y = new BigInteger(o.getString("y"), SERIALIZATION_BASE);
+      BigInteger p = new BigInteger(o.getString("p"), SERIALIZATION_BASE);
+      BigInteger q = new BigInteger(o.getString("q"), SERIALIZATION_BASE);
+      BigInteger g = new BigInteger(o.getString("g"), SERIALIZATION_BASE);
+      return createPublicKey(y, p, q, g);
+    } catch (NullPointerException e) {
+      throw new InvalidKeySpecException("y, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    } catch (NumberFormatException e) {
+      throw new InvalidKeySpecException("y, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    }
+  }
+
+  public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
+    try {
+      ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
+      ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
+      if (privateKey == null) {
+        throw new InvalidKeySpecException("privateKey must not be null");
+      }
+      if (publicKey == null) {
+        throw new InvalidKeySpecException("publicKey must not be null");
+      }
+      return new BrowserIDKeyPair(createPrivateKey(privateKey), createPublicKey(publicKey));
+    } catch (NonObjectJSONException e) {
+      throw new InvalidKeySpecException("privateKey and publicKey must be JSON objects");
+    }
+  }
 }
--- a/mobile/android/base/browserid/JSONWebTokenUtils.java
+++ b/mobile/android/base/browserid/JSONWebTokenUtils.java
@@ -88,17 +88,17 @@ public class JSONWebTokenUtils {
     return payload.toJSONString();
   }
 
   protected static String getCertificatePayloadString(VerifyingPublicKey publicKeyToSign, String email) throws NonObjectJSONException, IOException, ParseException  {
     ExtendedJSONObject payload = new ExtendedJSONObject();
     ExtendedJSONObject principal = new ExtendedJSONObject();
     principal.put("email", email);
     payload.put("principal", principal);
-    payload.put("public-key", new ExtendedJSONObject(publicKeyToSign.serialize()));
+    payload.put("public-key", publicKeyToSign.toJSONObject());
     return payload.toJSONString();
   }
 
   public static String createCertificate(VerifyingPublicKey publicKeyToSign, String email,
       String issuer, long issuedAt, long expiresAt, SigningPrivateKey privateKey) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException  {
     String certificatePayloadString = getCertificatePayloadString(publicKeyToSign, email);
     String payloadString = getPayloadString(certificatePayloadString, issuer, issuedAt, null, expiresAt);
     return JSONWebTokenUtils.encode(payloadString, privateKey);
--- a/mobile/android/base/browserid/RSACryptoImplementation.java
+++ b/mobile/android/base/browserid/RSACryptoImplementation.java
@@ -14,34 +14,49 @@ import java.security.Signature;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.security.spec.RSAPrivateKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.mozilla.gecko.sync.NonObjectJSONException;
 
 public class RSACryptoImplementation {
   public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
 
+  /**
+   * Parameters are serialized as decimal strings. Hex-versus-decimal was
+   * reverse-engineered from what the Persona public verifier accepted. We
+   * expect to follow the JOSE/JWT spec as it solidifies, and that will probably
+   * mean unifying this base.
+   */
+  protected static final int SERIALIZATION_BASE = 10;
+
   protected static class RSAVerifyingPublicKey implements VerifyingPublicKey {
     protected final RSAPublicKey publicKey;
 
     public RSAVerifyingPublicKey(RSAPublicKey publicKey) {
       this.publicKey = publicKey;
     }
 
+    /**
+     * Serialize to a JSON object.
+     * <p>
+     * Parameters are serialized as decimal strings. Hex-versus-decimal was
+     * reverse-engineered from what the Persona public verifier accepted.
+     */
     @Override
-    public String serialize() {
+    public ExtendedJSONObject toJSONObject() {
       ExtendedJSONObject o = new ExtendedJSONObject();
       o.put("algorithm", "RS");
-      o.put("n", publicKey.getModulus().toString(10));
-      o.put("e", publicKey.getPublicExponent().toString(10));
-      return o.toJSONString();
+      o.put("n", publicKey.getModulus().toString(SERIALIZATION_BASE));
+      o.put("e", publicKey.getPublicExponent().toString(SERIALIZATION_BASE));
+      return o;
     }
 
     @Override
     public boolean verifyMessage(byte[] bytes, byte[] signature)
         throws GeneralSecurityException {
       final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
       signer.initVerify(publicKey);
       signer.update(bytes);
@@ -56,36 +71,42 @@ public class RSACryptoImplementation {
       this.privateKey = privateKey;
     }
 
     @Override
     public String getAlgorithm() {
       return "RS" + (privateKey.getModulus().bitLength() + 7)/8;
     }
 
+    /**
+     * Serialize to a JSON object.
+     * <p>
+     * Parameters are serialized as decimal strings. Hex-versus-decimal was
+     * reverse-engineered from what the Persona public verifier accepted.
+     */
     @Override
-    public String serialize() {
+    public ExtendedJSONObject toJSONObject() {
       ExtendedJSONObject o = new ExtendedJSONObject();
       o.put("algorithm", "RS");
-      o.put("n", privateKey.getModulus().toString(10));
-      o.put("e", privateKey.getPrivateExponent().toString(10));
-      return o.toJSONString();
+      o.put("n", privateKey.getModulus().toString(SERIALIZATION_BASE));
+      o.put("d", privateKey.getPrivateExponent().toString(SERIALIZATION_BASE));
+      return o;
     }
 
     @Override
     public byte[] signMessage(byte[] bytes)
         throws GeneralSecurityException {
       final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
       signer.initSign(privateKey);
       signer.update(bytes);
       return signer.sign();
     }
   }
 
-  public static BrowserIDKeyPair generateKeypair(final int keysize) throws NoSuchAlgorithmException {
+  public static BrowserIDKeyPair generateKeyPair(final int keysize) throws NoSuchAlgorithmException {
     final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
     keyPairGenerator.initialize(keysize);
     final KeyPair keyPair = keyPairGenerator.generateKeyPair();
     RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
     RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
     return new BrowserIDKeyPair(new RSASigningPrivateKey(privateKey), new RSAVerifyingPublicKey(publicKey));
   }
 
@@ -109,9 +130,57 @@ public class RSACryptoImplementation {
     if (e == null) {
       throw new IllegalArgumentException("e must not be null");
     }
     KeyFactory keyFactory = KeyFactory.getInstance("RSA");
     KeySpec keySpec = new RSAPublicKeySpec(n, e);
     RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
     return new RSAVerifyingPublicKey(publicKey);
   }
+
+  public static SigningPrivateKey createPrivateKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
+    String algorithm = o.getString("algorithm");
+    if (!"RS".equals(algorithm)) {
+      throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
+    }
+    try {
+      BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
+      BigInteger d = new BigInteger(o.getString("d"), SERIALIZATION_BASE);
+      return createPrivateKey(n, d);
+    } catch (NullPointerException e) {
+      throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    } catch (NumberFormatException e) {
+      throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    }
+  }
+
+  public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
+    String algorithm = o.getString("algorithm");
+    if (!"RS".equals(algorithm)) {
+      throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
+    }
+    try {
+      BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
+      BigInteger e = new BigInteger(o.getString("e"), SERIALIZATION_BASE);
+      return createPublicKey(n, e);
+    } catch (NullPointerException e) {
+      throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    } catch (NumberFormatException e) {
+      throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
+    }
+  }
+
+  public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
+    try {
+      ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
+      ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
+      if (privateKey == null) {
+        throw new InvalidKeySpecException("privateKey must not be null");
+      }
+      if (publicKey == null) {
+        throw new InvalidKeySpecException("publicKey must not be null");
+      }
+      return new BrowserIDKeyPair(createPrivateKey(privateKey), createPublicKey(publicKey));
+    } catch (NonObjectJSONException e) {
+      throw new InvalidKeySpecException("privateKey and publicKey must be JSON objects");
+    }
+  }
 }
--- a/mobile/android/base/browserid/SigningPrivateKey.java
+++ b/mobile/android/base/browserid/SigningPrivateKey.java
@@ -1,38 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.browserid;
 
 import java.security.GeneralSecurityException;
 
+import org.mozilla.gecko.sync.ExtendedJSONObject;
+
 public interface SigningPrivateKey {
   /**
    * Return the JSON Web Token "alg" header corresponding to this private key.
    * <p>
    * The header is used when formatting web tokens, and generally denotes the
    * algorithm and an ad-hoc encoding of the key size.
    *
    * @return header.
    */
   public String getAlgorithm();
 
   /**
-   * Generate a printable representation of a private key.
+   * Generate a JSON representation of a private key.
    * <p>
    * <b>This should only be used for debugging. No private keys should go over
    * the wire at any time.</b>
    *
    * @param privateKey
    *          to represent.
-   * @return printable representation.
+   * @return JSON representation.
    */
-  public String serialize();
+  public ExtendedJSONObject toJSONObject();
 
   /**
    * Sign a message.
    * @param message to sign.
    * @return signature.
    * @throws GeneralSecurityException
    */
   public byte[] signMessage(byte[] message) throws GeneralSecurityException;
--- a/mobile/android/base/browserid/VerifyingPublicKey.java
+++ b/mobile/android/base/browserid/VerifyingPublicKey.java
@@ -1,26 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.browserid;
 
 import java.security.GeneralSecurityException;
 
+import org.mozilla.gecko.sync.ExtendedJSONObject;
+
 
 public interface VerifyingPublicKey {
   /**
-   * Generate a printable representation of a public key.
+   * Generate a JSON representation of a public key.
    *
    * @param publicKey
    *          to represent.
-   * @return printable representation.
+   * @return JSON representation.
    */
-  public String serialize();
+  public ExtendedJSONObject toJSONObject();
 
   /**
    * Verify a signature.
    *
    * @param message
    *          to verify signature of.
    * @param signature
    *          to verify.
--- a/mobile/android/base/fxa/sync/FxAccount.java
+++ b/mobile/android/base/fxa/sync/FxAccount.java
@@ -150,26 +150,26 @@ public class FxAccount {
    *
    * @param context to use.
    * @param tokenServerEndpoint to get token from.
    * @param keyPair to sign certificate for.
    * @param delegate to callback to.
    */
   public void login(final Context context, final String tokenServerEndpoint,
       final BrowserIDKeyPair keyPair, final Delegate delegate) {
-    ExtendedJSONObject keyPairObject;
+    ExtendedJSONObject publicKeyObject;
     try {
-      keyPairObject = new ExtendedJSONObject(keyPair.getPublic().serialize());
+      publicKeyObject = keyPair.getPublic().toJSONObject();
     } catch (Exception e) {
       delegate.handleError(e);
       return;
     }
 
     // We have nested executors in play here. Since we control the executor and
     // the delegates, we can guarantee that there is no dead-lock between the
     // inner FxAccountClient delegate, the outer TokenServerClient delegate, and
     // the user supplied delegate.
     FxAccountClient fxAccountClient = new FxAccountClient(idpEndpoint, executor);
-    fxAccountClient.sign(sessionTokenBytes, keyPairObject,
+    fxAccountClient.sign(sessionTokenBytes, publicKeyObject,
         JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS,
         new InnerFxAccountClientRequestDelegate(executor, authEndpoint, tokenServerEndpoint, keyPair, delegate));
   }
 }
--- a/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
+++ b/mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
@@ -108,17 +108,17 @@ public class FxAccountSyncAdapter extend
 
     Logger.info(LOG_TAG, "Syncing FxAccount" +
         " account named " + account.name +
         " for authority " + authority +
         " with instance " + this + ".");
 
     final CountDownLatch latch = new CountDownLatch(1);
     try {
-      final BrowserIDKeyPair keyPair = RSACryptoImplementation.generateKeypair(1024);
+      final BrowserIDKeyPair keyPair = RSACryptoImplementation.generateKeyPair(1024);
       Logger.info(LOG_TAG, "Generated keypair. ");
 
       final FxAccount fxAccount = FxAccountAuthenticator.fromAndroidAccount(getContext(), account);
       final String tokenServerEndpoint = fxAccount.authEndpoint + (fxAccount.authEndpoint.endsWith("/") ? "" : "/") + "1.0/sync/1.1";
 
       fxAccount.login(getContext(), tokenServerEndpoint, keyPair, new FxAccount.Delegate() {
         @Override
         public void handleSuccess(final String uid, final String endpoint, final AuthHeaderProvider authHeaderProvider) {
--- a/mobile/android/tests/background/junit3/src/fxa/TestBrowserIDKeyPairGeneration.java
+++ b/mobile/android/tests/background/junit3/src/fxa/TestBrowserIDKeyPairGeneration.java
@@ -33,17 +33,17 @@ public class TestBrowserIDKeyPairGenerat
       JSONWebTokenUtils.decode(token + "x", publicKey);
       fail("Expected exception.");
     } catch (GeneralSecurityException e) {
       // Do nothing.
     }
   }
 
   public void testEncodeDecodeSuccessRSA() throws Exception {
-    doTestEncodeDecode(RSACryptoImplementation.generateKeypair(1024));
-    doTestEncodeDecode(RSACryptoImplementation.generateKeypair(2048));
+    doTestEncodeDecode(RSACryptoImplementation.generateKeyPair(1024));
+    doTestEncodeDecode(RSACryptoImplementation.generateKeyPair(2048));
   }
 
   public void testEncodeDecodeSuccessDSA() throws Exception {
-    doTestEncodeDecode(DSACryptoImplementation.generateKeypair(512));
-    doTestEncodeDecode(DSACryptoImplementation.generateKeypair(1024));
+    doTestEncodeDecode(DSACryptoImplementation.generateKeyPair(512));
+    doTestEncodeDecode(DSACryptoImplementation.generateKeyPair(1024));
   }
 }