1 package org.bouncycastle.jsse.provider; 2 3 import java.net.Socket; 4 import java.security.KeyStore; 5 import java.security.KeyStore.PrivateKeyEntry; 6 import java.security.KeyStoreException; 7 import java.security.NoSuchAlgorithmException; 8 import java.security.Principal; 9 import java.security.PrivateKey; 10 import java.security.PublicKey; 11 import java.security.UnrecoverableKeyException; 12 import java.security.cert.CertPathValidatorException; 13 import java.security.cert.CertificateException; 14 import java.security.cert.X509Certificate; 15 import java.security.interfaces.DSAPublicKey; 16 import java.security.interfaces.ECPublicKey; 17 import java.util.ArrayList; 18 import java.util.Collections; 19 import java.util.Date; 20 import java.util.Enumeration; 21 import java.util.HashMap; 22 import java.util.HashSet; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Set; 26 import java.util.logging.Level; 27 import java.util.logging.Logger; 28 29 import javax.net.ssl.SSLEngine; 30 31 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 32 import org.bouncycastle.asn1.x509.KeyPurposeId; 33 import org.bouncycastle.asn1.x9.ECNamedCurveTable; 34 import org.bouncycastle.jcajce.util.JcaJceHelper; 35 import org.bouncycastle.jsse.BCExtendedSSLSession; 36 import org.bouncycastle.jsse.BCSNIHostName; 37 import org.bouncycastle.jsse.BCX509ExtendedKeyManager; 38 import org.bouncycastle.jsse.BCX509Key; 39 import org.bouncycastle.jsse.java.security.BCAlgorithmConstraints; 40 import org.bouncycastle.tls.KeyExchangeAlgorithm; 41 import org.bouncycastle.tls.NamedGroup; 42 import org.bouncycastle.tls.ProtocolVersion; 43 import org.bouncycastle.tls.TlsUtils; 44 45 class ProvX509KeyManagerSimple 46 extends BCX509ExtendedKeyManager 47 { 48 private static final Logger LOG = Logger.getLogger(ProvX509KeyManagerSimple.class.getName()); 49 50 private final boolean isInFipsMode; 51 private final JcaJceHelper helper; 52 private final Map<String, Credential> credentials; 53 54 private static final Map<String, PublicKeyFilter> FILTERS_CLIENT = createFiltersClient(); 55 private static final Map<String, PublicKeyFilter> FILTERS_SERVER = createFiltersServer(); 56 addECFilter13(Map<String, PublicKeyFilter> filters, int namedGroup13)57 private static void addECFilter13(Map<String, PublicKeyFilter> filters, int namedGroup13) 58 { 59 if (!NamedGroup.canBeNegotiated(namedGroup13, ProtocolVersion.TLSv13)) 60 { 61 throw new IllegalStateException("Invalid named group for TLS 1.3 EC filter"); 62 } 63 64 String curveName = NamedGroup.getCurveName(namedGroup13); 65 if (null != curveName) 66 { 67 ASN1ObjectIdentifier standardOID = ECNamedCurveTable.getOID(curveName); 68 if (null != standardOID) 69 { 70 String keyType = JsseUtils.getKeyType13("EC", namedGroup13); 71 PublicKeyFilter filter = new ECPublicKeyFilter13(standardOID); 72 addFilterToMap(filters, keyType, filter); 73 return; 74 } 75 } 76 77 LOG.warning("Failed to register public key filter for EC with " + NamedGroup.getText(namedGroup13)); 78 } 79 addFilter(Map<String, PublicKeyFilter> filters, String keyType)80 private static void addFilter(Map<String, PublicKeyFilter> filters, String keyType) 81 { 82 String algorithm = keyType; 83 84 addFilter(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, algorithm, null, keyType); 85 } 86 addFilter(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, String... keyTypes)87 private static void addFilter(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, String... keyTypes) 88 { 89 addFilter(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, null, clazz, keyTypes); 90 } 91 addFilter(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, Class<? extends PublicKey> clazz, String... keyTypes)92 private static void addFilter(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, 93 Class<? extends PublicKey> clazz, String... keyTypes) 94 { 95 PublicKeyFilter filter = new DefaultPublicKeyFilter(algorithm, clazz, keyUsageBit); 96 97 for (String keyType : keyTypes) 98 { 99 addFilterToMap(filters, keyType, filter); 100 } 101 } 102 addFilterLegacyServer(Map<String, PublicKeyFilter> filters, String algorithm, int... keyExchangeAlgorithms)103 private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, String algorithm, 104 int... keyExchangeAlgorithms) 105 { 106 addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, algorithm, keyExchangeAlgorithms); 107 } 108 addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, int... keyExchangeAlgorithms)109 private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, 110 int... keyExchangeAlgorithms) 111 { 112 addFilterLegacyServer(filters, keyUsageBit, algorithm, null, keyExchangeAlgorithms); 113 } 114 addFilterLegacyServer(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, int... keyExchangeAlgorithms)115 private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, 116 int... keyExchangeAlgorithms) 117 { 118 addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, null, clazz, keyExchangeAlgorithms); 119 } 120 addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, Class<? extends PublicKey> clazz, int... keyExchangeAlgorithms)121 private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, 122 Class<? extends PublicKey> clazz, int... keyExchangeAlgorithms) 123 { 124 addFilter(filters, keyUsageBit, algorithm, clazz, getKeyTypesLegacyServer(keyExchangeAlgorithms)); 125 } 126 addFilterToMap(Map<String, PublicKeyFilter> filters, String keyType, PublicKeyFilter filter)127 private static void addFilterToMap(Map<String, PublicKeyFilter> filters, String keyType, PublicKeyFilter filter) 128 { 129 if (null != filters.put(keyType, filter)) 130 { 131 throw new IllegalStateException("Duplicate keys in filters"); 132 } 133 } 134 createFiltersClient()135 private static Map<String, PublicKeyFilter> createFiltersClient() 136 { 137 Map<String, PublicKeyFilter> filters = new HashMap<String, PublicKeyFilter>(); 138 139 addFilter(filters, "Ed25519"); 140 addFilter(filters, "Ed448"); 141 142 addECFilter13(filters, NamedGroup.brainpoolP256r1tls13); 143 addECFilter13(filters, NamedGroup.brainpoolP384r1tls13); 144 addECFilter13(filters, NamedGroup.brainpoolP512r1tls13); 145 146 addECFilter13(filters, NamedGroup.secp256r1); 147 addECFilter13(filters, NamedGroup.secp384r1); 148 addECFilter13(filters, NamedGroup.secp521r1); 149 150 // TODO Perhaps check the public key OID explicitly for these 151 addFilter(filters, "RSA"); 152 addFilter(filters, "RSASSA-PSS"); 153 154 addFilter(filters, DSAPublicKey.class, "DSA"); 155 addFilter(filters, ECPublicKey.class, "EC"); 156 157 return Collections.unmodifiableMap(filters); 158 } 159 createFiltersServer()160 private static Map<String, PublicKeyFilter> createFiltersServer() 161 { 162 Map<String, PublicKeyFilter> filters = new HashMap<String, PublicKeyFilter>(); 163 164 addFilter(filters, "Ed25519"); 165 addFilter(filters, "Ed448"); 166 167 addECFilter13(filters, NamedGroup.brainpoolP256r1tls13); 168 addECFilter13(filters, NamedGroup.brainpoolP384r1tls13); 169 addECFilter13(filters, NamedGroup.brainpoolP512r1tls13); 170 171 addECFilter13(filters, NamedGroup.secp256r1); 172 addECFilter13(filters, NamedGroup.secp384r1); 173 addECFilter13(filters, NamedGroup.secp521r1); 174 175 // TODO Perhaps check the public key OID explicitly for these 176 addFilter(filters, "RSA"); 177 addFilter(filters, "RSASSA-PSS"); 178 179 addFilterLegacyServer(filters, DSAPublicKey.class, KeyExchangeAlgorithm.DHE_DSS, KeyExchangeAlgorithm.SRP_DSS); 180 addFilterLegacyServer(filters, ECPublicKey.class, KeyExchangeAlgorithm.ECDHE_ECDSA); 181 addFilterLegacyServer(filters, "RSA", KeyExchangeAlgorithm.DHE_RSA, KeyExchangeAlgorithm.ECDHE_RSA, 182 KeyExchangeAlgorithm.SRP_RSA); 183 addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_KEY_ENCIPHERMENT, "RSA", KeyExchangeAlgorithm.RSA); 184 185 return Collections.unmodifiableMap(filters); 186 } 187 getKeyTypesLegacyServer(int... keyExchangeAlgorithms)188 private static String[] getKeyTypesLegacyServer(int... keyExchangeAlgorithms) 189 { 190 int count = keyExchangeAlgorithms.length; 191 String[] keyTypes = new String[count]; 192 for (int i = 0; i < count; ++i) 193 { 194 keyTypes[i] = JsseUtils.getKeyTypeLegacyServer(keyExchangeAlgorithms[i]); 195 } 196 return keyTypes; 197 } 198 loadCredentials(KeyStore ks, char[] password)199 private static Map<String, Credential> loadCredentials(KeyStore ks, char[] password) 200 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException 201 { 202 Map<String, Credential> credentials = new HashMap<String, Credential>(4); 203 204 if (null != ks) 205 { 206 Enumeration<String> aliases = ks.aliases(); 207 while (aliases.hasMoreElements()) 208 { 209 String alias = aliases.nextElement(); 210 if (!ks.entryInstanceOf(alias, PrivateKeyEntry.class)) 211 { 212 continue; 213 } 214 215 PrivateKey privateKey = (PrivateKey)ks.getKey(alias, password); 216 if (null == privateKey) 217 { 218 continue; 219 } 220 221 X509Certificate[] certificateChain = JsseUtils.getX509CertificateChain(ks.getCertificateChain(alias)); 222 if (TlsUtils.isNullOrEmpty(certificateChain)) 223 { 224 continue; 225 } 226 227 credentials.put(alias, new Credential(alias, privateKey, certificateChain)); 228 } 229 } 230 231 return Collections.unmodifiableMap(credentials); 232 } 233 ProvX509KeyManagerSimple(boolean isInFipsMode, JcaJceHelper helper, KeyStore ks, char[] password)234 ProvX509KeyManagerSimple(boolean isInFipsMode, JcaJceHelper helper, KeyStore ks, char[] password) 235 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException 236 { 237 this.isInFipsMode = isInFipsMode; 238 this.helper = helper; 239 this.credentials = loadCredentials(ks, password); 240 } 241 chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket)242 public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) 243 { 244 return chooseAlias(getKeyTypes(keyTypes), issuers, TransportData.from(socket), false); 245 } 246 247 @Override chooseClientKeyBC(String[] keyTypes, Principal[] issuers, Socket socket)248 public BCX509Key chooseClientKeyBC(String[] keyTypes, Principal[] issuers, Socket socket) 249 { 250 return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(socket), false); 251 } 252 chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine)253 public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) 254 { 255 return chooseAlias(getKeyTypes(keyTypes), issuers, TransportData.from(engine), false); 256 } 257 258 @Override chooseEngineClientKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine)259 public BCX509Key chooseEngineClientKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine) 260 { 261 return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(engine), false); 262 } 263 chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)264 public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) 265 { 266 return chooseAlias(getKeyTypes(keyType), issuers, TransportData.from(engine), true); 267 } 268 269 @Override chooseEngineServerKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine)270 public BCX509Key chooseEngineServerKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine) 271 { 272 return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(engine), true); 273 } 274 chooseServerAlias(String keyType, Principal[] issuers, Socket socket)275 public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) 276 { 277 return chooseAlias(getKeyTypes(keyType), issuers, TransportData.from(socket), true); 278 } 279 280 @Override chooseServerKeyBC(String[] keyTypes, Principal[] issuers, Socket socket)281 public BCX509Key chooseServerKeyBC(String[] keyTypes, Principal[] issuers, Socket socket) 282 { 283 return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(socket), true); 284 } 285 getCertificateChain(String alias)286 public X509Certificate[] getCertificateChain(String alias) 287 { 288 Credential credential = getCredential(alias); 289 return null == credential ? null : credential.certificateChain.clone(); 290 } 291 getClientAliases(String keyType, Principal[] issuers)292 public String[] getClientAliases(String keyType, Principal[] issuers) 293 { 294 return getAliases(getKeyTypes(keyType), issuers, null, false); 295 } 296 getPrivateKey(String alias)297 public PrivateKey getPrivateKey(String alias) 298 { 299 Credential credential = getCredential(alias); 300 return null == credential ? null : credential.privateKey; 301 } 302 getServerAliases(String keyType, Principal[] issuers)303 public String[] getServerAliases(String keyType, Principal[] issuers) 304 { 305 return getAliases(getKeyTypes(keyType), issuers, null, true); 306 } 307 308 @Override getKeyBC(String keyType, String alias)309 protected BCX509Key getKeyBC(String keyType, String alias) 310 { 311 Credential credential = getCredential(alias); 312 return createKeyBC(keyType, credential); 313 } 314 chooseAlias(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)315 private String chooseAlias(List<String> keyTypes, Principal[] issuers, TransportData transportData, 316 boolean forServer) 317 { 318 Match bestMatch = getBestMatch(keyTypes, issuers, transportData, forServer); 319 320 if (bestMatch.compareTo(Match.NOTHING) < 0) 321 { 322 String keyType = keyTypes.get(bestMatch.keyTypeIndex); 323 String alias = getAlias(bestMatch); 324 if (LOG.isLoggable(Level.FINE)) 325 { 326 LOG.fine("Found matching key of type: " + keyType + ", returning alias: " + alias); 327 } 328 return alias; 329 } 330 331 LOG.fine("No matching key found"); 332 return null; 333 } 334 chooseKeyBC(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)335 private BCX509Key chooseKeyBC(List<String> keyTypes, Principal[] issuers, TransportData transportData, 336 boolean forServer) 337 { 338 Match bestMatch = getBestMatch(keyTypes, issuers, transportData, forServer); 339 340 if (bestMatch.compareTo(Match.NOTHING) < 0) 341 { 342 String keyType = keyTypes.get(bestMatch.keyTypeIndex); 343 344 BCX509Key keyBC = createKeyBC(keyType, bestMatch.credential); 345 if (null != keyBC) 346 { 347 if (LOG.isLoggable(Level.FINE)) 348 { 349 LOG.fine("Found matching key of type: " + keyType + ", from alias: " + getAlias(bestMatch)); 350 } 351 return keyBC; 352 } 353 } 354 355 LOG.fine("No matching key found"); 356 return null; 357 } 358 createKeyBC(String keyType, Credential credential)359 private BCX509Key createKeyBC(String keyType, Credential credential) 360 { 361 return null == credential 362 ? null 363 : new ProvX509Key(keyType, credential.privateKey, credential.certificateChain); 364 } 365 getAliases(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)366 private String[] getAliases(List<String> keyTypes, Principal[] issuers, TransportData transportData, 367 boolean forServer) 368 { 369 if (!credentials.isEmpty() && !keyTypes.isEmpty()) 370 { 371 int keyTypeLimit = keyTypes.size(); 372 Set<Principal> uniqueIssuers = getUniquePrincipals(issuers); 373 BCAlgorithmConstraints algorithmConstraints = TransportData.getAlgorithmConstraints(transportData, true); 374 Date atDate = new Date(); 375 String requestedHostName = getRequestedHostName(transportData, forServer); 376 List<Match> matches = null; 377 378 for (Credential credential : credentials.values()) 379 { 380 Match match = getPotentialMatch(credential, keyTypes, keyTypeLimit, uniqueIssuers, algorithmConstraints, 381 forServer, atDate, requestedHostName); 382 383 if (match.compareTo(Match.NOTHING) < 0) 384 { 385 matches = addToMatches(matches, match); 386 } 387 } 388 389 if (null != matches && !matches.isEmpty()) 390 { 391 // NOTE: We are relying on this being a stable sort 392 Collections.sort(matches); 393 394 return getAliases(matches); 395 } 396 } 397 398 return null; 399 } 400 getBestMatch(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)401 private Match getBestMatch(List<String> keyTypes, Principal[] issuers, TransportData transportData, 402 boolean forServer) 403 { 404 Match bestMatchSoFar = Match.NOTHING; 405 406 if (!credentials.isEmpty() && !keyTypes.isEmpty()) 407 { 408 int keyTypeLimit = keyTypes.size(); 409 Set<Principal> uniqueIssuers = getUniquePrincipals(issuers); 410 BCAlgorithmConstraints algorithmConstraints = TransportData.getAlgorithmConstraints(transportData, true); 411 Date atDate = new Date(); 412 String requestedHostName = getRequestedHostName(transportData, forServer); 413 414 for (Credential credential : credentials.values()) 415 { 416 Match match = getPotentialMatch(credential, keyTypes, keyTypeLimit, uniqueIssuers, 417 algorithmConstraints, forServer, atDate, requestedHostName); 418 419 if (match.compareTo(bestMatchSoFar) < 0) 420 { 421 bestMatchSoFar = match; 422 423 if (bestMatchSoFar.isIdeal()) 424 { 425 return bestMatchSoFar; 426 } 427 if (bestMatchSoFar.isValid()) 428 { 429 keyTypeLimit = Math.min(keyTypeLimit, bestMatchSoFar.keyTypeIndex + 1); 430 } 431 } 432 } 433 } 434 435 return bestMatchSoFar; 436 } 437 getPotentialMatch(Credential credential, List<String> keyTypes, int keyTypeLimit, Set<Principal> uniqueIssuers, BCAlgorithmConstraints algorithmConstraints, boolean forServer, Date atDate, String requestedHostName)438 private Match getPotentialMatch(Credential credential, List<String> keyTypes, int keyTypeLimit, 439 Set<Principal> uniqueIssuers, BCAlgorithmConstraints algorithmConstraints, boolean forServer, Date atDate, 440 String requestedHostName) 441 { 442 X509Certificate[] chain = credential.certificateChain; 443 if (!TlsUtils.isNullOrEmpty(chain) && isSuitableChainForIssuers(chain, uniqueIssuers)) 444 { 445 int keyTypeIndex = getSuitableKeyTypeForEECert(chain[0], keyTypes, keyTypeLimit, algorithmConstraints, 446 forServer); 447 if (keyTypeIndex >= 0 && isSuitableChain(chain, algorithmConstraints, forServer)) 448 { 449 Match.Quality quality = getCertificateQuality(chain[0], atDate, requestedHostName); 450 451 return new Match(quality, keyTypeIndex, credential); 452 } 453 } 454 return Match.NOTHING; 455 } 456 getCredential(String alias)457 private Credential getCredential(String alias) 458 { 459 return null == alias ? null : credentials.get(alias); 460 } 461 isSuitableChain(X509Certificate[] chain, BCAlgorithmConstraints algorithmConstraints, boolean forServer)462 private boolean isSuitableChain(X509Certificate[] chain, BCAlgorithmConstraints algorithmConstraints, 463 boolean forServer) 464 { 465 try 466 { 467 Set<X509Certificate> trustedCerts = Collections.emptySet(); 468 KeyPurposeId ekuOID = ProvX509KeyManager.getRequiredExtendedKeyUsage(forServer); 469 int kuBit = -1; // i.e. no checks; we handle them in isSuitableEECert 470 471 ProvAlgorithmChecker.checkChain(isInFipsMode, helper, algorithmConstraints, trustedCerts, chain, ekuOID, 472 kuBit); 473 474 return true; 475 } 476 catch (CertPathValidatorException e) 477 { 478 return false; 479 } 480 } 481 addToMatches(List<Match> matches, Match match)482 private static List<Match> addToMatches(List<Match> matches, Match match) 483 { 484 if (null == matches) 485 { 486 matches = new ArrayList<Match>(); 487 } 488 489 matches.add(match); 490 return matches; 491 } 492 getAlias(Match match)493 private static String getAlias(Match match) 494 { 495 return match.credential.alias; 496 } 497 getAliases(List<Match> matches)498 private static String[] getAliases(List<Match> matches) 499 { 500 int count = matches.size(), pos = 0; 501 String[] result = new String[count]; 502 for (Match match : matches) 503 { 504 result[pos++] = getAlias(match); 505 } 506 return result; 507 } 508 getCertificateQuality(X509Certificate certificate, Date atDate, String requestedHostName)509 private static Match.Quality getCertificateQuality(X509Certificate certificate, Date atDate, String requestedHostName) 510 { 511 try 512 { 513 certificate.checkValidity(atDate); 514 } 515 catch (CertificateException e) 516 { 517 return Match.Quality.EXPIRED; 518 } 519 520 if (null != requestedHostName) 521 { 522 try 523 { 524 /* 525 * NOTE: For compatibility with SunJSSE, we also re-use HTTPS endpoint ID checks for 526 * SNI certificate selection. 527 */ 528 ProvX509TrustManager.checkEndpointID(requestedHostName, certificate, "HTTPS"); 529 } 530 catch (CertificateException e) 531 { 532 return Match.Quality.MISMATCH_SNI; 533 } 534 } 535 536 /* 537 * Prefer RSA certificates with more specific KeyUsage over "multi-use" ones. 538 */ 539 if ("RSA".equalsIgnoreCase(JsseUtils.getPublicKeyAlgorithm(certificate.getPublicKey()))) 540 { 541 boolean[] keyUsage = certificate.getKeyUsage(); 542 if (ProvAlgorithmChecker.supportsKeyUsage(keyUsage, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE) && 543 ProvAlgorithmChecker.supportsKeyUsage(keyUsage, ProvAlgorithmChecker.KU_KEY_ENCIPHERMENT)) 544 { 545 return Match.Quality.RSA_MULTI_USE; 546 } 547 } 548 549 return Match.Quality.OK; 550 } 551 getKeyTypes(String... keyTypes)552 private static List<String> getKeyTypes(String... keyTypes) 553 { 554 if (null != keyTypes && keyTypes.length > 0) 555 { 556 ArrayList<String> result = new ArrayList<String>(keyTypes.length); 557 for (String keyType : keyTypes) 558 { 559 if (null == keyType) 560 { 561 throw new IllegalArgumentException("Key types cannot be null"); 562 } 563 if (!result.contains(keyType)) 564 { 565 result.add(keyType); 566 } 567 } 568 return Collections.unmodifiableList(result); 569 } 570 return Collections.emptyList(); 571 } 572 getRequestedHostName(TransportData transportData, boolean forServer)573 private static String getRequestedHostName(TransportData transportData, boolean forServer) 574 { 575 if (null != transportData && forServer) 576 { 577 BCExtendedSSLSession sslSession = transportData.getHandshakeSession(); 578 if (null != sslSession) 579 { 580 BCSNIHostName sniHostName = JsseUtils.getSNIHostName(sslSession.getRequestedServerNames()); 581 if (null != sniHostName) 582 { 583 return sniHostName.getAsciiName(); 584 } 585 } 586 } 587 return null; 588 } 589 getSuitableKeyTypeForEECert(X509Certificate eeCert, List<String> keyTypes, int keyTypeLimit, BCAlgorithmConstraints algorithmConstraints, boolean forServer)590 private static int getSuitableKeyTypeForEECert(X509Certificate eeCert, List<String> keyTypes, int keyTypeLimit, 591 BCAlgorithmConstraints algorithmConstraints, boolean forServer) 592 { 593 Map<String, PublicKeyFilter> filters = forServer ? FILTERS_SERVER : FILTERS_CLIENT; 594 595 PublicKey publicKey = eeCert.getPublicKey(); 596 boolean[] keyUsage = eeCert.getKeyUsage(); 597 598 for (int keyTypeIndex = 0; keyTypeIndex < keyTypeLimit; ++keyTypeIndex) 599 { 600 String keyType = keyTypes.get(keyTypeIndex); 601 PublicKeyFilter filter = filters.get(keyType); 602 if (null != filter && filter.accepts(publicKey, keyUsage, algorithmConstraints)) 603 { 604 return keyTypeIndex; 605 } 606 } 607 608 return -1; 609 } 610 getUniquePrincipals(Principal[] principals)611 private static Set<Principal> getUniquePrincipals(Principal[] principals) 612 { 613 if (null == principals) 614 { 615 return null; 616 } 617 if (principals.length > 0) 618 { 619 Set<Principal> result = new HashSet<Principal>(); 620 for (int i = 0; i < principals.length; ++i) 621 { 622 Principal principal = principals[i]; 623 if (null != principal) 624 { 625 result.add(principal); 626 } 627 } 628 if (!result.isEmpty()) 629 { 630 return Collections.unmodifiableSet(result); 631 } 632 } 633 return Collections.emptySet(); 634 } 635 isSuitableChainForIssuers(X509Certificate[] chain, Set<Principal> uniqueIssuers)636 private static boolean isSuitableChainForIssuers(X509Certificate[] chain, Set<Principal> uniqueIssuers) 637 { 638 // NOTE: Empty issuers means same as absent issuers, per SunJSSE 639 if (null == uniqueIssuers || uniqueIssuers.isEmpty()) 640 { 641 return true; 642 } 643 int pos = chain.length; 644 while (--pos >= 0) 645 { 646 if (uniqueIssuers.contains(chain[pos].getIssuerX500Principal())) 647 { 648 return true; 649 } 650 } 651 X509Certificate eeCert = chain[0]; 652 return eeCert.getBasicConstraints() >= 0 653 && uniqueIssuers.contains(eeCert.getSubjectX500Principal()); 654 } 655 656 private static class Credential 657 { 658 private final String alias; 659 private final PrivateKey privateKey; 660 private final X509Certificate[] certificateChain; 661 Credential(String alias, PrivateKey privateKey, X509Certificate[] certificateChain)662 Credential(String alias, PrivateKey privateKey, X509Certificate[] certificateChain) 663 { 664 this.alias = alias; 665 this.privateKey = privateKey; 666 this.certificateChain = certificateChain; 667 } 668 } 669 670 private static final class Match 671 implements Comparable<Match> 672 { 673 // NOTE: We rely on these being in preference order. 674 static enum Quality 675 { 676 OK, 677 RSA_MULTI_USE, 678 MISMATCH_SNI, 679 EXPIRED, 680 // TODO[jsse] Consider allowing certificates with invalid ExtendedKeyUsage and/or KeyUsage (as SunJSSE does) 681 // MISMATCH_EKU, 682 // MISMATCH_KU, 683 NONE 684 } 685 686 static final Quality INVALID = Quality.MISMATCH_SNI; 687 static final Match NOTHING = new Match(Quality.NONE, -1, null); 688 689 final Quality quality; 690 final int keyTypeIndex; 691 final Credential credential; 692 Match(Quality quality, int keyTypeIndex, Credential credential)693 Match(Quality quality, int keyTypeIndex, Credential credential) 694 { 695 this.quality = quality; 696 this.keyTypeIndex = keyTypeIndex; 697 this.credential = credential; 698 } 699 compareTo(Match that)700 public int compareTo(Match that) 701 { 702 int cmp = Boolean.compare(that.isValid(), this.isValid()); 703 if (cmp == 0) 704 { 705 cmp = Integer.compare(this.keyTypeIndex, that.keyTypeIndex); 706 if (cmp == 0) 707 { 708 cmp = this.quality.compareTo(that.quality); 709 } 710 } 711 return cmp; 712 } 713 isIdeal()714 boolean isIdeal() 715 { 716 return Quality.OK == quality && 0 == keyTypeIndex; 717 } 718 isValid()719 boolean isValid() 720 { 721 return quality.compareTo(INVALID) < 0; 722 } 723 } 724 725 private static interface PublicKeyFilter 726 { accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)727 boolean accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints); 728 } 729 730 private static final class DefaultPublicKeyFilter 731 implements PublicKeyFilter 732 { 733 final String algorithm; 734 final Class<? extends PublicKey> clazz; 735 final int keyUsageBit; 736 DefaultPublicKeyFilter(String algorithm, Class<? extends PublicKey> clazz, int keyUsageBit)737 DefaultPublicKeyFilter(String algorithm, Class<? extends PublicKey> clazz, int keyUsageBit) 738 { 739 this.algorithm = algorithm; 740 this.clazz = clazz; 741 this.keyUsageBit = keyUsageBit; 742 } 743 accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)744 public boolean accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints) 745 { 746 return appliesTo(publicKey) 747 && ProvAlgorithmChecker.permitsKeyUsage(publicKey, keyUsage, keyUsageBit, algorithmConstraints); 748 } 749 appliesTo(PublicKey publicKey)750 private boolean appliesTo(PublicKey publicKey) 751 { 752 return (null != algorithm && algorithm.equalsIgnoreCase(JsseUtils.getPublicKeyAlgorithm(publicKey))) 753 || (null != clazz && clazz.isInstance(publicKey)); 754 } 755 } 756 757 private static final class ECPublicKeyFilter13 758 implements PublicKeyFilter 759 { 760 final ASN1ObjectIdentifier standardOID; 761 ECPublicKeyFilter13(ASN1ObjectIdentifier standardOID)762 ECPublicKeyFilter13(ASN1ObjectIdentifier standardOID) 763 { 764 this.standardOID = standardOID; 765 } 766 accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)767 public boolean accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints) 768 { 769 return appliesTo(publicKey) 770 && ProvAlgorithmChecker.permitsKeyUsage(publicKey, keyUsage, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, 771 algorithmConstraints); 772 } 773 appliesTo(PublicKey publicKey)774 private boolean appliesTo(PublicKey publicKey) 775 { 776 if ("EC".equalsIgnoreCase(JsseUtils.getPublicKeyAlgorithm(publicKey)) 777 || ECPublicKey.class.isInstance(publicKey)) 778 { 779 ASN1ObjectIdentifier oid = JsseUtils.getNamedCurveOID(publicKey); 780 if (standardOID.equals(oid)) 781 { 782 return true; 783 } 784 } 785 return false; 786 } 787 } 788 } 789