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