// check if this certificate is appropriate for this type of use // first check extensions, if they match, check expiration // note: we may want to move this code into the sun.security.validator // package CheckResult check(X509Certificate cert, Date date) { if (this == NONE) { return CheckResult.OK; } // check extensions try { // check extended key usage List<String> certEku = cert.getExtendedKeyUsage(); if ((certEku != null) && Collections.disjoint(validEku, certEku)) { // if extension present and it does not contain any of // the valid EKU OIDs, return extension_mismatch return CheckResult.EXTENSION_MISMATCH; } // check key usage boolean[] ku = cert.getKeyUsage(); if (ku != null) { String algorithm = cert.getPublicKey().getAlgorithm(); boolean kuSignature = getBit(ku, 0); if (algorithm.equals("RSA")) { // require either signature bit // or if server also allow key encipherment bit if (kuSignature == false) { if ((this == CLIENT) || (getBit(ku, 2) == false)) { return CheckResult.EXTENSION_MISMATCH; } } } else if (algorithm.equals("DSA")) { // require signature bit if (kuSignature == false) { return CheckResult.EXTENSION_MISMATCH; } } else if (algorithm.equals("DH")) { // require keyagreement bit if (getBit(ku, 4) == false) { return CheckResult.EXTENSION_MISMATCH; } } else if (algorithm.equals("EC")) { // require signature bit if (kuSignature == false) { return CheckResult.EXTENSION_MISMATCH; } // For servers, also require key agreement. // This is not totally accurate as the keyAgreement bit // is only necessary for static ECDH key exchange and // not ephemeral ECDH. We leave it in for now until // there are signs that this check causes problems // for real world EC certificates. if ((this == SERVER) && (getBit(ku, 4) == false)) { return CheckResult.EXTENSION_MISMATCH; } } } } catch (CertificateException e) { // extensions unparseable, return failure return CheckResult.EXTENSION_MISMATCH; } try { cert.checkValidity(date); return CheckResult.OK; } catch (CertificateException e) { return CheckResult.EXPIRED; } } } // enum for the result of the extension check // NOTE: the order of the constants is important as they are used // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH private static enum CheckResult { OK, // ok or not checked EXPIRED, // extensions valid but cert expired EXTENSION_MISMATCH, // extensions invalid (expiration not checked) } /* * Return a List of all candidate matches in the specified builder * that fit the parameters. * We exclude entries in the KeyStore if they are not: * . private key entries * . the certificates are not X509 certificates * . the algorithm of the key in the EE cert doesn't match one of keyTypes * . none of the certs is issued by a Principal in issuerSet * Using those entries would not be possible or they would almost * certainly be rejected by the peer. * * In addition to those checks, we also check the extensions in the EE * cert and its expiration. Even if there is a mismatch, we include * such certificates because they technically work and might be accepted * by the peer. This leads to more graceful failure and better error * messages if the cert expires from one day to the next. * * The return values are: * . null, if there are no matching entries at all * . if 'findAll' is 'false' and there is a perfect match, a List * with a single element (early return) * . if 'findAll' is 'false' and there is NO perfect match, a List * with all the imperfect matches (expired, wrong extensions) * . if 'findAll' is 'true', a List with all perfect and imperfect * matches */ private List<EntryStatus> getAliases(int builderIndex, List<KeyType> keyTypes, Set<Principal> issuerSet, boolean findAll, CheckType checkType, AlgorithmConstraints constraints) throws Exception { Builder builder = builders.get(builderIndex); KeyStore ks = builder.getKeyStore(); List<EntryStatus> results = null; Date date = verificationDate; boolean preferred = false; for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) { String alias = e.nextElement(); // check if it is a key entry (private key or secret key) if (ks.isKeyEntry(alias) == false) { continue; } Certificate[] chain = ks.getCertificateChain(alias); if ((chain == null) || (chain.length == 0)) { // must be secret key entry, ignore continue; } boolean incompatible = false; for (Certificate cert : chain) { if (cert instanceof X509Certificate == false) { // not an X509Certificate, ignore this alias incompatible = true; break; } } if (incompatible) { continue; } // check keytype int keyIndex = -1; int j = 0; for (KeyType keyType : keyTypes) { if (keyType.matches(chain)) { keyIndex = j; break; } j++; } if (keyIndex == -1) { if (useDebug) { debug.println("Ignoring alias " + alias + ": key algorithm does not match"); } continue; } // check issuers if (issuerSet != null) { boolean found = false; for (Certificate cert : chain) { X509Certificate xcert = (X509Certificate)cert; if (issuerSet.contains(xcert.getIssuerX500Principal())) { found = true; break; } } if (found == false) { if (useDebug) { debug.println("Ignoring alias " + alias + ": issuers do not match"); } continue; } } // check the algorithm constraints if (constraints != null && !conformsToAlgorithmConstraints(constraints, chain)) { if (useDebug) { debug.println("Ignoring alias " + alias + ": certificate list does not conform to " + "algorithm constraints"); } continue; } if (date == null) { date = new Date(); } CheckResult checkResult = checkType.check((X509Certificate)chain[0], date); EntryStatus status = new EntryStatus(builderIndex, keyIndex, alias, chain, checkResult); if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) { preferred = true; } if (preferred && (findAll == false)) { // if we have a good match and do not need all matches, // return immediately return Collections.singletonList(status); } else { if (results == null) { results = new ArrayList<EntryStatus>(); } results.add(status); } } return results; } private static boolean conformsToAlgorithmConstraints( AlgorithmConstraints constraints, Certificate[] chain) { AlgorithmChecker checker = new AlgorithmChecker(constraints); try { checker.init(false); } catch (CertPathValidatorException cpve) { // unlikely to happen return false; } // It is a forward checker, so we need to check from trust to target. for (int i = chain.length - 1; i >= 0; i--) { Certificate cert = chain[i]; try { // We don't care about the unresolved critical extensions. checker.check(cert, Collections.<String>emptySet()); } catch (CertPathValidatorException cpve) { return false; } } return true; } }Lines 501–741 of 741. Previous lines
// check if this certificate is appropriate for this type of use // first check extensions, if they match, check expiration // note: we may want to move this code into the sun.security.validator // package CheckResult check(X509Certificate cert, Date date) { if (this == NONE) { return CheckResult.OK; } // check extensions try { // check extended key usage List<String> certEku = cert.getExtendedKeyUsage(); if ((certEku != null) && Collections.disjoint(validEku, certEku)) { // if extension present and it does not contain any of // the valid EKU OIDs, return extension_mismatch return CheckResult.EXTENSION_MISMATCH; } // check key usage boolean[] ku = cert.getKeyUsage(); if (ku != null) { String algorithm = cert.getPublicKey().getAlgorithm(); boolean kuSignature = getBit(ku, 0); if (algorithm.equals("RSA")) { // require either signature bit // or if server also allow key encipherment bit if (kuSignature == false) { if ((this == CLIENT) || (getBit(ku, 2) == false)) { return CheckResult.EXTENSION_MISMATCH; } } } else if (algorithm.equals("DSA")) { // require signature bit if (kuSignature == false) { return CheckResult.EXTENSION_MISMATCH; } } else if (algorithm.equals("DH")) { // require keyagreement bit if (getBit(ku, 4) == false) { return CheckResult.EXTENSION_MISMATCH; } } else if (algorithm.equals("EC")) { // require signature bit if (kuSignature == false) { return CheckResult.EXTENSION_MISMATCH; } // For servers, also require key agreement. // This is not totally accurate as the keyAgreement bit // is only necessary for static ECDH key exchange and // not ephemeral ECDH. We leave it in for now until // there are signs that this check causes problems // for real world EC certificates. if ((this == SERVER) && (getBit(ku, 4) == false)) { return CheckResult.EXTENSION_MISMATCH; } } } } catch (CertificateException e) { // extensions unparseable, return failure return CheckResult.EXTENSION_MISMATCH; } try { cert.checkValidity(date); return CheckResult.OK; } catch (CertificateException e) { return CheckResult.EXPIRED; } } } // enum for the result of the extension check // NOTE: the order of the constants is important as they are used // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH private static enum CheckResult { OK, // ok or not checked EXPIRED, // extensions valid but cert expired EXTENSION_MISMATCH, // extensions invalid (expiration not checked) } /* * Return a List of all candidate matches in the specified builder * that fit the parameters. * We exclude entries in the KeyStore if they are not: * . private key entries * . the certificates are not X509 certificates * . the algorithm of the key in the EE cert doesn't match one of keyTypes * . none of the certs is issued by a Principal in issuerSet * Using those entries would not be possible or they would almost * certainly be rejected by the peer. * * In addition to those checks, we also check the extensions in the EE * cert and its expiration. Even if there is a mismatch, we include * such certificates because they technically work and might be accepted * by the peer. This leads to more graceful failure and better error * messages if the cert expires from one day to the next. * * The return values are: * . null, if there are no matching entries at all * . if 'findAll' is 'false' and there is a perfect match, a List * with a single element (early return) * . if 'findAll' is 'false' and there is NO perfect match, a List * with all the imperfect matches (expired, wrong extensions) * . if 'findAll' is 'true', a List with all perfect and imperfect * matches */ private List<EntryStatus> getAliases(int builderIndex, List<KeyType> keyTypes, Set<Principal> issuerSet, boolean findAll, CheckType checkType, AlgorithmConstraints constraints) throws Exception { Builder builder = builders.get(builderIndex); KeyStore ks = builder.getKeyStore(); List<EntryStatus> results = null; Date date = verificationDate; boolean preferred = false; for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) { String alias = e.nextElement(); // check if it is a key entry (private key or secret key) if (ks.isKeyEntry(alias) == false) { continue; } Certificate[] chain = ks.getCertificateChain(alias); if ((chain == null) || (chain.length == 0)) { // must be secret key entry, ignore continue; } boolean incompatible = false; for (Certificate cert : chain) { if (cert instanceof X509Certificate == false) { // not an X509Certificate, ignore this alias incompatible = true; break; } } if (incompatible) { continue; } // check keytype int keyIndex = -1; int j = 0; for (KeyType keyType : keyTypes) { if (keyType.matches(chain)) { keyIndex = j; break; } j++; } if (keyIndex == -1) { if (useDebug) { debug.println("Ignoring alias " + alias + ": key algorithm does not match"); } continue; } // check issuers if (issuerSet != null) { boolean found = false; for (Certificate cert : chain) { X509Certificate xcert = (X509Certificate)cert; if (issuerSet.contains(xcert.getIssuerX500Principal())) { found = true; break; } } if (found == false) { if (useDebug) { debug.println("Ignoring alias " + alias + ": issuers do not match"); } continue; } } // check the algorithm constraints if (constraints != null && !conformsToAlgorithmConstraints(constraints, chain)) { if (useDebug) { debug.println("Ignoring alias " + alias + ": certificate list does not conform to " + "algorithm constraints"); } continue; } if (date == null) { date = new Date(); } CheckResult checkResult = checkType.check((X509Certificate)chain[0], date); EntryStatus status = new EntryStatus(builderIndex, keyIndex, alias, chain, checkResult); if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) { preferred = true; } if (preferred && (findAll == false)) { // if we have a good match and do not need all matches, // return immediately return Collections.singletonList(status); } else { if (results == null) { results = new ArrayList<EntryStatus>(); } results.add(status); } } return results; } private static boolean conformsToAlgorithmConstraints( AlgorithmConstraints constraints, Certificate[] chain) { AlgorithmChecker checker = new AlgorithmChecker(constraints); try { checker.init(false); } catch (CertPathValidatorException cpve) { // unlikely to happen return false; } // It is a forward checker, so we need to check from trust to target. for (int i = chain.length - 1; i >= 0; i--) { Certificate cert = chain[i]; try { // We don't care about the unresolved critical extensions. checker.check(cert, Collections.<String>emptySet()); } catch (CertPathValidatorException cpve) { return false; } } return true; } }