mobile/android/base/browserid/RSACryptoImplementation.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.browserid;
     7 import java.math.BigInteger;
     8 import java.security.GeneralSecurityException;
     9 import java.security.KeyFactory;
    10 import java.security.KeyPair;
    11 import java.security.KeyPairGenerator;
    12 import java.security.NoSuchAlgorithmException;
    13 import java.security.Signature;
    14 import java.security.interfaces.RSAPrivateKey;
    15 import java.security.interfaces.RSAPublicKey;
    16 import java.security.spec.InvalidKeySpecException;
    17 import java.security.spec.KeySpec;
    18 import java.security.spec.RSAPrivateKeySpec;
    19 import java.security.spec.RSAPublicKeySpec;
    21 import org.mozilla.gecko.sync.ExtendedJSONObject;
    22 import org.mozilla.gecko.sync.NonObjectJSONException;
    24 public class RSACryptoImplementation {
    25   public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
    27   /**
    28    * Parameters are serialized as decimal strings. Hex-versus-decimal was
    29    * reverse-engineered from what the Persona public verifier accepted. We
    30    * expect to follow the JOSE/JWT spec as it solidifies, and that will probably
    31    * mean unifying this base.
    32    */
    33   protected static final int SERIALIZATION_BASE = 10;
    35   protected static class RSAVerifyingPublicKey implements VerifyingPublicKey {
    36     protected final RSAPublicKey publicKey;
    38     public RSAVerifyingPublicKey(RSAPublicKey publicKey) {
    39       this.publicKey = publicKey;
    40     }
    42     /**
    43      * Serialize to a JSON object.
    44      * <p>
    45      * Parameters are serialized as decimal strings. Hex-versus-decimal was
    46      * reverse-engineered from what the Persona public verifier accepted.
    47      */
    48     @Override
    49     public ExtendedJSONObject toJSONObject() {
    50       ExtendedJSONObject o = new ExtendedJSONObject();
    51       o.put("algorithm", "RS");
    52       o.put("n", publicKey.getModulus().toString(SERIALIZATION_BASE));
    53       o.put("e", publicKey.getPublicExponent().toString(SERIALIZATION_BASE));
    54       return o;
    55     }
    57     @Override
    58     public boolean verifyMessage(byte[] bytes, byte[] signature)
    59         throws GeneralSecurityException {
    60       final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
    61       signer.initVerify(publicKey);
    62       signer.update(bytes);
    63       return signer.verify(signature);
    64     }
    65   }
    67   protected static class RSASigningPrivateKey implements SigningPrivateKey {
    68     protected final RSAPrivateKey privateKey;
    70     public RSASigningPrivateKey(RSAPrivateKey privateKey) {
    71       this.privateKey = privateKey;
    72     }
    74     @Override
    75     public String getAlgorithm() {
    76       return "RS" + (privateKey.getModulus().bitLength() + 7)/8;
    77     }
    79     /**
    80      * Serialize to a JSON object.
    81      * <p>
    82      * Parameters are serialized as decimal strings. Hex-versus-decimal was
    83      * reverse-engineered from what the Persona public verifier accepted.
    84      */
    85     @Override
    86     public ExtendedJSONObject toJSONObject() {
    87       ExtendedJSONObject o = new ExtendedJSONObject();
    88       o.put("algorithm", "RS");
    89       o.put("n", privateKey.getModulus().toString(SERIALIZATION_BASE));
    90       o.put("d", privateKey.getPrivateExponent().toString(SERIALIZATION_BASE));
    91       return o;
    92     }
    94     @Override
    95     public byte[] signMessage(byte[] bytes)
    96         throws GeneralSecurityException {
    97       final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
    98       signer.initSign(privateKey);
    99       signer.update(bytes);
   100       return signer.sign();
   101     }
   102   }
   104   public static BrowserIDKeyPair generateKeyPair(final int keysize) throws NoSuchAlgorithmException {
   105     final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
   106     keyPairGenerator.initialize(keysize);
   107     final KeyPair keyPair = keyPairGenerator.generateKeyPair();
   108     RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
   109     RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
   110     return new BrowserIDKeyPair(new RSASigningPrivateKey(privateKey), new RSAVerifyingPublicKey(publicKey));
   111   }
   113   public static SigningPrivateKey createPrivateKey(BigInteger n, BigInteger d) throws NoSuchAlgorithmException, InvalidKeySpecException {
   114     if (n == null) {
   115       throw new IllegalArgumentException("n must not be null");
   116     }
   117     if (d == null) {
   118       throw new IllegalArgumentException("d must not be null");
   119     }
   120     KeyFactory keyFactory = KeyFactory.getInstance("RSA");
   121     KeySpec keySpec = new RSAPrivateKeySpec(n, d);
   122     RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
   123     return new RSASigningPrivateKey(privateKey);
   124   }
   126   public static VerifyingPublicKey createPublicKey(BigInteger n, BigInteger e) throws NoSuchAlgorithmException, InvalidKeySpecException {
   127     if (n == null) {
   128       throw new IllegalArgumentException("n must not be null");
   129     }
   130     if (e == null) {
   131       throw new IllegalArgumentException("e must not be null");
   132     }
   133     KeyFactory keyFactory = KeyFactory.getInstance("RSA");
   134     KeySpec keySpec = new RSAPublicKeySpec(n, e);
   135     RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
   136     return new RSAVerifyingPublicKey(publicKey);
   137   }
   139   public static SigningPrivateKey createPrivateKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
   140     String algorithm = o.getString("algorithm");
   141     if (!"RS".equals(algorithm)) {
   142       throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
   143     }
   144     try {
   145       BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
   146       BigInteger d = new BigInteger(o.getString("d"), SERIALIZATION_BASE);
   147       return createPrivateKey(n, d);
   148     } catch (NullPointerException e) {
   149       throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
   150     } catch (NumberFormatException e) {
   151       throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
   152     }
   153   }
   155   public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
   156     String algorithm = o.getString("algorithm");
   157     if (!"RS".equals(algorithm)) {
   158       throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
   159     }
   160     try {
   161       BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
   162       BigInteger e = new BigInteger(o.getString("e"), SERIALIZATION_BASE);
   163       return createPublicKey(n, e);
   164     } catch (NullPointerException e) {
   165       throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
   166     } catch (NumberFormatException e) {
   167       throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
   168     }
   169   }
   171   public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
   172     try {
   173       ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
   174       ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
   175       if (privateKey == null) {
   176         throw new InvalidKeySpecException("privateKey must not be null");
   177       }
   178       if (publicKey == null) {
   179         throw new InvalidKeySpecException("publicKey must not be null");
   180       }
   181       return new BrowserIDKeyPair(createPrivateKey(privateKey), createPublicKey(publicKey));
   182     } catch (NonObjectJSONException e) {
   183       throw new InvalidKeySpecException("privateKey and publicKey must be JSON objects");
   184     }
   185   }
   186 }

mercurial