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