Home | History | Annotate | Download | only in securemessage
      1 /* Copyright 2018 Google LLC
      2  *
      3  * Licensed under the Apache License, Version 2.0 (the "License");
      4  * you may not use this file except in compliance with the License.
      5  * You may obtain a copy of the License at
      6  *
      7  *     https://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software
     10  * distributed under the License is distributed on an "AS IS" BASIS,
     11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12  * See the License for the specific language governing permissions and
     13  * limitations under the License.
     14  */
     15 package com.google.security.cryptauth.lib.securemessage;
     16 
     17 import com.google.protobuf.ByteString;
     18 import com.google.security.annotations.SuppressInsecureCipherModeCheckerPendingReview;
     19 import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.DhPublicKey;
     20 import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.EcP256PublicKey;
     21 import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
     22 import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SimpleRsaPublicKey;
     23 import java.math.BigInteger;
     24 import java.security.InvalidAlgorithmParameterException;
     25 import java.security.KeyFactory;
     26 import java.security.KeyPair;
     27 import java.security.KeyPairGenerator;
     28 import java.security.NoSuchAlgorithmException;
     29 import java.security.PublicKey;
     30 import java.security.SecureRandom;
     31 import java.security.interfaces.ECPublicKey;
     32 import java.security.interfaces.RSAPublicKey;
     33 import java.security.spec.ECFieldFp;
     34 import java.security.spec.ECGenParameterSpec;
     35 import java.security.spec.ECParameterSpec;
     36 import java.security.spec.ECPoint;
     37 import java.security.spec.ECPublicKeySpec;
     38 import java.security.spec.InvalidKeySpecException;
     39 import java.security.spec.RSAPublicKeySpec;
     40 import javax.crypto.interfaces.DHPrivateKey;
     41 import javax.crypto.interfaces.DHPublicKey;
     42 import javax.crypto.spec.DHParameterSpec;
     43 import javax.crypto.spec.DHPublicKeySpec;
     44 
     45 /**
     46  * Utility class containing static factory methods for a simple protobuf based representation of
     47  * EC public keys that is intended for use with the SecureMessage library.
     48  *
     49  * N.B.: Requires the availability of an EC security provider supporting the NIST P-256 curve.
     50  *
     51  */
     52 public class PublicKeyProtoUtil {
     53 
     54   private PublicKeyProtoUtil() {}  // Do not instantiate
     55 
     56   /**
     57    * Caches state about whether the current platform supports Elliptic Curve algorithms.
     58    */
     59   private static final Boolean IS_LEGACY_CRYPTO_REQUIRED = determineIfLegacyCryptoRequired();
     60 
     61   private static final BigInteger ONE = new BigInteger("1");
     62   private static final BigInteger TWO = new BigInteger("2");
     63 
     64   /**
     65    * Name for Elliptic Curve cryptography algorithm suite, used by the security provider. If the
     66    * security provider does not implement the specified algorithm, runtime errors will ensue.
     67    */
     68   private static final String EC_ALG = "EC";
     69 
     70   /**
     71    * A common name for the NIST P-256 curve, used by most Java security providers.
     72    */
     73   private static final String EC_P256_COMMON_NAME = "secp256r1";
     74 
     75   /**
     76    * A name the NIST P-256 curve, used by the OpenSSL Java security provider (e.g,. on Android).
     77    */
     78   private static final String EC_P256_OPENSSL_NAME = "prime256v1";
     79 
     80   /**
     81    * The {@link ECParameterSpec} for the NIST P-256 Elliptic Curve.
     82    */
     83   private static final ECParameterSpec EC_P256_PARAMS = isLegacyCryptoRequired() ? null :
     84       ((ECPublicKey) generateEcP256KeyPair().getPublic()).getParams();
     85 
     86   /**
     87    * The prime {@code p} describing the field for the NIST P-256 curve.
     88    */
     89   private static final BigInteger EC_P256_P = isLegacyCryptoRequired() ? null :
     90       ((ECFieldFp) EC_P256_PARAMS.getCurve().getField()).getP();
     91 
     92   /**
     93    * The coefficient {@code a} for the NIST P-256 curve.
     94    */
     95   private static final BigInteger EC_P256_A = isLegacyCryptoRequired() ? null :
     96       EC_P256_PARAMS.getCurve().getA();
     97 
     98   /**
     99    * The coefficient {@code b} for the NIST P-256 curve.
    100    */
    101   private static final BigInteger EC_P256_B = isLegacyCryptoRequired() ? null :
    102       EC_P256_PARAMS.getCurve().getB();
    103 
    104   /**
    105    * Maximum number of bytes in a 2's complement encoding of a NIST P-256 elliptic curve point.
    106    */
    107   private static final int MAX_P256_ENCODING_BYTES = 33;
    108 
    109   /**
    110    * The JCA name for the RSA cryptography suite.
    111    */
    112   private static final String RSA_ALG = "RSA";
    113 
    114   private static final int RSA2048_MODULUS_BITS = 2048;
    115 
    116   /**
    117    * Maximum number of bytes in a 2's complement encoding of a 2048-bit RSA key.
    118    */
    119   private static final int MAX_RSA2048_ENCODING_BYTES = 257;
    120 
    121   /**
    122    * The JCA name for the Diffie-Hellman cryptography suite.
    123    */
    124   private static final String DH_ALG = "DH";
    125 
    126   /**
    127    * The prime from the 2048-bit MODP Group (group 14) described in RFC 3526, to be used for
    128    * Diffie-Hellman computations. Use only if Elliptic Curve ciphers are unavailable.
    129    */
    130   public static final BigInteger DH_P = new BigInteger(
    131       "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
    132       "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
    133       "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
    134       "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
    135       "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
    136       "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
    137       "83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
    138       "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
    139       "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
    140       "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
    141       "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
    142 
    143   /**
    144    * The generator for the 2048-bit MODP Group (group 14) described in RFC 3526, to be used for
    145    * Diffie-Hellman computations. Use only if Elliptic Curve ciphers are unavailable.
    146    */
    147   public static final BigInteger DH_G = TWO;
    148 
    149   /**
    150    * The size of the Diffie-Hellman exponent to use, in bits.
    151    */
    152   public static final int DH_LEN = 512;
    153 
    154   /**
    155    * Maximum number of bytes in a 2's complement encoding of a
    156    * Diffie-Hellman key using {@link #DH_G}.
    157    */
    158   private static final int MAX_DH2048_ENCODING_BYTES = 257;
    159 
    160   /**
    161    * Version code for the Honeycomb release of Android, which is the first release supporting
    162    * Elliptic Curve.
    163    */
    164   public static final int ANDROID_HONEYCOMB_SDK_INT = 11;
    165 
    166   /**
    167    * Encodes any supported {@link PublicKey} type as a {@link GenericPublicKey} proto message.
    168    *
    169    * @see SecureMessageProto constants (defined in the .proto file) for supported types
    170    */
    171   public static GenericPublicKey encodePublicKey(PublicKey pk) {
    172     if (pk == null) {
    173       throw new NullPointerException();
    174     }
    175     if (pk instanceof ECPublicKey) {
    176       return GenericPublicKey.newBuilder()
    177           .setType(SecureMessageProto.PublicKeyType.EC_P256)
    178           .setEcP256PublicKey(encodeEcPublicKey(pk))
    179           .build();
    180     }
    181     if (pk instanceof RSAPublicKey) {
    182       return GenericPublicKey.newBuilder()
    183           .setType(SecureMessageProto.PublicKeyType.RSA2048)
    184           .setRsa2048PublicKey(encodeRsa2048PublicKey(pk))
    185           .build();
    186     }
    187     if (pk instanceof DHPublicKey) {
    188       return GenericPublicKey.newBuilder()
    189           .setType(SecureMessageProto.PublicKeyType.DH2048_MODP)
    190           .setDh2048PublicKey(encodeDh2048PublicKey(pk))
    191           .build();
    192     }
    193     throw new IllegalArgumentException("Unsupported PublicKey type");
    194   }
    195 
    196   /**
    197    * Encodes an {@link ECPublicKey} to an {@link EcP256PublicKey} proto message.
    198    */
    199   public static EcP256PublicKey encodeEcPublicKey(PublicKey pk) {
    200     ECPublicKey epk = pkToECPublicKey(pk);
    201     return EcP256PublicKey.newBuilder()
    202         .setX(extractX(epk))
    203         .setY(extractY(epk))
    204         .build();
    205   }
    206 
    207   /**
    208    * Encodes a 2048-bit {@link RSAPublicKey} to an {@link SimpleRsaPublicKey} proto message.
    209    */
    210   public static SimpleRsaPublicKey encodeRsa2048PublicKey(PublicKey pk) {
    211     RSAPublicKey rpk = pkToRSAPublicKey(pk);
    212     return SimpleRsaPublicKey.newBuilder()
    213         .setN(ByteString.copyFrom(rpk.getModulus().toByteArray()))
    214         .setE(rpk.getPublicExponent().intValue())
    215         .build();
    216   }
    217 
    218   /**
    219    * Encodes a 2048-bit {@link DhPublicKey} using the {@link #DH_G} group to a
    220    * {@link DhPublicKey} proto message.
    221    */
    222   public static DhPublicKey encodeDh2048PublicKey(PublicKey pk) {
    223     DHPublicKey dhpk = pkToDHPublicKey(pk);
    224     return DhPublicKey.newBuilder()
    225         .setY(ByteString.copyFrom(dhpk.getY().toByteArray()))
    226         .build();
    227   }
    228 
    229   /**
    230    * Extracts a {@link PublicKey} from an {@link GenericPublicKey} proto message.
    231    *
    232    * @throws InvalidKeySpecException if the input is not a valid and/or supported public key type
    233    */
    234   public static PublicKey parsePublicKey(GenericPublicKey gpk) throws InvalidKeySpecException {
    235     if (!gpk.hasType()) {
    236       // "required" means nothing in micro proto land. We have to check this ourselves.
    237       throw new InvalidKeySpecException("GenericPublicKey.type is a required field");
    238     }
    239     switch (gpk.getType()) {
    240       case EC_P256:
    241         if (!gpk.hasEcP256PublicKey()) {
    242           break;
    243         }
    244         return parseEcPublicKey(gpk.getEcP256PublicKey());
    245       case RSA2048:
    246         if (!gpk.hasRsa2048PublicKey()) {
    247           break;
    248         }
    249         return parseRsa2048PublicKey(gpk.getRsa2048PublicKey());
    250       case DH2048_MODP:
    251         if (!gpk.hasDh2048PublicKey()) {
    252           break;
    253         }
    254         return parseDh2048PublicKey(gpk.getDh2048PublicKey());
    255       default:
    256         throw new InvalidKeySpecException("Unsupported GenericPublicKey type: " + gpk.getType());
    257     }
    258     throw new InvalidKeySpecException("key object is missing for key type: " + gpk.getType());
    259   }
    260 
    261   /**
    262    * Extracts a {@link ECPublicKey} from an {@link EcP256PublicKey} proto message.
    263    *
    264    * @throws InvalidKeySpecException if the input is not a valid NIST P-256 public key or if
    265    *   this platform does not support Elliptic Curve keys
    266    */
    267   public static ECPublicKey parseEcPublicKey(EcP256PublicKey p256pk)
    268       throws InvalidKeySpecException {
    269     if (!p256pk.hasX() || !p256pk.hasY()) {
    270       throw new InvalidKeySpecException("Key is missing a required coordinate");
    271     }
    272     if (isLegacyCryptoRequired()) {
    273       throw new InvalidKeySpecException("Elliptic Curve keys not supported on this platform");
    274     }
    275     byte[] encodedX = p256pk.getX().toByteArray();
    276     byte[] encodedY = p256pk.getY().toByteArray();
    277     try {
    278       validateEcP256CoordinateEncoding(encodedX);
    279       validateEcP256CoordinateEncoding(encodedY);
    280       BigInteger wX = new BigInteger(encodedX);
    281       BigInteger wY = new BigInteger(encodedY);
    282       validateEcP256CurvePoint(wX, wY);
    283       return (ECPublicKey) KeyFactory.getInstance(EC_ALG).generatePublic(
    284           new ECPublicKeySpec(new ECPoint(wX, wY), EC_P256_PARAMS));
    285     } catch (NoSuchAlgorithmException e) {
    286       throw new RuntimeException(e);
    287     }
    288   }
    289 
    290   /**
    291    * Extracts a {@link RSAPublicKey} from an {@link SimpleRsaPublicKey} proto message.
    292    *
    293    * @throws InvalidKeySpecException when the input RSA public key is invalid
    294    */
    295   public static RSAPublicKey parseRsa2048PublicKey(SimpleRsaPublicKey pk)
    296       throws InvalidKeySpecException {
    297     if (!pk.hasN()) {
    298       throw new InvalidKeySpecException("required field is missing");
    299     }
    300     byte[] encodedN = pk.getN().toByteArray();
    301     validateSimpleRsaEncoding(encodedN);
    302     BigInteger n = new BigInteger(encodedN);
    303     if (n.bitLength() != RSA2048_MODULUS_BITS) {
    304       throw new InvalidKeySpecException();
    305     }
    306     BigInteger e = BigInteger.valueOf(pk.getE());
    307     try {
    308       return (RSAPublicKey) KeyFactory.getInstance(RSA_ALG).generatePublic(
    309           new RSAPublicKeySpec(n, e));
    310     } catch (NoSuchAlgorithmException e1) {
    311       throw new AssertionError(e1);  // Should never happen
    312     }
    313   }
    314 
    315   /**
    316    * Extracts a {@link DHPublicKey} from an {@link DhPublicKey} proto message.
    317    *
    318    * @throws InvalidKeySpecException when the input DH public key is invalid
    319    */
    320   @SuppressInsecureCipherModeCheckerPendingReview // b/32143855
    321   public static DHPublicKey parseDh2048PublicKey(DhPublicKey pk) throws InvalidKeySpecException {
    322     if (!pk.hasY()) {
    323       throw new InvalidKeySpecException("required field is missing");
    324     }
    325     byte[] encodedY = pk.getY().toByteArray();
    326     validateDhEncoding(encodedY);
    327     BigInteger y;
    328     try {
    329       y = new BigInteger(encodedY);
    330     } catch (NumberFormatException e) {
    331       throw new InvalidKeySpecException();
    332     }
    333     validateDhGroupElement(y);
    334     try {
    335       return (DHPublicKey) KeyFactory.getInstance(DH_ALG).generatePublic(
    336           new DHPublicKeySpec(y, DH_P, DH_G));
    337     } catch (NoSuchAlgorithmException e) {
    338       throw new AssertionError(e);  // Should never happen
    339     }
    340   }
    341 
    342   /**
    343    * @return a freshly generated NIST P-256 Elliptic Curve key pair.
    344    */
    345   public static KeyPair generateEcP256KeyPair() {
    346     return getEcKeyGen().generateKeyPair();
    347   }
    348 
    349   /**
    350    * @return a freshly generated 2048-bit RSA key pair.
    351    */
    352   public static KeyPair generateRSA2048KeyPair() {
    353     return getRsaKeyGen().generateKeyPair();
    354   }
    355 
    356   /**
    357    * @return a freshly generated Diffie-Hellman key pair for the 2048-bit group
    358    *   described by {@link #DH_G}
    359    */
    360   public static KeyPair generateDh2048KeyPair() {
    361     try {
    362       return getDhKeyGen().generateKeyPair();
    363     } catch (InvalidAlgorithmParameterException e) {
    364       // Construct an appropriate KeyPair manually, since this platform refuses to do it for us
    365       DHParameterSpec spec = new DHParameterSpec(DH_P, DH_G);
    366       BigInteger x = new BigInteger(DH_LEN, new SecureRandom());
    367       DHPrivateKey privateKey = new DHPrivateKeyShim(x, spec);
    368       DHPublicKey publicKey = new DHPublicKeyShim(DH_G.modPow(x, DH_P), spec);
    369       return new KeyPair(publicKey, privateKey);
    370     }
    371   }
    372 
    373   /**
    374    * A lightweight encoding for a {@link DHPrivateKey}. Strongly recommended over attempting to use
    375    * {@link DHPrivateKey#getEncoded()}, but not compatible with the standard encoding.
    376    *
    377    * @see #parseDh2048PrivateKey(byte[])
    378    */
    379   public static byte[] encodeDh2048PrivateKey(DHPrivateKey sk) {
    380     return sk.getX().toByteArray();
    381   }
    382 
    383   /**
    384    * Parses a {@link DHPrivateKey} encoded with {@link #encodeDh2048PrivateKey(DHPrivateKey)}.
    385    */
    386   public static DHPrivateKey parseDh2048PrivateKey(byte[] encodedX)
    387       throws InvalidKeySpecException {
    388     validateDhEncoding(encodedX);  // Could be stricter for x, but should be fine to use this
    389     BigInteger x;
    390     try {
    391       x = new BigInteger(encodedX);
    392     } catch (NumberFormatException e) {
    393       throw new InvalidKeySpecException();
    394     }
    395     validateDhGroupElement(x);  // Again, this validation should be good enough
    396     return new DHPrivateKeyShim(x, new DHParameterSpec(DH_P, DH_G));
    397   }
    398 
    399   /**
    400    * @throws InvalidKeySpecException if point ({@code x},{@code y}) isn't on the NIST P-256 curve
    401    */
    402   private static void validateEcP256CurvePoint(BigInteger x, BigInteger y)
    403       throws InvalidKeySpecException {
    404     if ((x.signum() == -1) || (y.signum() == -1)) {
    405       throw new InvalidKeySpecException("Point encoding must use only non-negative integers");
    406     }
    407 
    408     BigInteger p = EC_P256_P;
    409     if ((x.compareTo(p) >= 0) || (y.compareTo(p) >= 0)) {
    410       throw new InvalidKeySpecException("Point lies outside of the expected field");
    411     }
    412 
    413     // Points on the curve satisfy y^2 = x^3 + ax + b  (mod p)
    414     BigInteger lhs = squareMod(y, p);
    415     BigInteger rhs = squareMod(x, p).add(EC_P256_A) // = (x^2 + a)
    416         .multiply(x).mod(p) // = x(x^2 + a) = x^3 + ax
    417         .add(EC_P256_B) // = x^3 + ax + b
    418         .mod(p);
    419     if (!lhs.equals(rhs)) {
    420       throw new InvalidKeySpecException("Point does not lie on the expected curve");
    421     }
    422   }
    423 
    424   /**
    425    * @return value of {@code x}^2 (mod {@code p})
    426    */
    427   private static BigInteger squareMod(BigInteger x, BigInteger p) {
    428     return x.multiply(x).mod(p);
    429   }
    430 
    431   /**
    432    * @throws InvalidKeySpecException if the coordinate is too large for a 256-bit curve
    433    */
    434   private static void validateEcP256CoordinateEncoding(byte[] p) throws InvalidKeySpecException {
    435     if ((p.length == 0)
    436         || (p.length > MAX_P256_ENCODING_BYTES)
    437         || (p.length == MAX_P256_ENCODING_BYTES && p[0] != 0)) {
    438       throw new InvalidKeySpecException();  // Intentionally vague for security reasons
    439     }
    440   }
    441 
    442   /**
    443    * @throws InvalidKeySpecException if the input is too large for a 2048-bit RSA modulus
    444    */
    445   private static void validateSimpleRsaEncoding(byte[] n) throws InvalidKeySpecException {
    446     if (n.length == 0 || n.length > MAX_RSA2048_ENCODING_BYTES) {
    447       throw new InvalidKeySpecException();
    448     }
    449   }
    450 
    451   /**
    452    * @throws InvalidKeySpecException if the public key is too large for a 2048-bit DH group
    453    */
    454   private static void validateDhEncoding(byte[] y) throws InvalidKeySpecException {
    455     if (y.length == 0 || y.length > MAX_DH2048_ENCODING_BYTES) {
    456       throw new InvalidKeySpecException();
    457     }
    458   }
    459 
    460   /**
    461    * @throws InvalidKeySpecException if {@code y} is not a valid Diffie-Hellman public key
    462    */
    463   private static void validateDhGroupElement(BigInteger y) throws InvalidKeySpecException {
    464     // Check that 1 < y < p -1
    465     if ((y.compareTo(ONE) < 1) || (y.compareTo(DH_P.subtract(ONE)) > -1)) {
    466       throw new InvalidKeySpecException();
    467     }
    468   }
    469 
    470   private static ByteString extractY(ECPublicKey epk) {
    471     return ByteString.copyFrom(epk.getW().getAffineY().toByteArray());
    472   }
    473 
    474   private static ByteString extractX(ECPublicKey epk) {
    475     return ByteString.copyFrom(epk.getW().getAffineX().toByteArray());
    476   }
    477 
    478   private static ECPublicKey pkToECPublicKey(PublicKey pk) {
    479     if (pk == null) {
    480       throw new NullPointerException();
    481     }
    482     if (!(pk instanceof ECPublicKey)) {
    483       throw new IllegalArgumentException("Not an EC Public Key");
    484     }
    485     return (ECPublicKey) pk;
    486   }
    487 
    488   private static RSAPublicKey pkToRSAPublicKey(PublicKey pk) {
    489     if (pk == null) {
    490       throw new NullPointerException();
    491     }
    492     if (!(pk instanceof RSAPublicKey)) {
    493       throw new IllegalArgumentException("Not an RSA Public Key");
    494     }
    495     return (RSAPublicKey) pk;
    496   }
    497 
    498   private static DHPublicKey pkToDHPublicKey(PublicKey pk) {
    499     if (pk == null) {
    500       throw new NullPointerException();
    501     }
    502     if (!(pk instanceof DHPublicKey)) {
    503       throw new IllegalArgumentException("Not a DH Public Key");
    504     }
    505     return (DHPublicKey) pk;
    506   }
    507 
    508   /**
    509    * @return an EC {@link KeyPairGenerator} object initialized for NIST P-256.
    510    */
    511   private static KeyPairGenerator getEcKeyGen() {
    512     KeyPairGenerator keygen;
    513     try {
    514       keygen = KeyPairGenerator.getInstance(EC_ALG);
    515     } catch (NoSuchAlgorithmException e) {
    516       throw new RuntimeException(e);
    517     }
    518     try {
    519       // Try using the OpenSSL provider first, since we prefer it over BouncyCastle
    520       keygen.initialize(new ECGenParameterSpec(EC_P256_OPENSSL_NAME));
    521       return keygen;
    522     } catch (InvalidAlgorithmParameterException e) {
    523       // Try another name for NIST P-256
    524     }
    525     try {
    526       keygen.initialize(new ECGenParameterSpec(EC_P256_COMMON_NAME));
    527       return keygen;
    528     } catch (InvalidAlgorithmParameterException e) {
    529       throw new RuntimeException("Unable to find the NIST P-256 curve");
    530     }
    531   }
    532 
    533   /**
    534    * @return an RSA {@link KeyPairGenerator} object initialized for 2048-bit keys.
    535    */
    536   private static KeyPairGenerator getRsaKeyGen() {
    537     try {
    538       KeyPairGenerator keygen = KeyPairGenerator.getInstance(RSA_ALG);
    539       keygen.initialize(RSA2048_MODULUS_BITS);
    540       return keygen;
    541     } catch (NoSuchAlgorithmException e) {
    542       throw new AssertionError(e);  // This should never happen
    543     }
    544   }
    545 
    546   /**
    547    * @return a DH {@link KeyPairGenerator} object initialized for the group described by {@link
    548    *     #DH_G}.
    549    * @throws InvalidAlgorithmParameterException on some platforms that don't support large DH groups
    550    */
    551   @SuppressInsecureCipherModeCheckerPendingReview // b/32143855
    552   private static KeyPairGenerator getDhKeyGen() throws InvalidAlgorithmParameterException {
    553     try {
    554       KeyPairGenerator keygen = KeyPairGenerator.getInstance(DH_ALG);
    555       keygen.initialize(new DHParameterSpec(DH_P, DH_G, DH_LEN));
    556       return keygen;
    557     } catch (NoSuchAlgorithmException e) {
    558       throw new AssertionError(e);  // This should never happen
    559     }
    560   }
    561 
    562   /**
    563    * A lightweight shim class to enable the creation of {@link DHPublicKey} and {@link DHPrivateKey}
    564    * objects that accept arbitrary {@link DHParameterSpec}s -- unfortunately, many platforms do
    565    * not support using reasonably sized Diffie-Hellman groups any other way. For instance, see
    566    * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6521495">Java bug 6521495</a>.
    567    */
    568   public abstract static class DHKeyShim {
    569 
    570     private BigInteger eitherXorY;
    571     private DHParameterSpec params;
    572 
    573     public DHKeyShim(BigInteger eitherXorY, DHParameterSpec params) {
    574       this.eitherXorY = eitherXorY;
    575       this.params = params;
    576     }
    577 
    578     public DHParameterSpec getParams() {
    579       return params;
    580     }
    581 
    582     public String getAlgorithm() {
    583       return "DH";
    584     }
    585 
    586     public String getFormat() {
    587       return null;
    588     }
    589 
    590     public byte[] getEncoded() {
    591       return null;
    592     }
    593 
    594     public BigInteger getX() {
    595       return eitherXorY;
    596     }
    597 
    598     public BigInteger getY() {
    599       return eitherXorY;
    600     }
    601   }
    602 
    603   /**
    604    * A simple {@link DHPublicKey} implementation.
    605    *
    606    * @see DHKeyShim
    607    */
    608   public static class DHPublicKeyShim extends DHKeyShim implements DHPublicKey {
    609     public DHPublicKeyShim(BigInteger y, DHParameterSpec params) {
    610       super(y, params);
    611     }
    612   }
    613 
    614   /**
    615    * A simple {@link DHPrivateKey} implementation.
    616    *
    617    * @see DHKeyShim
    618    */
    619   public static class DHPrivateKeyShim extends DHKeyShim implements DHPrivateKey {
    620     public DHPrivateKeyShim(BigInteger x, DHParameterSpec params) {
    621       super(x, params);
    622     }
    623   }
    624 
    625   /**
    626    * @return true if this platform does not support Elliptic Curve algorithms
    627    */
    628   public static boolean isLegacyCryptoRequired() {
    629     return IS_LEGACY_CRYPTO_REQUIRED;
    630   }
    631 
    632   /**
    633    * @return true if using the Elliptic Curve key generator fails on this platform
    634    */
    635   private static boolean determineIfLegacyCryptoRequired() {
    636     try {
    637       getEcKeyGen();
    638     } catch (Exception e) {
    639       return true;
    640     }
    641     return false;
    642   }
    643 }
    644