• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PublicKeyProtoUtil()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    */
encodePublicKey(PublicKey pk)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    */
encodeEcPublicKey(PublicKey pk)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    */
encodeRsa2048PublicKey(PublicKey pk)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    */
encodeDh2048PublicKey(PublicKey pk)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    */
parsePublicKey(GenericPublicKey gpk)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    */
parseEcPublicKey(EcP256PublicKey p256pk)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    */
parseRsa2048PublicKey(SimpleRsaPublicKey pk)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
parseDh2048PublicKey(DhPublicKey pk)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    */
generateEcP256KeyPair()345   public static KeyPair generateEcP256KeyPair() {
346     return getEcKeyGen().generateKeyPair();
347   }
348 
349   /**
350    * @return a freshly generated 2048-bit RSA key pair.
351    */
generateRSA2048KeyPair()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    */
generateDh2048KeyPair()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    */
encodeDh2048PrivateKey(DHPrivateKey sk)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    */
parseDh2048PrivateKey(byte[] encodedX)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    */
validateEcP256CurvePoint(BigInteger x, BigInteger y)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    */
squareMod(BigInteger x, BigInteger p)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    */
validateEcP256CoordinateEncoding(byte[] p)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    */
validateSimpleRsaEncoding(byte[] n)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    */
validateDhEncoding(byte[] y)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    */
validateDhGroupElement(BigInteger y)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 
extractY(ECPublicKey epk)470   private static ByteString extractY(ECPublicKey epk) {
471     return ByteString.copyFrom(epk.getW().getAffineY().toByteArray());
472   }
473 
extractX(ECPublicKey epk)474   private static ByteString extractX(ECPublicKey epk) {
475     return ByteString.copyFrom(epk.getW().getAffineX().toByteArray());
476   }
477 
pkToECPublicKey(PublicKey pk)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 
pkToRSAPublicKey(PublicKey pk)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 
pkToDHPublicKey(PublicKey pk)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    */
getEcKeyGen()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    */
getRsaKeyGen()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
getDhKeyGen()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 
DHKeyShim(BigInteger eitherXorY, DHParameterSpec params)573     public DHKeyShim(BigInteger eitherXorY, DHParameterSpec params) {
574       this.eitherXorY = eitherXorY;
575       this.params = params;
576     }
577 
getParams()578     public DHParameterSpec getParams() {
579       return params;
580     }
581 
getAlgorithm()582     public String getAlgorithm() {
583       return "DH";
584     }
585 
getFormat()586     public String getFormat() {
587       return null;
588     }
589 
getEncoded()590     public byte[] getEncoded() {
591       return null;
592     }
593 
getX()594     public BigInteger getX() {
595       return eitherXorY;
596     }
597 
getY()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 {
DHPublicKeyShim(BigInteger y, DHParameterSpec params)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 {
DHPrivateKeyShim(BigInteger x, DHParameterSpec params)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    */
isLegacyCryptoRequired()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    */
determineIfLegacyCryptoRequired()635   private static boolean determineIfLegacyCryptoRequired() {
636     try {
637       getEcKeyGen();
638     } catch (Exception e) {
639       return true;
640     }
641     return false;
642   }
643 }
644