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