public static void main(String[] args) throws Exception {

    String s = "-keystore ks -storepass changeit -keypass changeit -debug ";
    new File("ks").delete();

    // Create a cert with an unknown extension: 1.2.3.4, and an invalid
    // KeyUsage extension
    String genkey =
        s
            + "-genkeypair -alias a -dname CN=A -ext 1.2.3.4=1234 "
            + "-ext "
            + PKIXExtensions.KeyUsage_Id.toString()
            + "=5678";
    sun.security.tools.keytool.Main.main(genkey.split(" "));

    // Get the list output to a string
    String list = s + "-list -v";
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    PrintStream oldOut = System.out;
    System.setOut(new PrintStream(bout));
    sun.security.tools.keytool.Main.main(list.split(" "));
    System.setOut(oldOut);
    String out = bout.toString();
    System.out.println(out);

    if (!out.contains("1.2.3.4")) {
      throw new Exception("Unknown extension 1.2.3.4 is not listed");
    }
    if (!out.contains("0000: 12 34")) {
      throw new Exception("Unknown extension 1.2.3.4 has no content");
    }
    if (!out.contains("Unparseable KeyUsage")) {
      throw new Exception("Unparseable KeyUsage is not listed");
    }
    if (!out.contains("0000: 56 78")) {
      throw new Exception("Unparseable KeyUsage has no content");
    }
  }
  /**
   * Verifies a matching certificate.
   *
   * <p>This method executes any of the validation steps in the PKIX path validation algorithm which
   * were not satisfied via filtering out non-compliant certificates with certificate matching
   * rules.
   *
   * <p>If the last certificate is being verified (the one whose subject matches the target subject,
   * then the steps in Section 6.1.4 of the Certification Path Validation algorithm are NOT
   * executed, regardless of whether or not the last cert is an end-entity cert or not. This allows
   * callers to certify CA certs as well as EE certs.
   *
   * @param cert the certificate to be verified
   * @param currentState the current state against which the cert is verified
   * @param certPathList the certPathList generated thus far
   */
  void verifyCert(X509Certificate cert, State currState, List certPathList)
      throws GeneralSecurityException {
    if (debug != null)
      debug.println(
          "ReverseBuilder.verifyCert(SN: "
              + Debug.toHexString(cert.getSerialNumber())
              + "\n  Subject: "
              + cert.getSubjectX500Principal()
              + ")");

    ReverseState currentState = (ReverseState) currState;

    /* we don't perform any validation of the trusted cert */
    if (currentState.isInitial()) {
      return;
    }

    /*
     * check for looping - abort a loop if
     * ((we encounter the same certificate twice) AND
     * ((policyMappingInhibited = true) OR (no policy mapping
     * extensions can be found between the occurences of the same
     * certificate)))
     * in order to facilitate the check to see if there are
     * any policy mapping extensions found between the occurences
     * of the same certificate, we reverse the certpathlist first
     */
    if ((certPathList != null) && (!certPathList.isEmpty())) {
      List reverseCertList = new ArrayList();
      Iterator iter = certPathList.iterator();
      while (iter.hasNext()) {
        reverseCertList.add(0, iter.next());
      }

      Iterator cpListIter = reverseCertList.iterator();
      boolean policyMappingFound = false;
      while (cpListIter.hasNext()) {
        X509Certificate cpListCert = (X509Certificate) cpListIter.next();
        X509CertImpl cpListCertImpl = X509CertImpl.toImpl(cpListCert);
        PolicyMappingsExtension policyMappingsExt = cpListCertImpl.getPolicyMappingsExtension();
        if (policyMappingsExt != null) {
          policyMappingFound = true;
        }
        if (debug != null) debug.println("policyMappingFound = " + policyMappingFound);
        if (cert.equals(cpListCert)) {
          if ((buildParams.isPolicyMappingInhibited()) || (!policyMappingFound)) {
            if (debug != null) debug.println("loop detected!!");
            throw new CertPathValidatorException("loop detected");
          }
        }
      }
    }

    /* check if target cert */
    boolean finalCert = cert.getSubjectX500Principal().equals(targetSubjectDN);

    /* check if CA cert */
    boolean caCert = (cert.getBasicConstraints() != -1 ? true : false);

    /* if there are more certs to follow, verify certain constraints */
    if (!finalCert) {

      /* check if CA cert */
      if (!caCert) throw new CertPathValidatorException("cert is NOT a CA cert");

      /* If the certificate was not self-issued, verify that
       * remainingCerts is greater than zero
       */
      if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) {
        throw new CertPathValidatorException("pathLenConstraint violated, path too long");
      }

      /*
       * Check keyUsage extension (only if CA cert and not final cert)
       */
      KeyChecker.verifyCAKeyUsage(cert);

    } else {

      /*
       * If final cert, check that it satisfies specified target
       * constraints
       */
      if (targetCertSelector.match(cert) == false) {
        throw new CertPathValidatorException("target certificate " + "constraints check failed");
      }
    }

    /*
     * Check revocation.
     */
    if (buildParams.isRevocationEnabled()) {

      boolean crlSign = currentState.crlChecker.check(cert, currentState.pubKey, true);

      // if this cert can't vouch for the CRL on the next cert, and
      // if this wasn't the last cert in the chain, then we can't
      // keep going from here!
      // NOTE: if we ever add indirect/idp support, this will have
      // to change...
      if ((!crlSign) && (!finalCert))
        throw new CertPathValidatorException("cert can't vouch for crl");
    }

    /* Check name constraints if this is not a self-issued cert */
    if (finalCert || !X509CertImpl.isSelfIssued(cert)) {
      if (currentState.nc != null) {
        try {
          if (!currentState.nc.verify(cert)) {
            throw new CertPathValidatorException("name constraints check failed");
          }
        } catch (IOException ioe) {
          throw new CertPathValidatorException(ioe);
        }
      }
    }

    /*
     * Check policy
     */
    X509CertImpl certImpl = X509CertImpl.toImpl(cert);
    currentState.rootNode =
        PolicyChecker.processPolicies(
            currentState.certIndex,
            initPolicies,
            currentState.explicitPolicy,
            currentState.policyMapping,
            currentState.inhibitAnyPolicy,
            buildParams.getPolicyQualifiersRejected(),
            currentState.rootNode,
            certImpl,
            finalCert);

    /*
     * Check CRITICAL private extensions
     */
    Set unresolvedCritExts = cert.getCriticalExtensionOIDs();
    if (unresolvedCritExts == null) {
      unresolvedCritExts = Collections.EMPTY_SET;
    }
    Iterator i = currentState.userCheckers.iterator();
    while (i.hasNext()) {
      PKIXCertPathChecker checker = (PKIXCertPathChecker) i.next();
      checker.check(cert, unresolvedCritExts);
    }
    /*
     * Look at the remaining extensions and remove any ones we have
     * already checked. If there are any left, throw an exception!
     */
    if (!unresolvedCritExts.isEmpty()) {
      unresolvedCritExts.remove(PKIXExtensions.BasicConstraints_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.NameConstraints_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.CertificatePolicies_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.PolicyMappings_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.PolicyConstraints_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.InhibitAnyPolicy_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.SubjectAlternativeName_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.KeyUsage_Id.toString());
      unresolvedCritExts.remove(PKIXExtensions.ExtendedKeyUsage_Id.toString());

      if (!unresolvedCritExts.isEmpty())
        throw new CertificateException("Unrecognized critical extension(s)");
    }

    /*
     * Check signature.
     */
    if (buildParams.getSigProvider() != null) {
      cert.verify(currentState.pubKey, buildParams.getSigProvider());
    } else {
      cert.verify(currentState.pubKey);
    }
  }