Exemplo n.º 1
0
 /**
  * Returns true if a custom (not com.sun.security.auth.PolicyFile) system-wide policy object has
  * been set or installed. This method is called by SubjectDomainCombiner to provide backwards
  * compatibility for developers that provide their own javax.security.auth.Policy implementations.
  *
  * @return true if a custom (not com.sun.security.auth.PolicyFile) system-wide policy object has
  *     been set; false otherwise
  */
 static boolean isCustomPolicySet(Debug debug) {
   if (policy != null) {
     if (debug != null && isCustomPolicy) {
       debug.println(
           "Providing backwards compatibility for "
               + "javax.security.auth.policy implementation: "
               + policy.toString());
     }
     return isCustomPolicy;
   }
   // check if custom policy has been set using auth.policy.provider prop
   String policyClass =
       java.security.AccessController.doPrivileged(
           new java.security.PrivilegedAction<String>() {
             public String run() {
               return Security.getProperty("auth.policy.provider");
             }
           });
   if (policyClass != null && !policyClass.equals("com.sun.security.auth.PolicyFile")) {
     if (debug != null) {
       debug.println(
           "Providing backwards compatibility for "
               + "javax.security.auth.policy implementation: "
               + policyClass);
     }
     return true;
   }
   return false;
 }
Exemplo n.º 2
0
  private PKIXCertPathBuilderResult buildCertPath(
      boolean searchAllCertStores, List<List<Vertex>> adjList) throws CertPathBuilderException {
    // Init shared variables and build certification path
    pathCompleted = false;
    trustAnchor = null;
    finalPublicKey = null;
    policyTreeResult = null;
    LinkedList<X509Certificate> certPathList = new LinkedList<>();
    try {
      buildForward(adjList, certPathList, searchAllCertStores);
    } catch (GeneralSecurityException | IOException e) {
      if (debug != null) {
        debug.println("SunCertPathBuilder.engineBuild() exception in " + "build");
        e.printStackTrace();
      }
      throw new SunCertPathBuilderException(
          "unable to find valid " + "certification path to requested target",
          e,
          new AdjacencyList(adjList));
    }

    // construct SunCertPathBuilderResult
    try {
      if (pathCompleted) {
        if (debug != null) debug.println("SunCertPathBuilder.engineBuild() " + "pathCompleted");

        // we must return a certpath which has the target
        // as the first cert in the certpath - i.e. reverse
        // the certPathList
        Collections.reverse(certPathList);

        return new SunCertPathBuilderResult(
            cf.generateCertPath(certPathList),
            trustAnchor,
            policyTreeResult,
            finalPublicKey,
            new AdjacencyList(adjList));
      }
    } catch (CertificateException e) {
      if (debug != null) {
        debug.println("SunCertPathBuilder.engineBuild() exception " + "in wrap-up");
        e.printStackTrace();
      }
      throw new SunCertPathBuilderException(
          "unable to find valid " + "certification path to requested target",
          e,
          new AdjacencyList(adjList));
    }

    return null;
  }
Exemplo n.º 3
0
 /** Get the provider object. Loads the provider if it is not already loaded. */
 synchronized Provider getProvider() {
   // volatile variable load
   Provider p = provider;
   if (p != null) {
     return p;
   }
   if (shouldLoad() == false) {
     return null;
   }
   if (isLoading) {
     // because this method is synchronized, this can only
     // happen if there is recursion.
     if (debug != null) {
       debug.println("Recursion loading provider: " + this);
       new Exception("Call trace").printStackTrace();
     }
     return null;
   }
   try {
     isLoading = true;
     tries++;
     p = doLoadProvider();
   } finally {
     isLoading = false;
   }
   provider = p;
   return p;
 }
Exemplo n.º 4
0
  /*
   * Private build forward method.
   */
  private void buildForward(
      List<List<Vertex>> adjacencyList,
      LinkedList<X509Certificate> certPathList,
      boolean searchAllCertStores)
      throws GeneralSecurityException, IOException {
    if (debug != null) {
      debug.println("SunCertPathBuilder.buildForward()...");
    }

    /* Initialize current state */
    ForwardState currentState = new ForwardState();
    currentState.initState(buildParams.certPathCheckers());

    /* Initialize adjacency list */
    adjacencyList.clear();
    adjacencyList.add(new LinkedList<Vertex>());

    currentState.untrustedChecker = new UntrustedChecker();

    depthFirstSearchForward(
        buildParams.targetSubject(),
        currentState,
        new ForwardBuilder(buildParams, searchAllCertStores),
        adjacencyList,
        certPathList);
  }
  private AccessControlContext goCombiner(
      ProtectionDomain[] current, AccessControlContext assigned) {

    // the assigned ACC's combiner is not null --
    // let the combiner do its thing

    // XXX we could add optimizations to 'current' here ...

    if (getDebug() != null) {
      debug.println("AccessControlContext invoking the Combiner");
    }

    // No need to clone current and assigned.context
    // combine() will not update them
    ProtectionDomain[] combinedPds = assigned.combiner.combine(current, assigned.context);

    // return new AccessControlContext(combinedPds, assigned.combiner);

    // Reuse existing ACC
    this.context = combinedPds;
    this.combiner = assigned.combiner;
    this.isPrivileged = false;

    return this;
  }
  /*
   * Retrieves all CA certificates which satisfy constraints
   * and requirements specified in the parameters and PKIX state.
   */
  private Collection getMatchingCACerts(ReverseState currentState)
      throws CertificateException, CertStoreException, IOException {

    /*
     * Compose a CertSelector to filter out
     * certs which do not satisfy requirements.
     */
    X509CertSelector sel = new X509CertSelector();

    /*
     * Match on issuer (subject of previous cert)
     */
    CertPathHelper.setIssuer(sel, currentState.subjectDN);

    /*
     * Match on certificate validity date.
     */
    sel.setCertificateValid(date);

    /*
     * Match on target subject name (checks that current cert's
     * name constraints permit it to certify target).
     * (4 is the integer type for DIRECTORY name).
     */
    sel.addPathToName(4, targetCertSelector.getSubjectAsBytes());

    /*
     * Policy processing optimizations
     */
    if (currentState.explicitPolicy == 0) sel.setPolicy(getMatchingPolicies());

    /*
     * If previous cert has a subject key identifier extension,
     * use it to match on authority key identifier extension.
     */
    /*if (currentState.subjKeyId != null) {
    AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
    	(KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
    			null, null);
    sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
         }*/

    /*
     * Require CA certs
     */
    sel.setBasicConstraints(0);

    /* Retrieve matching certs from CertStores */
    ArrayList reverseCerts = new ArrayList();
    addMatchingCerts(sel, buildParams.getCertStores(), reverseCerts);

    /* Sort remaining certs using name constraints */
    Collections.sort(reverseCerts, new PKIXCertComparator());

    if (debug != null)
      debug.println("ReverseBuilder.getMatchingCACerts got " + reverseCerts.size() + " certs.");
    return reverseCerts;
  }
Exemplo n.º 7
0
 /** Returns a printable representation of the CRLNumberExtension. */
 public String toString() {
   String s =
       super.toString()
           + extensionLabel
           + ": "
           + ((crlNumber == null) ? "" : Debug.toHexString(crlNumber))
           + "\n";
   return (s);
 }
 static Debug getDebug() {
   if (debugInit) return debug;
   else {
     if (Policy.isSet()) {
       debug = Debug.getInstance("access");
       debugInit = true;
     }
     return debug;
   }
 }
 /*     */ private boolean isWorthTrying(
     X509Certificate paramX509Certificate1, X509Certificate paramX509Certificate2)
       /*     */ {
   /* 220 */ boolean bool = false;
   /*     */
   /* 222 */ if (debug != null) {
     /* 223 */ debug.println(
         "PKIXCertPathValidator.isWorthTrying() checking if this trusted cert is worth trying ...");
     /*     */ }
   /*     */
   /* 227 */ if (paramX509Certificate2 == null) {
     /* 228 */ return true;
     /*     */ }
   /*     */
   /* 231 */ AdaptableX509CertSelector localAdaptableX509CertSelector =
       new AdaptableX509CertSelector();
   /*     */
   /* 235 */ localAdaptableX509CertSelector.setSubject(
       paramX509Certificate2.getIssuerX500Principal());
   /*     */
   /* 238 */ localAdaptableX509CertSelector.setValidityPeriod(
       paramX509Certificate2.getNotBefore(), paramX509Certificate2.getNotAfter());
   /*     */ try
   /*     */ {
     /* 246 */ X509CertImpl localX509CertImpl = X509CertImpl.toImpl(paramX509Certificate2);
     /* 247 */ localAdaptableX509CertSelector.parseAuthorityKeyIdentifierExtension(
         localX509CertImpl.getAuthorityKeyIdentifierExtension());
     /*     */
     /* 250 */ bool = localAdaptableX509CertSelector.match(paramX509Certificate1);
     /*     */ }
   /*     */ catch (Exception localException)
   /*     */ {
     /*     */ }
   /* 255 */ if (debug != null) {
     /* 256 */ if (bool) /* 257 */ debug.println("YES - try this trustedCert");
     /*     */ else {
       /* 259 */ debug.println("NO - don't try this trustedCert");
       /*     */ }
     /*     */ }
   /*     */
   /* 263 */ return bool;
   /*     */ }
Exemplo n.º 10
0
  /**
   * Attempts to build a certification path using the Sun build algorithm from a trusted anchor(s)
   * to a target subject, which must both be specified in the input parameter set. This method will
   * attempt to build in the forward direction: from the target to the CA.
   *
   * <p>The certification path that is constructed is validated according to the PKIX specification.
   *
   * @param params the parameter set for building a path. Must be an instance of <code>
   *     PKIXBuilderParameters</code>.
   * @return a certification path builder result.
   * @exception CertPathBuilderException Exception thrown if builder is unable to build a complete
   *     certification path from the trusted anchor(s) to the target subject.
   * @throws InvalidAlgorithmParameterException if the given parameters are inappropriate for this
   *     certification path builder.
   */
  @Override
  public CertPathBuilderResult engineBuild(CertPathParameters params)
      throws CertPathBuilderException, InvalidAlgorithmParameterException {

    if (debug != null) {
      debug.println("SunCertPathBuilder.engineBuild(" + params + ")");
    }

    buildParams = PKIX.checkBuilderParams(params);
    return build();
  }
Exemplo n.º 11
0
  public String toString() {
    String LINE_SEP = System.getProperty("line.separator");

    StringBuffer strbuf =
        new StringBuffer(
            "SunJCE Diffie-Hellman Public Key:"
                + LINE_SEP
                + "y:"
                + LINE_SEP
                + Debug.toHexString(this.y)
                + LINE_SEP
                + "p:"
                + LINE_SEP
                + Debug.toHexString(this.p)
                + LINE_SEP
                + "g:"
                + LINE_SEP
                + Debug.toHexString(this.g));
    if (this.l != 0) strbuf.append(LINE_SEP + "l:" + LINE_SEP + "    " + this.l);
    return strbuf.toString();
  }
Exemplo n.º 12
0
  /*
   * Retrieves all end-entity certificates which satisfy constraints
   * and requirements specified in the parameters and PKIX state.
   */
  private Collection getMatchingEECerts(ReverseState currentState)
      throws CertStoreException, CertificateException, IOException {

    /*
     * Compose a CertSelector to filter out
     * certs which do not satisfy requirements.
     *
     * First, retrieve clone of current target cert constraints,
     * and then add more selection criteria based on current validation state.
     */
    X509CertSelector sel = (X509CertSelector) buildParams.getTargetCertConstraints();

    /*
     * Match on issuer (subject of previous cert)
     */
    CertPathHelper.setIssuer(sel, currentState.subjectDN);

    /*
     * Match on certificate validity date.
     */
    sel.setCertificateValid(date);

    /*
     * Policy processing optimizations
     */
    if (currentState.explicitPolicy == 0) sel.setPolicy(getMatchingPolicies());

    /*
     * If previous cert has a subject key identifier extension,
     * use it to match on authority key identifier extension.
     */
    /*if (currentState.subjKeyId != null) {
    AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
    	(KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
    	null, null);
    sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
         }*/

    /*
     * Require EE certs
     */
    sel.setBasicConstraints(-2);

    /* Retrieve matching certs from CertStores */
    HashSet eeCerts = new HashSet();
    addMatchingCerts(sel, buildParams.getCertStores(), eeCerts);

    if (debug != null) {
      debug.println("ReverseBuilder.getMatchingEECerts got " + eeCerts.size() + " certs.");
    }
    return eeCerts;
  }
Exemplo n.º 13
0
  /*
   * Verify the signature of the OCSP response.
   * The responder's cert is implicitly trusted.
   */
  private boolean verifySignature(X509Certificate cert) throws CertPathValidatorException {

    try {
      Signature respSignature = Signature.getInstance(sigAlgId.getName());
      respSignature.initVerify(cert.getPublicKey());
      respSignature.update(tbsResponseData);

      if (respSignature.verify(signature)) {
        if (debug != null) {
          debug.println("Verified signature of OCSP Response");
        }
        return true;

      } else {
        if (debug != null) {
          debug.println("Error verifying signature of OCSP Response");
        }
        return false;
      }
    } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) {
      throw new CertPathValidatorException(e);
    }
  }
Exemplo n.º 14
0
  /**
   * Retrieves all certs from a CertStore which satisfy the requirements specified in the parameters
   * and the current PKIX state (name constraints, policy constraints, etc).
   *
   * @param currentState the current state. Must be an instance of <code>ReverseState</code>
   */
  Collection getMatchingCerts(State currState)
      throws CertStoreException, CertificateException, IOException {
    ReverseState currentState = (ReverseState) currState;

    if (debug != null) debug.println("In ReverseBuilder.getMatchingCerts.");

    /*
     * The last certificate could be an EE or a CA certificate
     * (we may be building a partial certification path or
     * establishing trust in a CA).
     *
     * Try the EE certs before the CA certs. It will be more
     * common to build a path to an end entity.
     */
    Collection certs = getMatchingEECerts(currentState);
    certs.addAll(getMatchingCACerts(currentState));

    return certs;
  }
Exemplo n.º 15
0
  /*
   * This inner class compares 2 PKIX certificates according to which
   * should be tried first when building a path to the target. For
   * now, the algorithm is to look at name constraints in each cert and those
   * which constrain the path closer to the target should be
   * ranked higher. Later, we may want to consider other components,
   * such as key identifiers.
   */
  class PKIXCertComparator implements Comparator {

    private Debug debug = Debug.getInstance("certpath");

    public int compare(Object o1, Object o2) {

      X509Certificate cert1 = (X509Certificate) o1;
      X509Certificate cert2 = (X509Certificate) o2;

      /*
       * if either cert certifies the target, always
       * put at head of list.
       */
      if (cert1.getSubjectX500Principal().equals(targetSubjectDN)) {
        return -1;
      }
      if (cert2.getSubjectX500Principal().equals(targetSubjectDN)) {
        return 1;
      }

      int targetDist1;
      int targetDist2;
      try {
        X500Name targetSubjectName = X500Name.asX500Name(targetSubjectDN);
        targetDist1 = Builder.targetDistance(null, cert1, targetSubjectName);
        targetDist2 = Builder.targetDistance(null, cert2, targetSubjectName);
      } catch (IOException e) {
        if (debug != null) {
          debug.println("IOException in call to Builder.targetDistance");
          e.printStackTrace();
        }
        throw new ClassCastException("Invalid target subject distinguished name");
      }

      if (targetDist1 == targetDist2) return 0;

      if (targetDist1 == -1) return 1;

      if (targetDist1 < targetDist2) return -1;

      return 1;
    }
  }
Exemplo n.º 16
0
 private PKIXCertPathBuilderResult build() throws CertPathBuilderException {
   List<List<Vertex>> adjList = new ArrayList<>();
   PKIXCertPathBuilderResult result = buildCertPath(false, adjList);
   if (result == null) {
     if (debug != null) {
       debug.println(
           "SunCertPathBuilder.engineBuild: 2nd pass; "
               + "try building again searching all certstores");
     }
     // try again
     adjList.clear();
     result = buildCertPath(true, adjList);
     if (result == null) {
       throw new SunCertPathBuilderException(
           "unable to find valid " + "certification path to requested target",
           new AdjacencyList(adjList));
     }
   }
   return result;
 }
  /**
   * Determines whether the access request indicated by the specified permission should be allowed
   * or denied, based on the security policy currently in effect, and the context in this object.
   * The request is allowed only if every ProtectionDomain in the context implies the permission.
   * Otherwise the request is denied.
   *
   * <p>This method quietly returns if the access request is permitted, or throws a suitable
   * AccessControlException otherwise.
   *
   * @param perm the requested permission.
   * @exception AccessControlException if the specified permission is not permitted, based on the
   *     current security policy and the context encapsulated by this object.
   * @exception NullPointerException if the permission to check for is null.
   */
  public void checkPermission(Permission perm) throws AccessControlException {
    boolean dumpDebug = false;

    if (perm == null) {
      throw new NullPointerException("permission can't be null");
    }
    if (getDebug() != null) {
      // If "codebase" is not specified, we dump the info by default.
      dumpDebug = !Debug.isOn("codebase=");
      if (!dumpDebug) {
        // If "codebase" is specified, only dump if the specified code
        // value is in the stack.
        for (int i = 0; context != null && i < context.length; i++) {
          if (context[i].getCodeSource() != null
              && context[i].getCodeSource().getLocation() != null
              && Debug.isOn("codebase=" + context[i].getCodeSource().getLocation().toString())) {
            dumpDebug = true;
            break;
          }
        }
      }

      dumpDebug &=
          !Debug.isOn("permission=")
              || Debug.isOn("permission=" + perm.getClass().getCanonicalName());

      if (dumpDebug && Debug.isOn("stack")) {
        Thread.currentThread().dumpStack();
      }

      if (dumpDebug && Debug.isOn("domain")) {
        if (context == null) {
          debug.println("domain (context is null)");
        } else {
          for (int i = 0; i < context.length; i++) {
            debug.println("domain " + i + " " + context[i]);
          }
        }
      }
    }

    /*
     * iterate through the ProtectionDomains in the context.
     * Stop at the first one that doesn't allow the
     * requested permission (throwing an exception).
     *
     */

    /* if ctxt is null, all we had on the stack were system domains,
    or the first domain was a Privileged system domain. This
    is to make the common case for system code very fast */

    if (context == null) return;

    for (int i = 0; i < context.length; i++) {
      if (context[i] != null && !context[i].implies(perm)) {
        if (dumpDebug) {
          debug.println("access denied " + perm);
        }

        if (Debug.isOn("failure") && debug != null) {
          // Want to make sure this is always displayed for failure,
          // but do not want to display again if already displayed
          // above.
          if (!dumpDebug) {
            debug.println("access denied " + perm);
          }
          Thread.currentThread().dumpStack();
          final ProtectionDomain pd = context[i];
          final Debug db = debug;
          AccessController.doPrivileged(
              new PrivilegedAction<Void>() {
                public Void run() {
                  db.println("domain that failed " + pd);
                  return null;
                }
              });
        }
        throw new AccessControlException("access denied " + perm, perm);
      }
    }

    // allow if all of them allowed access
    if (dumpDebug) {
      debug.println("access allowed " + perm);
    }

    return;
  }
Exemplo n.º 18
0
  private void invoke(String methodName) throws LoginException {

    // start at moduleIndex
    // - this can only be non-zero if methodName is LOGIN_METHOD

    for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
      try {

        int mIndex = 0;
        Method[] methods = null;

        if (moduleStack[i].module != null) {
          methods = moduleStack[i].module.getClass().getMethods();
        } else {

          // instantiate the LoginModule
          //
          // Allow any object to be a LoginModule as long as it
          // conforms to the interface.
          Class<?> c =
              Class.forName(moduleStack[i].entry.getLoginModuleName(), true, contextClassLoader);

          Constructor<?> constructor = c.getConstructor(PARAMS);
          Object[] args = {};
          moduleStack[i].module = constructor.newInstance(args);

          // call the LoginModule's initialize method
          methods = moduleStack[i].module.getClass().getMethods();
          for (mIndex = 0; mIndex < methods.length; mIndex++) {
            if (methods[mIndex].getName().equals(INIT_METHOD)) {
              break;
            }
          }

          Object[] initArgs = {subject, callbackHandler, state, moduleStack[i].entry.getOptions()};
          // invoke the LoginModule initialize method
          //
          // Throws ArrayIndexOutOfBoundsException if no such
          // method defined.  May improve to use LoginException in
          // the future.
          methods[mIndex].invoke(moduleStack[i].module, initArgs);
        }

        // find the requested method in the LoginModule
        for (mIndex = 0; mIndex < methods.length; mIndex++) {
          if (methods[mIndex].getName().equals(methodName)) {
            break;
          }
        }

        // set up the arguments to be passed to the LoginModule method
        Object[] args = {};

        // invoke the LoginModule method
        //
        // Throws ArrayIndexOutOfBoundsException if no such
        // method defined.  May improve to use LoginException in
        // the future.
        boolean status =
            ((Boolean) methods[mIndex].invoke(moduleStack[i].module, args)).booleanValue();

        if (status == true) {

          // if SUFFICIENT, return if no prior REQUIRED errors
          if (!methodName.equals(ABORT_METHOD)
              && !methodName.equals(LOGOUT_METHOD)
              && moduleStack[i].entry.getControlFlag()
                  == AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT
              && firstRequiredError == null) {

            // clear state
            clearState();

            if (debug != null) debug.println(methodName + " SUFFICIENT success");
            return;
          }

          if (debug != null) debug.println(methodName + " success");
          success = true;
        } else {
          if (debug != null) debug.println(methodName + " ignored");
        }

      } catch (NoSuchMethodException nsme) {
        MessageFormat form =
            new MessageFormat(
                ResourcesMgr.getString(
                    "unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor"));
        Object[] source = {moduleStack[i].entry.getLoginModuleName()};
        throwException(null, new LoginException(form.format(source)));
      } catch (InstantiationException ie) {
        throwException(
            null,
            new LoginException(
                ResourcesMgr.getString("unable.to.instantiate.LoginModule.") + ie.getMessage()));
      } catch (ClassNotFoundException cnfe) {
        throwException(
            null,
            new LoginException(
                ResourcesMgr.getString("unable.to.find.LoginModule.class.") + cnfe.getMessage()));
      } catch (IllegalAccessException iae) {
        throwException(
            null,
            new LoginException(
                ResourcesMgr.getString("unable.to.access.LoginModule.") + iae.getMessage()));
      } catch (InvocationTargetException ite) {

        // failure cases

        LoginException le;

        if (ite.getCause() instanceof PendingException && methodName.equals(LOGIN_METHOD)) {

          // XXX
          //
          // if a module's LOGIN_METHOD threw a PendingException
          // then immediately throw it.
          //
          // when LoginContext is called again,
          // the module that threw the exception is invoked first
          // (the module list is not invoked from the start).
          // previously thrown exception state is still present.
          //
          // it is assumed that the module which threw
          // the exception can have its
          // LOGIN_METHOD invoked twice in a row
          // without any commit/abort in between.
          //
          // in all cases when LoginContext returns
          // (either via natural return or by throwing an exception)
          // we need to call clearState before returning.
          // the only time that is not true is in this case -
          // do not call throwException here.

          throw (PendingException) ite.getCause();

        } else if (ite.getCause() instanceof LoginException) {

          le = (LoginException) ite.getCause();

        } else if (ite.getCause() instanceof SecurityException) {

          // do not want privacy leak
          // (e.g., sensitive file path in exception msg)

          le = new LoginException("Security Exception");
          le.initCause(new SecurityException());
          if (debug != null) {
            debug.println(
                "original security exception with detail msg "
                    + "replaced by new exception with empty detail msg");
            debug.println("original security exception: " + ite.getCause().toString());
          }
        } else {

          // capture an unexpected LoginModule exception
          java.io.StringWriter sw = new java.io.StringWriter();
          ite.getCause().printStackTrace(new java.io.PrintWriter(sw));
          sw.flush();
          le = new LoginException(sw.toString());
        }

        if (moduleStack[i].entry.getControlFlag()
            == AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {

          if (debug != null) debug.println(methodName + " REQUISITE failure");

          // if REQUISITE, then immediately throw an exception
          if (methodName.equals(ABORT_METHOD) || methodName.equals(LOGOUT_METHOD)) {
            if (firstRequiredError == null) firstRequiredError = le;
          } else {
            throwException(firstRequiredError, le);
          }

        } else if (moduleStack[i].entry.getControlFlag()
            == AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {

          if (debug != null) debug.println(methodName + " REQUIRED failure");

          // mark down that a REQUIRED module failed
          if (firstRequiredError == null) firstRequiredError = le;

        } else {

          if (debug != null) debug.println(methodName + " OPTIONAL failure");

          // mark down that an OPTIONAL module failed
          if (firstError == null) firstError = le;
        }
      }
    }

    // we went thru all the LoginModules.
    if (firstRequiredError != null) {
      // a REQUIRED module failed -- return the error
      throwException(firstRequiredError, null);
    } else if (success == false && firstError != null) {
      // no module succeeded -- return the first error
      throwException(firstError, null);
    } else if (success == false) {
      // no module succeeded -- all modules were IGNORED
      throwException(
          new LoginException(ResourcesMgr.getString("Login.Failure.all.modules.ignored")), null);
    } else {
      // success

      clearState();
      return;
    }
  }
Exemplo n.º 19
0
/**
 * The {@code LoginContext} class describes the basic methods used to authenticate Subjects and
 * provides a way to develop an application independent of the underlying authentication technology.
 * A {@code Configuration} specifies the authentication technology, or {@code LoginModule}, to be
 * used with a particular application. Different LoginModules can be plugged in under an application
 * without requiring any modifications to the application itself.
 *
 * <p>In addition to supporting <i>pluggable</i> authentication, this class also supports the notion
 * of <i>stacked</i> authentication. Applications may be configured to use more than one
 * LoginModule. For example, one could configure both a Kerberos LoginModule and a smart card
 * LoginModule under an application.
 *
 * <p>A typical caller instantiates a LoginContext with a <i>name</i> and a {@code CallbackHandler}.
 * LoginContext uses the <i>name</i> as the index into a Configuration to determine which
 * LoginModules should be used, and which ones must succeed in order for the overall authentication
 * to succeed. The {@code CallbackHandler} is passed to the underlying LoginModules so they may
 * communicate and interact with users (prompting for a username and password via a graphical user
 * interface, for example).
 *
 * <p>Once the caller has instantiated a LoginContext, it invokes the {@code login} method to
 * authenticate a {@code Subject}. The {@code login} method invokes the configured modules to
 * perform their respective types of authentication (username/password, smart card pin verification,
 * etc.). Note that the LoginModules will not attempt authentication retries nor introduce delays if
 * the authentication fails. Such tasks belong to the LoginContext caller.
 *
 * <p>If the {@code login} method returns without throwing an exception, then the overall
 * authentication succeeded. The caller can then retrieve the newly authenticated Subject by
 * invoking the {@code getSubject} method. Principals and Credentials associated with the Subject
 * may be retrieved by invoking the Subject's respective {@code getPrincipals}, {@code
 * getPublicCredentials}, and {@code getPrivateCredentials} methods.
 *
 * <p>To logout the Subject, the caller calls the {@code logout} method. As with the {@code login}
 * method, this {@code logout} method invokes the {@code logout} method for the configured modules.
 *
 * <p>A LoginContext should not be used to authenticate more than one Subject. A separate
 * LoginContext should be used to authenticate each different Subject.
 *
 * <p>The following documentation applies to all LoginContext constructors:
 *
 * <ol>
 *   <li>{@code Subject}
 *       <ul>
 *         <li>If the constructor has a Subject input parameter, the LoginContext uses the
 *             caller-specified Subject object.
 *         <li>If the caller specifies a {@code null} Subject and a {@code null} value is permitted,
 *             the LoginContext instantiates a new Subject.
 *         <li>If the constructor does <b>not</b> have a Subject input parameter, the LoginContext
 *             instantiates a new Subject.
 *             <p>
 *       </ul>
 *   <li>{@code Configuration}
 *       <ul>
 *         <li>If the constructor has a Configuration input parameter and the caller specifies a
 *             non-null Configuration, the LoginContext uses the caller-specified Configuration.
 *             <p>If the constructor does <b>not</b> have a Configuration input parameter, or if the
 *             caller specifies a {@code null} Configuration object, the constructor uses the
 *             following call to get the installed Configuration:
 *             <pre>
 *      config = Configuration.getConfiguration();
 * </pre>
 *             For both cases, the <i>name</i> argument given to the constructor is passed to the
 *             {@code Configuration.getAppConfigurationEntry} method. If the Configuration has no
 *             entries for the specified <i>name</i>, then the {@code LoginContext} calls {@code
 *             getAppConfigurationEntry} with the name, "<i>other</i>" (the default entry name). If
 *             there is no entry for "<i>other</i>", then a {@code LoginException} is thrown.
 *         <li>When LoginContext uses the installed Configuration, the caller requires the
 *             createLoginContext.<em>name</em> and possibly createLoginContext.other
 *             AuthPermissions. Furthermore, the LoginContext will invoke configured modules from
 *             within an {@code AccessController.doPrivileged} call so that modules that perform
 *             security-sensitive tasks (such as connecting to remote hosts, and updating the
 *             Subject) will require the respective permissions, but the callers of the LoginContext
 *             will not require those permissions.
 *         <li>When LoginContext uses a caller-specified Configuration, the caller does not require
 *             any createLoginContext AuthPermission. The LoginContext saves the {@code
 *             AccessControlContext} for the caller, and invokes the configured modules from within
 *             an {@code AccessController.doPrivileged} call constrained by that context. This means
 *             the caller context (stored when the LoginContext was created) must have sufficient
 *             permissions to perform any security-sensitive tasks that the modules may perform.
 *             <p>
 *       </ul>
 *   <li>{@code CallbackHandler}
 *       <ul>
 *         <li>If the constructor has a CallbackHandler input parameter, the LoginContext uses the
 *             caller-specified CallbackHandler object.
 *         <li>If the constructor does <b>not</b> have a CallbackHandler input parameter, or if the
 *             caller specifies a {@code null} CallbackHandler object (and a {@code null} value is
 *             permitted), the LoginContext queries the {@code auth.login.defaultCallbackHandler}
 *             security property for the fully qualified class name of a default handler
 *             implementation. If the security property is not set, then the underlying modules will
 *             not have a CallbackHandler for use in communicating with users. The caller thus
 *             assumes that the configured modules have alternative means for authenticating the
 *             user.
 *         <li>When the LoginContext uses the installed Configuration (instead of a caller-specified
 *             Configuration, see above), then this LoginContext must wrap any caller-specified or
 *             default CallbackHandler implementation in a new CallbackHandler implementation whose
 *             {@code handle} method implementation invokes the specified CallbackHandler's {@code
 *             handle} method in a {@code java.security.AccessController.doPrivileged} call
 *             constrained by the caller's current {@code AccessControlContext}.
 *       </ul>
 * </ol>
 *
 * @see java.security.Security
 * @see javax.security.auth.AuthPermission
 * @see javax.security.auth.Subject
 * @see javax.security.auth.callback.CallbackHandler
 * @see javax.security.auth.login.Configuration
 * @see javax.security.auth.spi.LoginModule
 * @see java.security.Security security properties
 */
public class LoginContext {

  private static final String INIT_METHOD = "initialize";
  private static final String LOGIN_METHOD = "login";
  private static final String COMMIT_METHOD = "commit";
  private static final String ABORT_METHOD = "abort";
  private static final String LOGOUT_METHOD = "logout";
  private static final String OTHER = "other";
  private static final String DEFAULT_HANDLER = "auth.login.defaultCallbackHandler";
  private Subject subject = null;
  private boolean subjectProvided = false;
  private boolean loginSucceeded = false;
  private CallbackHandler callbackHandler;
  private Map<String, ?> state = new HashMap<String, Object>();

  private Configuration config;
  private AccessControlContext creatorAcc = null; // customized config only
  private ModuleInfo[] moduleStack;
  private ClassLoader contextClassLoader = null;
  private static final Class<?>[] PARAMS = {};

  // state saved in the event a user-specified asynchronous exception
  // was specified and thrown

  private int moduleIndex = 0;
  private LoginException firstError = null;
  private LoginException firstRequiredError = null;
  private boolean success = false;

  private static final sun.security.util.Debug debug =
      sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");

  private void init(String name) throws LoginException {

    SecurityManager sm = System.getSecurityManager();
    if (sm != null && creatorAcc == null) {
      sm.checkPermission(new AuthPermission("createLoginContext." + name));
    }

    if (name == null) throw new LoginException(ResourcesMgr.getString("Invalid.null.input.name"));

    // get the Configuration
    if (config == null) {
      config =
          java.security.AccessController.doPrivileged(
              new java.security.PrivilegedAction<Configuration>() {
                public Configuration run() {
                  return Configuration.getConfiguration();
                }
              });
    }

    // get the LoginModules configured for this application
    AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
    if (entries == null) {

      if (sm != null && creatorAcc == null) {
        sm.checkPermission(new AuthPermission("createLoginContext." + OTHER));
      }

      entries = config.getAppConfigurationEntry(OTHER);
      if (entries == null) {
        MessageFormat form =
            new MessageFormat(ResourcesMgr.getString("No.LoginModules.configured.for.name"));
        Object[] source = {name};
        throw new LoginException(form.format(source));
      }
    }
    moduleStack = new ModuleInfo[entries.length];
    for (int i = 0; i < entries.length; i++) {
      // clone returned array
      moduleStack[i] =
          new ModuleInfo(
              new AppConfigurationEntry(
                  entries[i].getLoginModuleName(),
                  entries[i].getControlFlag(),
                  entries[i].getOptions()),
              null);
    }

    contextClassLoader =
        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<ClassLoader>() {
              public ClassLoader run() {
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                if (loader == null) {
                  // Don't use bootstrap class loader directly to ensure
                  // proper package access control!
                  loader = ClassLoader.getSystemClassLoader();
                }

                return loader;
              }
            });
  }

  private void loadDefaultCallbackHandler() throws LoginException {

    // get the default handler class
    try {

      final ClassLoader finalLoader = contextClassLoader;

      this.callbackHandler =
          java.security.AccessController.doPrivileged(
              new java.security.PrivilegedExceptionAction<CallbackHandler>() {
                public CallbackHandler run() throws Exception {
                  String defaultHandler = java.security.Security.getProperty(DEFAULT_HANDLER);
                  if (defaultHandler == null || defaultHandler.length() == 0) return null;
                  Class<? extends CallbackHandler> c =
                      Class.forName(defaultHandler, true, finalLoader)
                          .asSubclass(CallbackHandler.class);
                  return c.newInstance();
                }
              });
    } catch (java.security.PrivilegedActionException pae) {
      throw new LoginException(pae.getException().toString());
    }

    // secure it with the caller's ACC
    if (this.callbackHandler != null && creatorAcc == null) {
      this.callbackHandler =
          new SecureCallbackHandler(
              java.security.AccessController.getContext(), this.callbackHandler);
    }
  }

  /**
   * Instantiate a new {@code LoginContext} object with a name.
   *
   * @param name the name used as the index into the {@code Configuration}.
   * @exception LoginException if the caller-specified {@code name} does not appear in the {@code
   *     Configuration} and there is no {@code Configuration} entry for "<i>other</i>", or if the
   *     <i>auth.login.defaultCallbackHandler</i> security property was set, but the implementation
   *     class could not be loaded.
   *     <p>
   * @exception SecurityException if a SecurityManager is set and the caller does not have
   *     AuthPermission("createLoginContext.<i>name</i>"), or if a configuration entry for
   *     <i>name</i> does not exist and the caller does not additionally have
   *     AuthPermission("createLoginContext.other")
   */
  public LoginContext(String name) throws LoginException {
    init(name);
    loadDefaultCallbackHandler();
  }

  /**
   * Instantiate a new {@code LoginContext} object with a name and a {@code Subject} object.
   *
   * <p>
   *
   * @param name the name used as the index into the {@code Configuration}.
   *     <p>
   * @param subject the {@code Subject} to authenticate.
   * @exception LoginException if the caller-specified {@code name} does not appear in the {@code
   *     Configuration} and there is no {@code Configuration} entry for "<i>other</i>", if the
   *     caller-specified {@code subject} is {@code null}, or if the
   *     <i>auth.login.defaultCallbackHandler</i> security property was set, but the implementation
   *     class could not be loaded.
   *     <p>
   * @exception SecurityException if a SecurityManager is set and the caller does not have
   *     AuthPermission("createLoginContext.<i>name</i>"), or if a configuration entry for
   *     <i>name</i> does not exist and the caller does not additionally have
   *     AuthPermission("createLoginContext.other")
   */
  public LoginContext(String name, Subject subject) throws LoginException {
    init(name);
    if (subject == null)
      throw new LoginException(ResourcesMgr.getString("invalid.null.Subject.provided"));
    this.subject = subject;
    subjectProvided = true;
    loadDefaultCallbackHandler();
  }

  /**
   * Instantiate a new {@code LoginContext} object with a name and a {@code CallbackHandler} object.
   *
   * <p>
   *
   * @param name the name used as the index into the {@code Configuration}.
   *     <p>
   * @param callbackHandler the {@code CallbackHandler} object used by LoginModules to communicate
   *     with the user.
   * @exception LoginException if the caller-specified {@code name} does not appear in the {@code
   *     Configuration} and there is no {@code Configuration} entry for "<i>other</i>", or if the
   *     caller-specified {@code callbackHandler} is {@code null}.
   *     <p>
   * @exception SecurityException if a SecurityManager is set and the caller does not have
   *     AuthPermission("createLoginContext.<i>name</i>"), or if a configuration entry for
   *     <i>name</i> does not exist and the caller does not additionally have
   *     AuthPermission("createLoginContext.other")
   */
  public LoginContext(String name, CallbackHandler callbackHandler) throws LoginException {
    init(name);
    if (callbackHandler == null)
      throw new LoginException(ResourcesMgr.getString("invalid.null.CallbackHandler.provided"));
    this.callbackHandler =
        new SecureCallbackHandler(java.security.AccessController.getContext(), callbackHandler);
  }

  /**
   * Instantiate a new {@code LoginContext} object with a name, a {@code Subject} to be
   * authenticated, and a {@code CallbackHandler} object.
   *
   * <p>
   *
   * @param name the name used as the index into the {@code Configuration}.
   *     <p>
   * @param subject the {@code Subject} to authenticate.
   *     <p>
   * @param callbackHandler the {@code CallbackHandler} object used by LoginModules to communicate
   *     with the user.
   * @exception LoginException if the caller-specified {@code name} does not appear in the {@code
   *     Configuration} and there is no {@code Configuration} entry for "<i>other</i>", or if the
   *     caller-specified {@code subject} is {@code null}, or if the caller-specified {@code
   *     callbackHandler} is {@code null}.
   *     <p>
   * @exception SecurityException if a SecurityManager is set and the caller does not have
   *     AuthPermission("createLoginContext.<i>name</i>"), or if a configuration entry for
   *     <i>name</i> does not exist and the caller does not additionally have
   *     AuthPermission("createLoginContext.other")
   */
  public LoginContext(String name, Subject subject, CallbackHandler callbackHandler)
      throws LoginException {
    this(name, subject);
    if (callbackHandler == null)
      throw new LoginException(ResourcesMgr.getString("invalid.null.CallbackHandler.provided"));
    this.callbackHandler =
        new SecureCallbackHandler(java.security.AccessController.getContext(), callbackHandler);
  }

  /**
   * Instantiate a new {@code LoginContext} object with a name, a {@code Subject} to be
   * authenticated, a {@code CallbackHandler} object, and a login {@code Configuration}.
   *
   * <p>
   *
   * @param name the name used as the index into the caller-specified {@code Configuration}.
   *     <p>
   * @param subject the {@code Subject} to authenticate, or {@code null}.
   *     <p>
   * @param callbackHandler the {@code CallbackHandler} object used by LoginModules to communicate
   *     with the user, or {@code null}.
   *     <p>
   * @param config the {@code Configuration} that lists the login modules to be called to perform
   *     the authentication, or {@code null}.
   * @exception LoginException if the caller-specified {@code name} does not appear in the {@code
   *     Configuration} and there is no {@code Configuration} entry for "<i>other</i>".
   *     <p>
   * @exception SecurityException if a SecurityManager is set, <i>config</i> is {@code null}, and
   *     either the caller does not have AuthPermission("createLoginContext.<i>name</i>"), or if a
   *     configuration entry for <i>name</i> does not exist and the caller does not additionally
   *     have AuthPermission("createLoginContext.other")
   * @since 1.5
   */
  public LoginContext(
      String name, Subject subject, CallbackHandler callbackHandler, Configuration config)
      throws LoginException {
    this.config = config;
    if (config != null) {
      creatorAcc = java.security.AccessController.getContext();
    }

    init(name);
    if (subject != null) {
      this.subject = subject;
      subjectProvided = true;
    }
    if (callbackHandler == null) {
      loadDefaultCallbackHandler();
    } else if (creatorAcc == null) {
      this.callbackHandler =
          new SecureCallbackHandler(java.security.AccessController.getContext(), callbackHandler);
    } else {
      this.callbackHandler = callbackHandler;
    }
  }

  /**
   * Perform the authentication.
   *
   * <p>This method invokes the {@code login} method for each LoginModule configured for the
   * <i>name</i> specified to the {@code LoginContext} constructor, as determined by the login
   * {@code Configuration}. Each {@code LoginModule} then performs its respective type of
   * authentication (username/password, smart card pin verification, etc.).
   *
   * <p>This method completes a 2-phase authentication process by calling each configured
   * LoginModule's {@code commit} method if the overall authentication succeeded (the relevant
   * REQUIRED, REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded), or by calling each
   * configured LoginModule's {@code abort} method if the overall authentication failed. If
   * authentication succeeded, each successful LoginModule's {@code commit} method associates the
   * relevant Principals and Credentials with the {@code Subject}. If authentication failed, each
   * LoginModule's {@code abort} method removes/destroys any previously stored state.
   *
   * <p>If the {@code commit} phase of the authentication process fails, then the overall
   * authentication fails and this method invokes the {@code abort} method for each configured
   * {@code LoginModule}.
   *
   * <p>If the {@code abort} phase fails for any reason, then this method propagates the original
   * exception thrown either during the {@code login} phase or the {@code commit} phase. In either
   * case, the overall authentication fails.
   *
   * <p>In the case where multiple LoginModules fail, this method propagates the exception raised by
   * the first {@code LoginModule} which failed.
   *
   * <p>Note that if this method enters the {@code abort} phase (either the {@code login} or {@code
   * commit} phase failed), this method invokes all LoginModules configured for the application
   * regardless of their respective {@code Configuration} flag parameters. Essentially this means
   * that {@code Requisite} and {@code Sufficient} semantics are ignored during the {@code abort}
   * phase. This guarantees that proper cleanup and state restoration can take place.
   *
   * <p>
   *
   * @exception LoginException if the authentication fails.
   */
  public void login() throws LoginException {

    loginSucceeded = false;

    if (subject == null) {
      subject = new Subject();
    }

    try {
      // module invoked in doPrivileged
      invokePriv(LOGIN_METHOD);
      invokePriv(COMMIT_METHOD);
      loginSucceeded = true;
    } catch (LoginException le) {
      try {
        invokePriv(ABORT_METHOD);
      } catch (LoginException le2) {
        throw le;
      }
      throw le;
    }
  }

  /**
   * Logout the {@code Subject}.
   *
   * <p>This method invokes the {@code logout} method for each {@code LoginModule} configured for
   * this {@code LoginContext}. Each {@code LoginModule} performs its respective logout procedure
   * which may include removing/destroying {@code Principal} and {@code Credential} information from
   * the {@code Subject} and state cleanup.
   *
   * <p>Note that this method invokes all LoginModules configured for the application regardless of
   * their respective {@code Configuration} flag parameters. Essentially this means that {@code
   * Requisite} and {@code Sufficient} semantics are ignored for this method. This guarantees that
   * proper cleanup and state restoration can take place.
   *
   * <p>
   *
   * @exception LoginException if the logout fails.
   */
  public void logout() throws LoginException {
    if (subject == null) {
      throw new LoginException(ResourcesMgr.getString("null.subject.logout.called.before.login"));
    }

    // module invoked in doPrivileged
    invokePriv(LOGOUT_METHOD);
  }

  /**
   * Return the authenticated Subject.
   *
   * <p>
   *
   * @return the authenticated Subject. If the caller specified a Subject to this LoginContext's
   *     constructor, this method returns the caller-specified Subject. If a Subject was not
   *     specified and authentication succeeds, this method returns the Subject instantiated and
   *     used for authentication by this LoginContext. If a Subject was not specified, and
   *     authentication fails or has not been attempted, this method returns null.
   */
  public Subject getSubject() {
    if (!loginSucceeded && !subjectProvided) return null;
    return subject;
  }

  private void clearState() {
    moduleIndex = 0;
    firstError = null;
    firstRequiredError = null;
    success = false;
  }

  private void throwException(LoginException originalError, LoginException le)
      throws LoginException {

    // first clear state
    clearState();

    // throw the exception
    LoginException error = (originalError != null) ? originalError : le;
    throw error;
  }

  /**
   * Invokes the login, commit, and logout methods from a LoginModule inside a doPrivileged block
   * restricted by creatorAcc (may be null).
   *
   * <p>This version is called if the caller did not instantiate the LoginContext with a
   * Configuration object.
   */
  private void invokePriv(final String methodName) throws LoginException {
    try {
      java.security.AccessController.doPrivileged(
          new java.security.PrivilegedExceptionAction<Void>() {
            public Void run() throws LoginException {
              invoke(methodName);
              return null;
            }
          },
          creatorAcc);
    } catch (java.security.PrivilegedActionException pae) {
      throw (LoginException) pae.getException();
    }
  }

  private void invoke(String methodName) throws LoginException {

    // start at moduleIndex
    // - this can only be non-zero if methodName is LOGIN_METHOD

    for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
      try {

        int mIndex = 0;
        Method[] methods = null;

        if (moduleStack[i].module != null) {
          methods = moduleStack[i].module.getClass().getMethods();
        } else {

          // instantiate the LoginModule
          //
          // Allow any object to be a LoginModule as long as it
          // conforms to the interface.
          Class<?> c =
              Class.forName(moduleStack[i].entry.getLoginModuleName(), true, contextClassLoader);

          Constructor<?> constructor = c.getConstructor(PARAMS);
          Object[] args = {};
          moduleStack[i].module = constructor.newInstance(args);

          // call the LoginModule's initialize method
          methods = moduleStack[i].module.getClass().getMethods();
          for (mIndex = 0; mIndex < methods.length; mIndex++) {
            if (methods[mIndex].getName().equals(INIT_METHOD)) {
              break;
            }
          }

          Object[] initArgs = {subject, callbackHandler, state, moduleStack[i].entry.getOptions()};
          // invoke the LoginModule initialize method
          //
          // Throws ArrayIndexOutOfBoundsException if no such
          // method defined.  May improve to use LoginException in
          // the future.
          methods[mIndex].invoke(moduleStack[i].module, initArgs);
        }

        // find the requested method in the LoginModule
        for (mIndex = 0; mIndex < methods.length; mIndex++) {
          if (methods[mIndex].getName().equals(methodName)) {
            break;
          }
        }

        // set up the arguments to be passed to the LoginModule method
        Object[] args = {};

        // invoke the LoginModule method
        //
        // Throws ArrayIndexOutOfBoundsException if no such
        // method defined.  May improve to use LoginException in
        // the future.
        boolean status =
            ((Boolean) methods[mIndex].invoke(moduleStack[i].module, args)).booleanValue();

        if (status == true) {

          // if SUFFICIENT, return if no prior REQUIRED errors
          if (!methodName.equals(ABORT_METHOD)
              && !methodName.equals(LOGOUT_METHOD)
              && moduleStack[i].entry.getControlFlag()
                  == AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT
              && firstRequiredError == null) {

            // clear state
            clearState();

            if (debug != null) debug.println(methodName + " SUFFICIENT success");
            return;
          }

          if (debug != null) debug.println(methodName + " success");
          success = true;
        } else {
          if (debug != null) debug.println(methodName + " ignored");
        }

      } catch (NoSuchMethodException nsme) {
        MessageFormat form =
            new MessageFormat(
                ResourcesMgr.getString(
                    "unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor"));
        Object[] source = {moduleStack[i].entry.getLoginModuleName()};
        throwException(null, new LoginException(form.format(source)));
      } catch (InstantiationException ie) {
        throwException(
            null,
            new LoginException(
                ResourcesMgr.getString("unable.to.instantiate.LoginModule.") + ie.getMessage()));
      } catch (ClassNotFoundException cnfe) {
        throwException(
            null,
            new LoginException(
                ResourcesMgr.getString("unable.to.find.LoginModule.class.") + cnfe.getMessage()));
      } catch (IllegalAccessException iae) {
        throwException(
            null,
            new LoginException(
                ResourcesMgr.getString("unable.to.access.LoginModule.") + iae.getMessage()));
      } catch (InvocationTargetException ite) {

        // failure cases

        LoginException le;

        if (ite.getCause() instanceof PendingException && methodName.equals(LOGIN_METHOD)) {

          // XXX
          //
          // if a module's LOGIN_METHOD threw a PendingException
          // then immediately throw it.
          //
          // when LoginContext is called again,
          // the module that threw the exception is invoked first
          // (the module list is not invoked from the start).
          // previously thrown exception state is still present.
          //
          // it is assumed that the module which threw
          // the exception can have its
          // LOGIN_METHOD invoked twice in a row
          // without any commit/abort in between.
          //
          // in all cases when LoginContext returns
          // (either via natural return or by throwing an exception)
          // we need to call clearState before returning.
          // the only time that is not true is in this case -
          // do not call throwException here.

          throw (PendingException) ite.getCause();

        } else if (ite.getCause() instanceof LoginException) {

          le = (LoginException) ite.getCause();

        } else if (ite.getCause() instanceof SecurityException) {

          // do not want privacy leak
          // (e.g., sensitive file path in exception msg)

          le = new LoginException("Security Exception");
          le.initCause(new SecurityException());
          if (debug != null) {
            debug.println(
                "original security exception with detail msg "
                    + "replaced by new exception with empty detail msg");
            debug.println("original security exception: " + ite.getCause().toString());
          }
        } else {

          // capture an unexpected LoginModule exception
          java.io.StringWriter sw = new java.io.StringWriter();
          ite.getCause().printStackTrace(new java.io.PrintWriter(sw));
          sw.flush();
          le = new LoginException(sw.toString());
        }

        if (moduleStack[i].entry.getControlFlag()
            == AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {

          if (debug != null) debug.println(methodName + " REQUISITE failure");

          // if REQUISITE, then immediately throw an exception
          if (methodName.equals(ABORT_METHOD) || methodName.equals(LOGOUT_METHOD)) {
            if (firstRequiredError == null) firstRequiredError = le;
          } else {
            throwException(firstRequiredError, le);
          }

        } else if (moduleStack[i].entry.getControlFlag()
            == AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {

          if (debug != null) debug.println(methodName + " REQUIRED failure");

          // mark down that a REQUIRED module failed
          if (firstRequiredError == null) firstRequiredError = le;

        } else {

          if (debug != null) debug.println(methodName + " OPTIONAL failure");

          // mark down that an OPTIONAL module failed
          if (firstError == null) firstError = le;
        }
      }
    }

    // we went thru all the LoginModules.
    if (firstRequiredError != null) {
      // a REQUIRED module failed -- return the error
      throwException(firstRequiredError, null);
    } else if (success == false && firstError != null) {
      // no module succeeded -- return the first error
      throwException(firstError, null);
    } else if (success == false) {
      // no module succeeded -- all modules were IGNORED
      throwException(
          new LoginException(ResourcesMgr.getString("Login.Failure.all.modules.ignored")), null);
    } else {
      // success

      clearState();
      return;
    }
  }

  /**
   * Wrap the caller-specified CallbackHandler in our own and invoke it within a privileged block,
   * constrained by the caller's AccessControlContext.
   */
  private static class SecureCallbackHandler implements CallbackHandler {

    private final java.security.AccessControlContext acc;
    private final CallbackHandler ch;

    SecureCallbackHandler(java.security.AccessControlContext acc, CallbackHandler ch) {
      this.acc = acc;
      this.ch = ch;
    }

    public void handle(final Callback[] callbacks)
        throws java.io.IOException, UnsupportedCallbackException {
      try {
        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedExceptionAction<Void>() {
              public Void run() throws java.io.IOException, UnsupportedCallbackException {
                ch.handle(callbacks);
                return null;
              }
            },
            acc);
      } catch (java.security.PrivilegedActionException pae) {
        if (pae.getException() instanceof java.io.IOException) {
          throw (java.io.IOException) pae.getException();
        } else {
          throw (UnsupportedCallbackException) pae.getException();
        }
      }
    }
  }

  /** LoginModule information - incapsulates Configuration info and actual module instances */
  private static class ModuleInfo {
    AppConfigurationEntry entry;
    Object module;

    ModuleInfo(AppConfigurationEntry newEntry, Object newModule) {
      this.entry = newEntry;
      this.module = newModule;
    }
  }
}
/*     */ public class PKIXCertPathValidator extends CertPathValidatorSpi
/*     */ {
  /*  67 */ private static final Debug debug = Debug.getInstance("certpath");
  /*     */ private Date testDate;
  /*     */ private List<PKIXCertPathChecker> userCheckers;
  /*     */ private String sigProvider;
  /*     */ private BasicChecker basicChecker;
  /*  72 */ private boolean ocspEnabled = false;
  /*  73 */ private boolean onlyEECert = false;
  /*     */
  /*     */ public CertPathValidatorResult engineValidate(
      CertPath paramCertPath, CertPathParameters paramCertPathParameters)
      /*     */ throws CertPathValidatorException, InvalidAlgorithmParameterException
        /*     */ {
    /*  98 */ if (debug != null) {
      /*  99 */ debug.println("PKIXCertPathValidator.engineValidate()...");
      /*     */ }
    /* 101 */ if (!(paramCertPathParameters instanceof PKIXParameters)) {
      /* 102 */ throw new InvalidAlgorithmParameterException(
          "inappropriate parameters, must be an instance of PKIXParameters");
      /*     */ }
    /*     */
    /* 106 */ if ((!paramCertPath.getType().equals("X.509"))
        && (!paramCertPath.getType().equals("X509"))) {
      /* 107 */ throw new InvalidAlgorithmParameterException(
          "inappropriate certification path type specified, must be X.509 or X509");
      /*     */ }
    /*     */
    /* 111 */ PKIXParameters localPKIXParameters = (PKIXParameters) paramCertPathParameters;
    /*     */
    /* 115 */ Set localSet = localPKIXParameters.getTrustAnchors();
    /* 116 */ for (Object localObject1 = localSet.iterator();
        ((Iterator) localObject1).hasNext(); ) {
      localObject2 = (TrustAnchor) ((Iterator) localObject1).next();
      /* 117 */ if (((TrustAnchor) localObject2).getNameConstraints() != null) {
        /* 118 */ throw new InvalidAlgorithmParameterException(
            "name constraints in trust anchor not supported");
        /*     */ }
      /*     */
      /*     */ }
    /*     */
    /* 133 */ localObject1 = new ArrayList(paramCertPath.getCertificates());
    /*     */
    /* 135 */ if (debug != null) {
      /* 136 */ if (((ArrayList) localObject1).isEmpty()) {
        /* 137 */ debug.println("PKIXCertPathValidator.engineValidate() certList is empty");
        /*     */ }
      /*     */
      /* 140 */ debug.println("PKIXCertPathValidator.engineValidate() reversing certpath...");
      /*     */ }
    /*     */
    /* 143 */ Collections.reverse((List) localObject1);
    /*     */
    /* 148 */ populateVariables(localPKIXParameters);
    /*     */
    /* 152 */ Object localObject2 = null;
    /* 153 */ if (!((ArrayList) localObject1).isEmpty()) {
      /* 154 */ localObject2 = (X509Certificate) ((ArrayList) localObject1).get(0);
      /*     */ }
    /*     */
    /* 157 */ Object localObject3 = null;
    /*     */
    /* 161 */ for (TrustAnchor localTrustAnchor : localSet) {
      /* 162 */ X509Certificate localX509Certificate = localTrustAnchor.getTrustedCert();
      /* 163 */ if (localX509Certificate != null) {
        /* 164 */ if (debug != null) {
          /* 165 */ debug.println(
              "PKIXCertPathValidator.engineValidate() anchor.getTrustedCert() != null");
          /*     */ }
        /*     */
        /* 171 */ if (isWorthTrying(localX509Certificate, (X509Certificate) localObject2))
        /*     */ {
          /* 175 */ if (debug != null)
            /* 176 */ debug.println(
                "anchor.getTrustedCert().getSubjectX500Principal() = "
                    + localX509Certificate.getSubjectX500Principal());
          /*     */ }
        /*     */ }
      /*     */ else
      /*     */ {
        /* 181 */ if (debug != null) {
          /* 182 */ debug.println(
              "PKIXCertPathValidator.engineValidate(): anchor.getTrustedCert() == null");
          /*     */ }
        /*     */
        /*     */ try
        /*     */ {
          /* 188 */ PolicyNodeImpl localPolicyNodeImpl =
              new PolicyNodeImpl(
                  null, "2.5.29.32.0", null, false, Collections.singleton("2.5.29.32.0"), false);
          /*     */
          /* 191 */ PolicyNode localPolicyNode =
              doValidate(
                  localTrustAnchor,
                  paramCertPath,
                  (ArrayList) localObject1,
                  localPKIXParameters,
                  localPolicyNodeImpl);
          /*     */
          /* 194 */ return new PKIXCertPathValidatorResult(
              localTrustAnchor, localPolicyNode, this.basicChecker.getPublicKey());
          /*     */ }
        /*     */ catch (CertPathValidatorException localCertPathValidatorException)
        /*     */ {
          /* 198 */ localObject3 = localCertPathValidatorException;
          /*     */ }
        /*     */ }
      /*     */
      /*     */ }
    /*     */
    /* 204 */ if (localObject3 != null) {
      /* 205 */ throw localObject3;
      /*     */ }
    /*     */
    /* 208 */ throw new CertPathValidatorException(
        "Path does not chain with any of the trust anchors",
        null,
        null,
        -1,
        PKIXReason.NO_TRUST_ANCHOR);
    /*     */ }
  /*     */
  /*     */ private boolean isWorthTrying(
      X509Certificate paramX509Certificate1, X509Certificate paramX509Certificate2)
        /*     */ {
    /* 220 */ boolean bool = false;
    /*     */
    /* 222 */ if (debug != null) {
      /* 223 */ debug.println(
          "PKIXCertPathValidator.isWorthTrying() checking if this trusted cert is worth trying ...");
      /*     */ }
    /*     */
    /* 227 */ if (paramX509Certificate2 == null) {
      /* 228 */ return true;
      /*     */ }
    /*     */
    /* 231 */ AdaptableX509CertSelector localAdaptableX509CertSelector =
        new AdaptableX509CertSelector();
    /*     */
    /* 235 */ localAdaptableX509CertSelector.setSubject(
        paramX509Certificate2.getIssuerX500Principal());
    /*     */
    /* 238 */ localAdaptableX509CertSelector.setValidityPeriod(
        paramX509Certificate2.getNotBefore(), paramX509Certificate2.getNotAfter());
    /*     */ try
    /*     */ {
      /* 246 */ X509CertImpl localX509CertImpl = X509CertImpl.toImpl(paramX509Certificate2);
      /* 247 */ localAdaptableX509CertSelector.parseAuthorityKeyIdentifierExtension(
          localX509CertImpl.getAuthorityKeyIdentifierExtension());
      /*     */
      /* 250 */ bool = localAdaptableX509CertSelector.match(paramX509Certificate1);
      /*     */ }
    /*     */ catch (Exception localException)
    /*     */ {
      /*     */ }
    /* 255 */ if (debug != null) {
      /* 256 */ if (bool) /* 257 */ debug.println("YES - try this trustedCert");
      /*     */ else {
        /* 259 */ debug.println("NO - don't try this trustedCert");
        /*     */ }
      /*     */ }
    /*     */
    /* 263 */ return bool;
    /*     */ }
  /*     */
  /*     */ private void populateVariables(PKIXParameters paramPKIXParameters) /*     */ {
    /* 272 */ this.testDate = paramPKIXParameters.getDate();
    /* 273 */ if (this.testDate == null) {
      /* 274 */ this.testDate = new Date(System.currentTimeMillis());
      /*     */ }
    /*     */
    /* 277 */ this.userCheckers = paramPKIXParameters.getCertPathCheckers();
    /* 278 */ this.sigProvider = paramPKIXParameters.getSigProvider();
    /*     */
    /* 280 */ if (paramPKIXParameters.isRevocationEnabled())
    /*     */ {
      /* 282 */ this.ocspEnabled =
          ((Boolean)
                  AccessController.doPrivileged(
                      new GetBooleanSecurityPropertyAction("ocsp.enable")))
              .booleanValue();
      /*     */
      /* 285 */ this.onlyEECert =
          ((Boolean)
                  AccessController.doPrivileged(
                      new GetBooleanSecurityPropertyAction(
                          "com.sun.security.onlyCheckRevocationOfEECert")))
              .booleanValue();
      /*     */ }
    /*     */ }
  /*     */
  /*     */ private PolicyNode doValidate(
      TrustAnchor paramTrustAnchor,
      CertPath paramCertPath,
      ArrayList<X509Certificate> paramArrayList,
      PKIXParameters paramPKIXParameters,
      PolicyNodeImpl paramPolicyNodeImpl)
      /*     */ throws CertPathValidatorException
        /*     */ {
    /* 301 */ int i = paramArrayList.size();
    /*     */
    /* 303 */ this.basicChecker =
        new BasicChecker(paramTrustAnchor, this.testDate, this.sigProvider, false);
    /* 304 */ AlgorithmChecker localAlgorithmChecker = new AlgorithmChecker(paramTrustAnchor);
    /* 305 */ KeyChecker localKeyChecker =
        new KeyChecker(i, paramPKIXParameters.getTargetCertConstraints());
    /*     */
    /* 307 */ ConstraintsChecker localConstraintsChecker = new ConstraintsChecker(i);
    /*     */
    /* 310 */ PolicyChecker localPolicyChecker =
        new PolicyChecker(
            paramPKIXParameters.getInitialPolicies(),
            i,
            paramPKIXParameters.isExplicitPolicyRequired(),
            paramPKIXParameters.isPolicyMappingInhibited(),
            paramPKIXParameters.isAnyPolicyInhibited(),
            paramPKIXParameters.getPolicyQualifiersRejected(),
            paramPolicyNodeImpl);
    /*     */
    /* 317 */ UntrustedChecker localUntrustedChecker = new UntrustedChecker();
    /*     */
    /* 319 */ ArrayList localArrayList = new ArrayList();
    /*     */
    /* 322 */ localArrayList.add(localUntrustedChecker);
    /* 323 */ localArrayList.add(localAlgorithmChecker);
    /* 324 */ localArrayList.add(localKeyChecker);
    /* 325 */ localArrayList.add(localConstraintsChecker);
    /* 326 */ localArrayList.add(localPolicyChecker);
    /* 327 */ localArrayList.add(this.basicChecker);
    /*     */
    /* 330 */ if (paramPKIXParameters.isRevocationEnabled())
    /*     */ {
      /* 333 */ if (this.ocspEnabled) {
        /* 334 */ localObject =
            new OCSPChecker(paramCertPath, paramPKIXParameters, this.onlyEECert);
        /*     */
        /* 336 */ localArrayList.add(localObject);
        /*     */ }
      /*     */
      /* 340 */ localObject =
          new CrlRevocationChecker(
              paramTrustAnchor, paramPKIXParameters, paramArrayList, this.onlyEECert);
      /*     */
      /* 342 */ localArrayList.add(localObject);
      /*     */ }
    /*     */
    /* 346 */ localArrayList.addAll(this.userCheckers);
    /*     */
    /* 348 */ Object localObject = new PKIXMasterCertPathValidator(localArrayList);
    /*     */
    /* 351 */ ((PKIXMasterCertPathValidator) localObject).validate(paramCertPath, paramArrayList);
    /*     */
    /* 353 */ return localPolicyChecker.getPolicyTree();
    /*     */ }
  /*     */ }
 /*     */ public CertPathValidatorResult engineValidate(
     CertPath paramCertPath, CertPathParameters paramCertPathParameters)
     /*     */ throws CertPathValidatorException, InvalidAlgorithmParameterException
       /*     */ {
   /*  98 */ if (debug != null) {
     /*  99 */ debug.println("PKIXCertPathValidator.engineValidate()...");
     /*     */ }
   /* 101 */ if (!(paramCertPathParameters instanceof PKIXParameters)) {
     /* 102 */ throw new InvalidAlgorithmParameterException(
         "inappropriate parameters, must be an instance of PKIXParameters");
     /*     */ }
   /*     */
   /* 106 */ if ((!paramCertPath.getType().equals("X.509"))
       && (!paramCertPath.getType().equals("X509"))) {
     /* 107 */ throw new InvalidAlgorithmParameterException(
         "inappropriate certification path type specified, must be X.509 or X509");
     /*     */ }
   /*     */
   /* 111 */ PKIXParameters localPKIXParameters = (PKIXParameters) paramCertPathParameters;
   /*     */
   /* 115 */ Set localSet = localPKIXParameters.getTrustAnchors();
   /* 116 */ for (Object localObject1 = localSet.iterator();
       ((Iterator) localObject1).hasNext(); ) {
     localObject2 = (TrustAnchor) ((Iterator) localObject1).next();
     /* 117 */ if (((TrustAnchor) localObject2).getNameConstraints() != null) {
       /* 118 */ throw new InvalidAlgorithmParameterException(
           "name constraints in trust anchor not supported");
       /*     */ }
     /*     */
     /*     */ }
   /*     */
   /* 133 */ localObject1 = new ArrayList(paramCertPath.getCertificates());
   /*     */
   /* 135 */ if (debug != null) {
     /* 136 */ if (((ArrayList) localObject1).isEmpty()) {
       /* 137 */ debug.println("PKIXCertPathValidator.engineValidate() certList is empty");
       /*     */ }
     /*     */
     /* 140 */ debug.println("PKIXCertPathValidator.engineValidate() reversing certpath...");
     /*     */ }
   /*     */
   /* 143 */ Collections.reverse((List) localObject1);
   /*     */
   /* 148 */ populateVariables(localPKIXParameters);
   /*     */
   /* 152 */ Object localObject2 = null;
   /* 153 */ if (!((ArrayList) localObject1).isEmpty()) {
     /* 154 */ localObject2 = (X509Certificate) ((ArrayList) localObject1).get(0);
     /*     */ }
   /*     */
   /* 157 */ Object localObject3 = null;
   /*     */
   /* 161 */ for (TrustAnchor localTrustAnchor : localSet) {
     /* 162 */ X509Certificate localX509Certificate = localTrustAnchor.getTrustedCert();
     /* 163 */ if (localX509Certificate != null) {
       /* 164 */ if (debug != null) {
         /* 165 */ debug.println(
             "PKIXCertPathValidator.engineValidate() anchor.getTrustedCert() != null");
         /*     */ }
       /*     */
       /* 171 */ if (isWorthTrying(localX509Certificate, (X509Certificate) localObject2))
       /*     */ {
         /* 175 */ if (debug != null)
           /* 176 */ debug.println(
               "anchor.getTrustedCert().getSubjectX500Principal() = "
                   + localX509Certificate.getSubjectX500Principal());
         /*     */ }
       /*     */ }
     /*     */ else
     /*     */ {
       /* 181 */ if (debug != null) {
         /* 182 */ debug.println(
             "PKIXCertPathValidator.engineValidate(): anchor.getTrustedCert() == null");
         /*     */ }
       /*     */
       /*     */ try
       /*     */ {
         /* 188 */ PolicyNodeImpl localPolicyNodeImpl =
             new PolicyNodeImpl(
                 null, "2.5.29.32.0", null, false, Collections.singleton("2.5.29.32.0"), false);
         /*     */
         /* 191 */ PolicyNode localPolicyNode =
             doValidate(
                 localTrustAnchor,
                 paramCertPath,
                 (ArrayList) localObject1,
                 localPKIXParameters,
                 localPolicyNodeImpl);
         /*     */
         /* 194 */ return new PKIXCertPathValidatorResult(
             localTrustAnchor, localPolicyNode, this.basicChecker.getPublicKey());
         /*     */ }
       /*     */ catch (CertPathValidatorException localCertPathValidatorException)
       /*     */ {
         /* 198 */ localObject3 = localCertPathValidatorException;
         /*     */ }
       /*     */ }
     /*     */
     /*     */ }
   /*     */
   /* 204 */ if (localObject3 != null) {
     /* 205 */ throw localObject3;
     /*     */ }
   /*     */
   /* 208 */ throw new CertPathValidatorException(
       "Path does not chain with any of the trust anchors",
       null,
       null,
       -1,
       PKIXReason.NO_TRUST_ANCHOR);
   /*     */ }
Exemplo n.º 22
0
  private static void initialize() {
    props = new Properties();
    boolean loadedProps = false;
    boolean overrideAll = false;

    // first load the system properties file
    // to determine the value of security.overridePropertiesFile
    File propFile = securityPropFile("java.security");
    if (propFile.exists()) {
      try {
        FileInputStream fis = new FileInputStream(propFile);
        InputStream is = new BufferedInputStream(fis);
        props.load(is);
        is.close();
        loadedProps = true;

        if (sdebug != null) {
          sdebug.println("reading security properties file: " + propFile);
        }
      } catch (IOException e) {
        if (sdebug != null) {
          sdebug.println("unable to load security properties from " + propFile);
          e.printStackTrace();
        }
      }
    }

    if ("true".equalsIgnoreCase(props.getProperty("security.overridePropertiesFile"))) {

      String extraPropFile = System.getProperty("java.security.properties");
      if (extraPropFile != null && extraPropFile.startsWith("=")) {
        overrideAll = true;
        extraPropFile = extraPropFile.substring(1);
      }

      if (overrideAll) {
        props = new Properties();
        if (sdebug != null) {
          sdebug.println("overriding other security properties files!");
        }
      }

      // now load the user-specified file so its values
      // will win if they conflict with the earlier values
      if (extraPropFile != null) {
        try {
          URL propURL;

          extraPropFile = PropertyExpander.expand(extraPropFile);
          propFile = new File(extraPropFile);
          if (propFile.exists()) {
            propURL = new URL("file:" + propFile.getCanonicalPath());
          } else {
            propURL = new URL(extraPropFile);
          }
          BufferedInputStream bis = new BufferedInputStream(propURL.openStream());
          props.load(bis);
          bis.close();
          loadedProps = true;

          if (sdebug != null) {
            sdebug.println("reading security properties file: " + propURL);
            if (overrideAll) {
              sdebug.println("overriding other security properties files!");
            }
          }
        } catch (Exception e) {
          if (sdebug != null) {
            sdebug.println("unable to load security properties from " + extraPropFile);
            e.printStackTrace();
          }
        }
      }
    }

    if (!loadedProps) {
      initializeStatic();
      if (sdebug != null) {
        sdebug.println("unable to load security properties " + "-- using defaults");
      }
    }
  }
Exemplo n.º 23
0
 /*     */ Permission resolve(Permission paramPermission, Certificate[] paramArrayOfCertificate)
       /*     */ {
   /* 230 */ if (this.certs != null)
   /*     */ {
     /* 232 */ if (paramArrayOfCertificate == null) {
       /* 233 */ return null;
       /*     */ }
     /*     */
     /* 238 */ for (int j = 0; j < this.certs.length; j++) {
       /* 239 */ int i = 0;
       /* 240 */ for (int k = 0; k < paramArrayOfCertificate.length; k++) {
         /* 241 */ if (this.certs[j].equals(paramArrayOfCertificate[k])) {
           /* 242 */ i = 1;
           /* 243 */ break;
           /*     */ }
         /*     */ }
       /* 246 */ if (i == 0) return null;
       /*     */ }
     /*     */ }
   /*     */ try
   /*     */ {
     /* 250 */ Class localClass = paramPermission.getClass();
     /*     */
     /* 252 */ if ((this.name == null) && (this.actions == null)) {
       /*     */ try {
         /* 254 */ Constructor localConstructor1 = localClass.getConstructor(PARAMS0);
         /* 255 */ return (Permission) localConstructor1.newInstance(new Object[0]);
         /*     */ } catch (NoSuchMethodException localNoSuchMethodException2) {
         /*     */ try {
           /* 258 */ Constructor localConstructor4 = localClass.getConstructor(PARAMS1);
           /* 259 */ return (Permission) localConstructor4.newInstance(new Object[] {this.name});
           /*     */ }
         /*     */ catch (NoSuchMethodException localNoSuchMethodException4) {
           /* 262 */ Constructor localConstructor6 = localClass.getConstructor(PARAMS2);
           /* 263 */ return (Permission)
               localConstructor6.newInstance(new Object[] {this.name, this.actions});
           /*     */ }
         /*     */ }
       /*     */ }
     /*     */
     /* 268 */ if ((this.name != null) && (this.actions == null)) {
       /*     */ try {
         /* 270 */ Constructor localConstructor2 = localClass.getConstructor(PARAMS1);
         /* 271 */ return (Permission) localConstructor2.newInstance(new Object[] {this.name});
         /*     */ }
       /*     */ catch (NoSuchMethodException localNoSuchMethodException3) {
         /* 274 */ Constructor localConstructor5 = localClass.getConstructor(PARAMS2);
         /* 275 */ return (Permission)
             localConstructor5.newInstance(new Object[] {this.name, this.actions});
         /*     */ }
       /*     */ }
     /*     */
     /* 279 */ Constructor localConstructor3 = localClass.getConstructor(PARAMS2);
     /* 280 */ return (Permission)
         localConstructor3.newInstance(new Object[] {this.name, this.actions});
     /*     */ }
   /*     */ catch (NoSuchMethodException localNoSuchMethodException1)
   /*     */ {
     /* 285 */ if (debug != null) {
       /* 286 */ debug.println(
           "NoSuchMethodException:\n  could not find proper constructor for " + this.type);
       /*     */
       /* 288 */ localNoSuchMethodException1.printStackTrace();
       /*     */ }
     /* 290 */ return null;
     /*     */ } catch (Exception localException) {
     /* 292 */ if (debug != null) {
       /* 293 */ debug.println("unable to instantiate " + this.name);
       /* 294 */ localException.printStackTrace();
       /*     */ }
     /*     */ }
   /* 296 */ return null;
   /*     */ }
Exemplo n.º 24
0
/*     */ public final class UnresolvedPermission extends Permission
    /*     */ implements Serializable
/*     */ {
  /*     */ private static final long serialVersionUID = -4821973115467008846L;
  /* 109 */ private static final Debug debug =
      Debug.getInstance("policy,access", "UnresolvedPermission");
  /*     */ private String type;
  /*     */ private String name;
  /*     */ private String actions;
  /*     */ private transient Certificate[] certs;
  /* 221 */ private static final Class[] PARAMS0 = new Class[0];
  /* 222 */ private static final Class[] PARAMS1 = {String.class};
  /* 223 */ private static final Class[] PARAMS2 = {String.class, String.class};
  /*     */
  /*     */ public UnresolvedPermission(
      String paramString1,
      String paramString2,
      String paramString3,
      Certificate[] paramArrayOfCertificate)
        /*     */ {
    /* 159 */ super(paramString1);
    /*     */
    /* 161 */ if (paramString1 == null) {
      /* 162 */ throw new NullPointerException("type can't be null");
      /*     */ }
    /* 164 */ this.type = paramString1;
    /* 165 */ this.name = paramString2;
    /* 166 */ this.actions = paramString3;
    /* 167 */ if (paramArrayOfCertificate != null)
    /*     */ {
      /* 169 */ for (int i = 0; i < paramArrayOfCertificate.length; i++) {
        /* 170 */ if (!(paramArrayOfCertificate[i] instanceof X509Certificate))
        /*     */ {
          /* 173 */ this.certs = ((Certificate[]) paramArrayOfCertificate.clone());
          /* 174 */ break;
          /*     */ }
        /*     */ }
      /*     */
      /* 178 */ if (this.certs == null)
      /*     */ {
        /* 181 */ i = 0;
        /* 182 */ int j = 0;
        /* 183 */ while (i < paramArrayOfCertificate.length) {
          /* 184 */ j++;
          /* 185 */ while ((i + 1 < paramArrayOfCertificate.length)
              && (((X509Certificate) paramArrayOfCertificate[i])
                  .getIssuerDN()
                  .equals(((X509Certificate) paramArrayOfCertificate[(i + 1)]).getSubjectDN())))
          /*     */ {
            /* 188 */ i++;
            /*     */ }
          /* 190 */ i++;
          /*     */ }
        /* 192 */ if (j == paramArrayOfCertificate.length)
        /*     */ {
          /* 195 */ this.certs = ((Certificate[]) paramArrayOfCertificate.clone());
          /*     */ }
        /*     */
        /* 198 */ if (this.certs == null)
        /*     */ {
          /* 200 */ ArrayList localArrayList = new ArrayList();
          /*     */
          /* 202 */ i = 0;
          /* 203 */ while (i < paramArrayOfCertificate.length) {
            /* 204 */ localArrayList.add(paramArrayOfCertificate[i]);
            /* 205 */ while ((i + 1 < paramArrayOfCertificate.length)
                && (((X509Certificate) paramArrayOfCertificate[i])
                    .getIssuerDN()
                    .equals(((X509Certificate) paramArrayOfCertificate[(i + 1)]).getSubjectDN())))
            /*     */ {
              /* 208 */ i++;
              /*     */ }
            /* 210 */ i++;
            /*     */ }
          /* 212 */ this.certs = new Certificate[localArrayList.size()];
          /*     */
          /* 214 */ localArrayList.toArray(this.certs);
          /*     */ }
        /*     */ }
      /*     */ }
    /*     */ }
  /*     */
  /*     */ Permission resolve(Permission paramPermission, Certificate[] paramArrayOfCertificate)
        /*     */ {
    /* 230 */ if (this.certs != null)
    /*     */ {
      /* 232 */ if (paramArrayOfCertificate == null) {
        /* 233 */ return null;
        /*     */ }
      /*     */
      /* 238 */ for (int j = 0; j < this.certs.length; j++) {
        /* 239 */ int i = 0;
        /* 240 */ for (int k = 0; k < paramArrayOfCertificate.length; k++) {
          /* 241 */ if (this.certs[j].equals(paramArrayOfCertificate[k])) {
            /* 242 */ i = 1;
            /* 243 */ break;
            /*     */ }
          /*     */ }
        /* 246 */ if (i == 0) return null;
        /*     */ }
      /*     */ }
    /*     */ try
    /*     */ {
      /* 250 */ Class localClass = paramPermission.getClass();
      /*     */
      /* 252 */ if ((this.name == null) && (this.actions == null)) {
        /*     */ try {
          /* 254 */ Constructor localConstructor1 = localClass.getConstructor(PARAMS0);
          /* 255 */ return (Permission) localConstructor1.newInstance(new Object[0]);
          /*     */ } catch (NoSuchMethodException localNoSuchMethodException2) {
          /*     */ try {
            /* 258 */ Constructor localConstructor4 = localClass.getConstructor(PARAMS1);
            /* 259 */ return (Permission) localConstructor4.newInstance(new Object[] {this.name});
            /*     */ }
          /*     */ catch (NoSuchMethodException localNoSuchMethodException4) {
            /* 262 */ Constructor localConstructor6 = localClass.getConstructor(PARAMS2);
            /* 263 */ return (Permission)
                localConstructor6.newInstance(new Object[] {this.name, this.actions});
            /*     */ }
          /*     */ }
        /*     */ }
      /*     */
      /* 268 */ if ((this.name != null) && (this.actions == null)) {
        /*     */ try {
          /* 270 */ Constructor localConstructor2 = localClass.getConstructor(PARAMS1);
          /* 271 */ return (Permission) localConstructor2.newInstance(new Object[] {this.name});
          /*     */ }
        /*     */ catch (NoSuchMethodException localNoSuchMethodException3) {
          /* 274 */ Constructor localConstructor5 = localClass.getConstructor(PARAMS2);
          /* 275 */ return (Permission)
              localConstructor5.newInstance(new Object[] {this.name, this.actions});
          /*     */ }
        /*     */ }
      /*     */
      /* 279 */ Constructor localConstructor3 = localClass.getConstructor(PARAMS2);
      /* 280 */ return (Permission)
          localConstructor3.newInstance(new Object[] {this.name, this.actions});
      /*     */ }
    /*     */ catch (NoSuchMethodException localNoSuchMethodException1)
    /*     */ {
      /* 285 */ if (debug != null) {
        /* 286 */ debug.println(
            "NoSuchMethodException:\n  could not find proper constructor for " + this.type);
        /*     */
        /* 288 */ localNoSuchMethodException1.printStackTrace();
        /*     */ }
      /* 290 */ return null;
      /*     */ } catch (Exception localException) {
      /* 292 */ if (debug != null) {
        /* 293 */ debug.println("unable to instantiate " + this.name);
        /* 294 */ localException.printStackTrace();
        /*     */ }
      /*     */ }
    /* 296 */ return null;
    /*     */ }
  /*     */
  /*     */ public boolean implies(Permission paramPermission) /*     */ {
    /* 310 */ return false;
    /*     */ }
  /*     */
  /*     */ public boolean equals(Object paramObject) /*     */ {
    /* 330 */ if (paramObject == this) {
      /* 331 */ return true;
      /*     */ }
    /* 333 */ if (!(paramObject instanceof UnresolvedPermission)) /* 334 */ return false;
    /* 335 */ UnresolvedPermission localUnresolvedPermission = (UnresolvedPermission) paramObject;
    /*     */
    /* 338 */ if (!this.type.equals(localUnresolvedPermission.type)) {
      /* 339 */ return false;
      /*     */ }
    /*     */
    /* 343 */ if (this.name == null) {
      /* 344 */ if (localUnresolvedPermission.name != null) /* 345 */ return false;
      /*     */ }
    /* 347 */ else if (!this.name.equals(localUnresolvedPermission.name)) {
      /* 348 */ return false;
      /*     */ }
    /*     */
    /* 352 */ if (this.actions == null) {
      /* 353 */ if (localUnresolvedPermission.actions != null) {
        /* 354 */ return false;
        /*     */ }
      /*     */ }
    /* 357 */ else if (!this.actions.equals(localUnresolvedPermission.actions)) {
      /* 358 */ return false;
      /*     */ }
    /*     */
    /* 363 */ if (((this.certs == null) && (localUnresolvedPermission.certs != null))
        || ((this.certs != null) && (localUnresolvedPermission.certs == null))
        || ((this.certs != null)
            && (localUnresolvedPermission.certs != null)
            && (this.certs.length != localUnresolvedPermission.certs.length)))
    /*     */ {
      /* 367 */ return false;
      /*     */ }
    /*     */ int k;
    /*     */ int j;
    /* 373 */ for (int i = 0; (this.certs != null) && (i < this.certs.length); i++) {
      /* 374 */ k = 0;
      /* 375 */ for (j = 0; j < localUnresolvedPermission.certs.length; j++) {
        /* 376 */ if (this.certs[i].equals(localUnresolvedPermission.certs[j])) {
          /* 377 */ k = 1;
          /* 378 */ break;
          /*     */ }
        /*     */ }
      /* 381 */ if (k == 0) return false;
      /*     */ }
    /*     */
    /* 384 */ for (i = 0;
        (localUnresolvedPermission.certs != null) && (i < localUnresolvedPermission.certs.length);
        i++) {
      /* 385 */ k = 0;
      /* 386 */ for (j = 0; j < this.certs.length; j++) {
        /* 387 */ if (localUnresolvedPermission.certs[i].equals(this.certs[j])) {
          /* 388 */ k = 1;
          /* 389 */ break;
          /*     */ }
        /*     */ }
      /* 392 */ if (k == 0) return false;
      /*     */ }
    /* 394 */ return true;
    /*     */ }
  /*     */
  /*     */ public int hashCode() /*     */ {
    /* 404 */ int i = this.type.hashCode();
    /* 405 */ if (this.name != null) /* 406 */ i ^= this.name.hashCode();
    /* 407 */ if (this.actions != null) /* 408 */ i ^= this.actions.hashCode();
    /* 409 */ return i;
    /*     */ }
  /*     */
  /*     */ public String getActions() /*     */ {
    /* 424 */ return "";
    /*     */ }
  /*     */
  /*     */ public String getUnresolvedType() /*     */ {
    /* 437 */ return this.type;
    /*     */ }
  /*     */
  /*     */ public String getUnresolvedName() /*     */ {
    /* 451 */ return this.name;
    /*     */ }
  /*     */
  /*     */ public String getUnresolvedActions() /*     */ {
    /* 465 */ return this.actions;
    /*     */ }
  /*     */
  /*     */ public Certificate[] getUnresolvedCerts() /*     */ {
    /* 479 */ return this.certs == null ? null : (Certificate[]) this.certs.clone();
    /*     */ }
  /*     */
  /*     */ public String toString() /*     */ {
    /* 490 */ return "(unresolved " + this.type + " " + this.name + " " + this.actions + ")";
    /*     */ }
  /*     */
  /*     */ public PermissionCollection newPermissionCollection() /*     */ {
    /* 502 */ return new UnresolvedPermissionCollection();
    /*     */ }
  /*     */
  /*     */ private void writeObject(ObjectOutputStream paramObjectOutputStream)
      /*     */ throws IOException
        /*     */ {
    /* 524 */ paramObjectOutputStream.defaultWriteObject();
    /*     */
    /* 526 */ if ((this.certs == null) || (this.certs.length == 0)) {
      /* 527 */ paramObjectOutputStream.writeInt(0);
      /*     */ }
    /*     */ else {
      /* 530 */ paramObjectOutputStream.writeInt(this.certs.length);
      /*     */
      /* 532 */ for (int i = 0; i < this.certs.length; i++) {
        /* 533 */ Certificate localCertificate = this.certs[i];
        /*     */ try {
          /* 535 */ paramObjectOutputStream.writeUTF(localCertificate.getType());
          /* 536 */ byte[] arrayOfByte = localCertificate.getEncoded();
          /* 537 */ paramObjectOutputStream.writeInt(arrayOfByte.length);
          /* 538 */ paramObjectOutputStream.write(arrayOfByte);
          /*     */ } catch (CertificateEncodingException localCertificateEncodingException) {
          /* 540 */ throw new IOException(localCertificateEncodingException.getMessage());
          /*     */ }
        /*     */ }
      /*     */ }
    /*     */ }
  /*     */
  /*     */ private void readObject(ObjectInputStream paramObjectInputStream)
      /*     */ throws IOException, ClassNotFoundException
        /*     */ {
    /* 553 */ Hashtable localHashtable = null;
    /*     */
    /* 555 */ paramObjectInputStream.defaultReadObject();
    /*     */
    /* 557 */ if (this.type == null) {
      /* 558 */ throw new NullPointerException("type can't be null");
      /*     */ }
    /*     */
    /* 561 */ int i = paramObjectInputStream.readInt();
    /* 562 */ if (i > 0)
    /*     */ {
      /* 565 */ localHashtable = new Hashtable(3);
      /* 566 */ this.certs = new Certificate[i];
      /*     */ }
    /*     */
    /* 569 */ for (int j = 0; j < i; j++)
    /*     */ {
      /* 572 */ String str = paramObjectInputStream.readUTF();
      /*     */ CertificateFactory localCertificateFactory;
      /* 573 */ if (localHashtable.containsKey(str))
      /*     */ {
        /* 575 */ localCertificateFactory = (CertificateFactory) localHashtable.get(str);
        /*     */ }
      /*     */ else {
        /*     */ try {
          /* 579 */ localCertificateFactory = CertificateFactory.getInstance(str);
          /*     */ } catch (CertificateException localCertificateException1) {
          /* 581 */ throw new ClassNotFoundException(
              "Certificate factory for " + str + " not found");
          /*     */ }
        /*     */
        /* 585 */ localHashtable.put(str, localCertificateFactory);
        /*     */ }
      /*     */
      /* 588 */ byte[] arrayOfByte = null;
      /*     */ try {
        /* 590 */ arrayOfByte = new byte[paramObjectInputStream.readInt()];
        /*     */ } catch (OutOfMemoryError localOutOfMemoryError) {
        /* 592 */ throw new IOException("Certificate too big");
        /*     */ }
      /* 594 */ paramObjectInputStream.readFully(arrayOfByte);
      /* 595 */ ByteArrayInputStream localByteArrayInputStream =
          new ByteArrayInputStream(arrayOfByte);
      /*     */ try {
        /* 597 */ this.certs[j] =
            localCertificateFactory.generateCertificate(localByteArrayInputStream);
        /*     */ } catch (CertificateException localCertificateException2) {
        /* 599 */ throw new IOException(localCertificateException2.getMessage());
        /*     */ }
      /* 601 */ localByteArrayInputStream.close();
      /*     */ }
    /*     */ }
  /*     */ }
Exemplo n.º 25
0
/**
 * This class represents a reverse builder, which is able to retrieve matching certificates from
 * CertStores and verify a particular certificate against a ReverseState.
 *
 * @version 1.13 01/23/03
 * @since 1.4
 * @author Sean Mullan
 * @author Yassir Elley
 */
class ReverseBuilder extends Builder {

  private Debug debug = Debug.getInstance("certpath");

  private Date date;
  private X509CertSelector targetCertSelector;
  Set initPolicies;

  /**
   * Initialize the builder with the input parameters.
   *
   * @param params the parameter set used to build a certification path
   */
  ReverseBuilder(PKIXBuilderParameters buildParams, X500Principal targetSubjectDN) {
    super(buildParams, targetSubjectDN);

    // Initialize date if not specified
    date = buildParams.getDate();
    if (date == null) {
      date = new Date();
    }

    targetCertSelector = (X509CertSelector) buildParams.getTargetCertConstraints();

    Set initialPolicies = buildParams.getInitialPolicies();
    initPolicies = new HashSet();
    if (initialPolicies.isEmpty()) {
      // if no initialPolicies are specified by user, set
      // initPolicies to be anyPolicy by default
      initPolicies.add(PolicyChecker.ANY_POLICY);
    } else {
      Iterator i = initialPolicies.iterator();
      while (i.hasNext()) {
        initPolicies.add(i.next());
      }
    }
  }

  /**
   * Retrieves all certs from a CertStore which satisfy the requirements specified in the parameters
   * and the current PKIX state (name constraints, policy constraints, etc).
   *
   * @param currentState the current state. Must be an instance of <code>ReverseState</code>
   */
  Collection getMatchingCerts(State currState)
      throws CertStoreException, CertificateException, IOException {
    ReverseState currentState = (ReverseState) currState;

    if (debug != null) debug.println("In ReverseBuilder.getMatchingCerts.");

    /*
     * The last certificate could be an EE or a CA certificate
     * (we may be building a partial certification path or
     * establishing trust in a CA).
     *
     * Try the EE certs before the CA certs. It will be more
     * common to build a path to an end entity.
     */
    Collection certs = getMatchingEECerts(currentState);
    certs.addAll(getMatchingCACerts(currentState));

    return certs;
  }

  /*
   * Retrieves all end-entity certificates which satisfy constraints
   * and requirements specified in the parameters and PKIX state.
   */
  private Collection getMatchingEECerts(ReverseState currentState)
      throws CertStoreException, CertificateException, IOException {

    /*
     * Compose a CertSelector to filter out
     * certs which do not satisfy requirements.
     *
     * First, retrieve clone of current target cert constraints,
     * and then add more selection criteria based on current validation state.
     */
    X509CertSelector sel = (X509CertSelector) buildParams.getTargetCertConstraints();

    /*
     * Match on issuer (subject of previous cert)
     */
    CertPathHelper.setIssuer(sel, currentState.subjectDN);

    /*
     * Match on certificate validity date.
     */
    sel.setCertificateValid(date);

    /*
     * Policy processing optimizations
     */
    if (currentState.explicitPolicy == 0) sel.setPolicy(getMatchingPolicies());

    /*
     * If previous cert has a subject key identifier extension,
     * use it to match on authority key identifier extension.
     */
    /*if (currentState.subjKeyId != null) {
    AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
    	(KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
    	null, null);
    sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
         }*/

    /*
     * Require EE certs
     */
    sel.setBasicConstraints(-2);

    /* Retrieve matching certs from CertStores */
    HashSet eeCerts = new HashSet();
    addMatchingCerts(sel, buildParams.getCertStores(), eeCerts);

    if (debug != null) {
      debug.println("ReverseBuilder.getMatchingEECerts got " + eeCerts.size() + " certs.");
    }
    return eeCerts;
  }

  /*
   * Retrieves all CA certificates which satisfy constraints
   * and requirements specified in the parameters and PKIX state.
   */
  private Collection getMatchingCACerts(ReverseState currentState)
      throws CertificateException, CertStoreException, IOException {

    /*
     * Compose a CertSelector to filter out
     * certs which do not satisfy requirements.
     */
    X509CertSelector sel = new X509CertSelector();

    /*
     * Match on issuer (subject of previous cert)
     */
    CertPathHelper.setIssuer(sel, currentState.subjectDN);

    /*
     * Match on certificate validity date.
     */
    sel.setCertificateValid(date);

    /*
     * Match on target subject name (checks that current cert's
     * name constraints permit it to certify target).
     * (4 is the integer type for DIRECTORY name).
     */
    sel.addPathToName(4, targetCertSelector.getSubjectAsBytes());

    /*
     * Policy processing optimizations
     */
    if (currentState.explicitPolicy == 0) sel.setPolicy(getMatchingPolicies());

    /*
     * If previous cert has a subject key identifier extension,
     * use it to match on authority key identifier extension.
     */
    /*if (currentState.subjKeyId != null) {
    AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
    	(KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
    			null, null);
    sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
         }*/

    /*
     * Require CA certs
     */
    sel.setBasicConstraints(0);

    /* Retrieve matching certs from CertStores */
    ArrayList reverseCerts = new ArrayList();
    addMatchingCerts(sel, buildParams.getCertStores(), reverseCerts);

    /* Sort remaining certs using name constraints */
    Collections.sort(reverseCerts, new PKIXCertComparator());

    if (debug != null)
      debug.println("ReverseBuilder.getMatchingCACerts got " + reverseCerts.size() + " certs.");
    return reverseCerts;
  }

  /*
   * This inner class compares 2 PKIX certificates according to which
   * should be tried first when building a path to the target. For
   * now, the algorithm is to look at name constraints in each cert and those
   * which constrain the path closer to the target should be
   * ranked higher. Later, we may want to consider other components,
   * such as key identifiers.
   */
  class PKIXCertComparator implements Comparator {

    private Debug debug = Debug.getInstance("certpath");

    public int compare(Object o1, Object o2) {

      X509Certificate cert1 = (X509Certificate) o1;
      X509Certificate cert2 = (X509Certificate) o2;

      /*
       * if either cert certifies the target, always
       * put at head of list.
       */
      if (cert1.getSubjectX500Principal().equals(targetSubjectDN)) {
        return -1;
      }
      if (cert2.getSubjectX500Principal().equals(targetSubjectDN)) {
        return 1;
      }

      int targetDist1;
      int targetDist2;
      try {
        X500Name targetSubjectName = X500Name.asX500Name(targetSubjectDN);
        targetDist1 = Builder.targetDistance(null, cert1, targetSubjectName);
        targetDist2 = Builder.targetDistance(null, cert2, targetSubjectName);
      } catch (IOException e) {
        if (debug != null) {
          debug.println("IOException in call to Builder.targetDistance");
          e.printStackTrace();
        }
        throw new ClassCastException("Invalid target subject distinguished name");
      }

      if (targetDist1 == targetDist2) return 0;

      if (targetDist1 == -1) return 1;

      if (targetDist1 < targetDist2) return -1;

      return 1;
    }
  }

  /**
   * 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);
    }
  }

  /**
   * Verifies whether the input certificate completes the path. This checks whether the cert is the
   * target certificate.
   *
   * @param cert the certificate to test
   * @return a boolean value indicating whether the cert completes the path.
   */
  boolean isPathCompleted(X509Certificate cert) {
    return cert.getSubjectX500Principal().equals(targetSubjectDN);
  }

  /**
   * Adds the certificate to the certPathList
   *
   * @param cert the certificate to be added
   * @param certPathList the certification path list
   */
  void addCertToPath(X509Certificate cert, LinkedList certPathList) {
    certPathList.addLast(cert);
  }

  /**
   * Removes final certificate from the certPathList
   *
   * @param certPathList the certification path list
   */
  void removeFinalCertFromPath(LinkedList certPathList) {
    certPathList.removeLast();
  }
}
Exemplo n.º 26
0
  /*
   * This method performs a depth first search for a certification
   * path while building forward which meets the requirements set in
   * the parameters object.
   * It uses an adjacency list to store all certificates which were
   * tried (i.e. at one time added to the path - they may not end up in
   * the final path if backtracking occurs). This information can
   * be used later to debug or demo the build.
   *
   * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
   * for an explanation of the DFS algorithm.
   *
   * @param dN the distinguished name being currently searched for certs
   * @param currentState the current PKIX validation state
   */
  private void depthFirstSearchForward(
      X500Principal dN,
      ForwardState currentState,
      ForwardBuilder builder,
      List<List<Vertex>> adjList,
      LinkedList<X509Certificate> cpList)
      throws GeneralSecurityException, IOException {
    if (debug != null) {
      debug.println(
          "SunCertPathBuilder.depthFirstSearchForward("
              + dN
              + ", "
              + currentState.toString()
              + ")");
    }

    /*
     * Find all the certificates issued to dN which
     * satisfy the PKIX certification path constraints.
     */
    Collection<X509Certificate> certs =
        builder.getMatchingCerts(currentState, buildParams.certStores());
    List<Vertex> vertices = addVertices(certs, adjList);
    if (debug != null) {
      debug.println(
          "SunCertPathBuilder.depthFirstSearchForward(): " + "certs.size=" + vertices.size());
    }

    /*
     * For each cert in the collection, verify anything
     * that hasn't been checked yet (signature, revocation, etc)
     * and check for loops. Call depthFirstSearchForward()
     * recursively for each good cert.
     */

    vertices:
    for (Vertex vertex : vertices) {
      /**
       * Restore state to currentState each time through the loop. This is important because some of
       * the user-defined checkers modify the state, which MUST be restored if the cert eventually
       * fails to lead to the target and the next matching cert is tried.
       */
      ForwardState nextState = (ForwardState) currentState.clone();
      X509Certificate cert = vertex.getCertificate();

      try {
        builder.verifyCert(cert, nextState, cpList);
      } catch (GeneralSecurityException gse) {
        if (debug != null) {
          debug.println(
              "SunCertPathBuilder.depthFirstSearchForward()" + ": validation failed: " + gse);
          gse.printStackTrace();
        }
        vertex.setThrowable(gse);
        continue;
      }

      /*
       * Certificate is good.
       * If cert completes the path,
       *    process userCheckers that don't support forward checking
       *    and process policies over whole path
       *    and backtrack appropriately if there is a failure
       * else if cert does not complete the path,
       *    add it to the path
       */
      if (builder.isPathCompleted(cert)) {

        if (debug != null)
          debug.println(
              "SunCertPathBuilder.depthFirstSearchForward()" + ": commencing final verification");

        List<X509Certificate> appendedCerts = new ArrayList<>(cpList);

        /*
         * if the trust anchor selected is specified as a trusted
         * public key rather than a trusted cert, then verify this
         * cert (which is signed by the trusted public key), but
         * don't add it yet to the cpList
         */
        if (builder.trustAnchor.getTrustedCert() == null) {
          appendedCerts.add(0, cert);
        }

        Set<String> initExpPolSet = Collections.singleton(PolicyChecker.ANY_POLICY);

        PolicyNodeImpl rootNode =
            new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);

        List<PKIXCertPathChecker> checkers = new ArrayList<>();
        PolicyChecker policyChecker =
            new PolicyChecker(
                buildParams.initialPolicies(),
                appendedCerts.size(),
                buildParams.explicitPolicyRequired(),
                buildParams.policyMappingInhibited(),
                buildParams.anyPolicyInhibited(),
                buildParams.policyQualifiersRejected(),
                rootNode);
        checkers.add(policyChecker);

        // add the algorithm checker
        checkers.add(new AlgorithmChecker(builder.trustAnchor));

        BasicChecker basicChecker = null;
        if (nextState.keyParamsNeeded()) {
          PublicKey rootKey = cert.getPublicKey();
          if (builder.trustAnchor.getTrustedCert() == null) {
            rootKey = builder.trustAnchor.getCAPublicKey();
            if (debug != null)
              debug.println(
                  "SunCertPathBuilder.depthFirstSearchForward "
                      + "using buildParams public key: "
                      + rootKey.toString());
          }
          TrustAnchor anchor = new TrustAnchor(cert.getSubjectX500Principal(), rootKey, null);

          // add the basic checker
          basicChecker =
              new BasicChecker(anchor, buildParams.date(), buildParams.sigProvider(), true);
          checkers.add(basicChecker);
        }

        buildParams.setCertPath(cf.generateCertPath(appendedCerts));

        boolean revCheckerAdded = false;
        List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers();
        for (PKIXCertPathChecker ckr : ckrs) {
          if (ckr instanceof PKIXRevocationChecker) {
            if (revCheckerAdded) {
              throw new CertPathValidatorException(
                  "Only one PKIXRevocationChecker can be specified");
            }
            revCheckerAdded = true;
            // if it's our own, initialize it
            if (ckr instanceof RevocationChecker) {
              ((RevocationChecker) ckr).init(builder.trustAnchor, buildParams);
            }
          }
        }
        // only add a RevocationChecker if revocation is enabled and
        // a PKIXRevocationChecker has not already been added
        if (buildParams.revocationEnabled() && !revCheckerAdded) {
          checkers.add(new RevocationChecker(builder.trustAnchor, buildParams));
        }

        checkers.addAll(ckrs);

        // Why we don't need BasicChecker and RevocationChecker
        // if nextState.keyParamsNeeded() is false?

        for (int i = 0; i < appendedCerts.size(); i++) {
          X509Certificate currCert = appendedCerts.get(i);
          if (debug != null)
            debug.println("current subject = " + currCert.getSubjectX500Principal());
          Set<String> unresCritExts = currCert.getCriticalExtensionOIDs();
          if (unresCritExts == null) {
            unresCritExts = Collections.<String>emptySet();
          }

          for (PKIXCertPathChecker currChecker : checkers) {
            if (!currChecker.isForwardCheckingSupported()) {
              if (i == 0) {
                currChecker.init(false);

                // The user specified
                // AlgorithmChecker may not be
                // able to set the trust anchor until now.
                if (currChecker instanceof AlgorithmChecker) {
                  ((AlgorithmChecker) currChecker).trySetTrustAnchor(builder.trustAnchor);
                }
              }

              try {
                currChecker.check(currCert, unresCritExts);
              } catch (CertPathValidatorException cpve) {
                if (debug != null)
                  debug.println(
                      "SunCertPathBuilder.depthFirstSearchForward(): "
                          + "final verification failed: "
                          + cpve);
                // If the target cert itself is revoked, we
                // cannot trust it. We can bail out here.
                if (buildParams.targetCertConstraints().match(currCert)
                    && cpve.getReason() == BasicReason.REVOKED) {
                  throw cpve;
                }
                vertex.setThrowable(cpve);
                continue vertices;
              }
            }
          }

          /*
           * Remove extensions from user checkers that support
           * forward checking. After this step, we will have
           * removed all extensions that all user checkers
           * are capable of processing.
           */
          for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) {
            if (checker.isForwardCheckingSupported()) {
              Set<String> suppExts = checker.getSupportedExtensions();
              if (suppExts != null) {
                unresCritExts.removeAll(suppExts);
              }
            }
          }

          if (!unresCritExts.isEmpty()) {
            unresCritExts.remove(BasicConstraints_Id.toString());
            unresCritExts.remove(NameConstraints_Id.toString());
            unresCritExts.remove(CertificatePolicies_Id.toString());
            unresCritExts.remove(PolicyMappings_Id.toString());
            unresCritExts.remove(PolicyConstraints_Id.toString());
            unresCritExts.remove(InhibitAnyPolicy_Id.toString());
            unresCritExts.remove(SubjectAlternativeName_Id.toString());
            unresCritExts.remove(KeyUsage_Id.toString());
            unresCritExts.remove(ExtendedKeyUsage_Id.toString());

            if (!unresCritExts.isEmpty()) {
              throw new CertPathValidatorException(
                  "unrecognized critical extension(s)",
                  null,
                  null,
                  -1,
                  PKIXReason.UNRECOGNIZED_CRIT_EXT);
            }
          }
        }
        if (debug != null)
          debug.println(
              "SunCertPathBuilder.depthFirstSearchForward()"
                  + ": final verification succeeded - path completed!");
        pathCompleted = true;

        /*
         * if the user specified a trusted public key rather than
         * trusted certs, then add this cert (which is signed by
         * the trusted public key) to the cpList
         */
        if (builder.trustAnchor.getTrustedCert() == null) builder.addCertToPath(cert, cpList);
        // Save the trust anchor
        this.trustAnchor = builder.trustAnchor;

        /*
         * Extract and save the final target public key
         */
        if (basicChecker != null) {
          finalPublicKey = basicChecker.getPublicKey();
        } else {
          Certificate finalCert;
          if (cpList.isEmpty()) {
            finalCert = builder.trustAnchor.getTrustedCert();
          } else {
            finalCert = cpList.getLast();
          }
          finalPublicKey = finalCert.getPublicKey();
        }

        policyTreeResult = policyChecker.getPolicyTree();
        return;
      } else {
        builder.addCertToPath(cert, cpList);
      }

      /* Update the PKIX state */
      nextState.updateState(cert);

      /*
       * Append an entry for cert in adjacency list and
       * set index for current vertex.
       */
      adjList.add(new LinkedList<Vertex>());
      vertex.setIndex(adjList.size() - 1);

      /* recursively search for matching certs at next dN */
      depthFirstSearchForward(cert.getIssuerX500Principal(), nextState, builder, adjList, cpList);

      /*
       * If path has been completed, return ASAP!
       */
      if (pathCompleted) {
        return;
      } else {
        /*
         * If we get here, it means we have searched all possible
         * certs issued by the dN w/o finding any matching certs.
         * This means we have to backtrack to the previous cert in
         * the path and try some other paths.
         */
        if (debug != null)
          debug.println("SunCertPathBuilder.depthFirstSearchForward()" + ": backtracking");
        builder.removeFinalCertFromPath(cpList);
      }
    }
  }
Exemplo n.º 27
0
/**
 * Describes one step of a certification path build, consisting of a <code>Vertex</code> state
 * description, a certificate, a possible throwable, and a result code.
 *
 * @author Anne Anderson
 * @since 1.4
 * @see sun.security.provider.certpath.Vertex
 */
public class BuildStep {

  private static final Debug debug = Debug.getInstance("certpath");
  private Vertex vertex;
  private X509Certificate cert;
  private Throwable throwable;
  private int result;

  /**
   * result code associated with a certificate that may continue a path from the current
   * certificate.
   */
  public static final int POSSIBLE = 1;

  /**
   * result code associated with a certificate that was tried, but that represents an unsuccessful
   * path, so the certificate has been backed out to allow backtracking to the next possible path.
   */
  public static final int BACK = 2;

  /**
   * result code associated with a certificate that successfully continues the current path, but
   * does not yet reach the target.
   */
  public static final int FOLLOW = 3;

  /**
   * result code associated with a certificate that represents the end of the last possible path,
   * where no path successfully reached the target.
   */
  public static final int FAIL = 4;

  /**
   * result code associated with a certificate that represents the end of a path that successfully
   * reaches the target.
   */
  public static final int SUCCEED = 5;

  /**
   * construct a BuildStep
   *
   * @param vtx description of the vertex at this step
   * @param res result, where result is one of POSSIBLE, BACK, FOLLOW, FAIL, SUCCEED
   */
  public BuildStep(Vertex vtx, int res) {
    vertex = vtx;
    if (vertex != null) {
      cert = (X509Certificate) vertex.getCertificate();
      throwable = vertex.getThrowable();
    }
    result = res;
  }

  /**
   * return vertex description for this build step
   *
   * @returns Vertex
   */
  public Vertex getVertex() {
    return vertex;
  }

  /**
   * return the certificate associated with this build step
   *
   * @returns X509Certificate
   */
  public X509Certificate getCertificate() {
    return cert;
  }

  /**
   * return string form of issuer name from certificate associated with this build step
   *
   * @returns String form of issuer name or null, if no certificate.
   */
  public String getIssuerName() {
    return (cert == null ? null : cert.getIssuerX500Principal().toString());
  }

  /**
   * return string form of issuer name from certificate associated with this build step, or a
   * default name if no certificate associated with this build step, or if issuer name could not be
   * obtained from the certificate.
   *
   * @param defaultName name to use as default if unable to return an issuer name from the
   *     certificate, or if no certificate.
   * @returns String form of issuer name or defaultName, if no certificate or exception received
   *     while trying to extract issuer name from certificate.
   */
  public String getIssuerName(String defaultName) {
    return (cert == null ? defaultName : cert.getIssuerX500Principal().toString());
  }

  /**
   * return string form of subject name from certificate associated with this build step.
   *
   * @returns String form of subject name or null, if no certificate.
   */
  public String getSubjectName() {
    return (cert == null ? null : cert.getSubjectX500Principal().toString());
  }

  /**
   * return string form of subject name from certificate associated with this build step, or a
   * default name if no certificate associated with this build step, or if subject name could not be
   * obtained from the certificate.
   *
   * @param defaultName name to use as default if unable to return a subject name from the
   *     certificate, or if no certificate.
   * @returns String form of subject name or defaultName, if no certificate or if an exception was
   *     received while attempting to extract the subject name from the certificate.
   */
  public String getSubjectName(String defaultName) {
    return (cert == null ? defaultName : cert.getSubjectX500Principal().toString());
  }

  /**
   * return the exception associated with this build step.
   *
   * @returns Throwable
   */
  public Throwable getThrowable() {
    return throwable;
  }

  /**
   * return the result code associated with this build step. The result codes are POSSIBLE, FOLLOW,
   * BACK, FAIL, SUCCEED.
   *
   * @returns int result code
   */
  public int getResult() {
    return result;
  }

  /**
   * return a string representing the meaning of the result code associated with this build step.
   *
   * @param res result code
   * @returns String string representing meaning of the result code
   */
  public String resultToString(int res) {
    String resultString = "";
    switch (res) {
      case BuildStep.POSSIBLE:
        resultString = "Certificate to be tried.\n";
        break;
      case BuildStep.BACK:
        resultString =
            "Certificate backed out since path does not " + "satisfy build requirements.\n";
        break;
      case BuildStep.FOLLOW:
        resultString = "Certificate satisfies conditions.\n";
        break;
      case BuildStep.FAIL:
        resultString = "Certificate backed out since path does not " + "satisfy conditions.\n";
        break;
      case BuildStep.SUCCEED:
        resultString = "Certificate satisfies conditions.\n";
        break;
      default:
        resultString = "Internal error: Invalid step result value.\n";
    }
    return resultString;
  }

  /**
   * return a string representation of this build step, showing minimal detail.
   *
   * @returns String
   */
  public String toString() {
    String out = "Internal Error\n";
    switch (result) {
      case BACK:
      case FAIL:
        out = resultToString(result);
        out = out + vertex.throwableToString();
        break;
      case FOLLOW:
      case SUCCEED:
      case POSSIBLE:
        out = resultToString(result);
        break;
      default:
        out = "Internal Error: Invalid step result\n";
    }
    return out;
  }

  /**
   * return a string representation of this build step, showing all detail of the vertex state
   * appropriate to the result of this build step, and the certificate contents.
   *
   * @returns String
   */
  public String verboseToString() {
    String out = resultToString(getResult());
    switch (result) {
      case BACK:
      case FAIL:
        out = out + vertex.throwableToString();
        break;
      case FOLLOW:
      case SUCCEED:
        out = out + vertex.moreToString();
        break;
      case POSSIBLE:
        break;
      default:
        break;
    }
    out = out + "Certificate contains:\n" + vertex.certToString();
    return out;
  }

  /**
   * return a string representation of this build step, including all possible detail of the vertex
   * state, but not including the certificate contents.
   *
   * @returns String
   */
  public String fullToString() {
    String out = resultToString(getResult());
    out = out + vertex.toString();
    return out;
  }
}
Exemplo n.º 28
0
/**
 * This class centralizes all security properties and common security methods. One of its primary
 * uses is to manage providers.
 *
 * @author Benjamin Renaud
 * @version 1.126, 05/18/04
 */
public final class Security {

  /* Are we debugging? -- for developers */
  private static final Debug sdebug = Debug.getInstance("properties");

  /* The java.security properties */
  private static Properties props;

  // An element in the cache
  private static class ProviderProperty {
    String className;
    Provider provider;
  }

  static {
    // doPrivileged here because there are multiple
    // things in initialize that might require privs.
    // (the FileInputStream call and the File.exists call,
    // the securityPropFile call, etc)
    AccessController.doPrivileged(
        new PrivilegedAction() {
          public Object run() {
            initialize();
            return null;
          }
        });
  }

  private static void initialize() {
    props = new Properties();
    boolean loadedProps = false;
    boolean overrideAll = false;

    // first load the system properties file
    // to determine the value of security.overridePropertiesFile
    File propFile = securityPropFile("java.security");
    if (propFile.exists()) {
      try {
        FileInputStream fis = new FileInputStream(propFile);
        InputStream is = new BufferedInputStream(fis);
        props.load(is);
        is.close();
        loadedProps = true;

        if (sdebug != null) {
          sdebug.println("reading security properties file: " + propFile);
        }
      } catch (IOException e) {
        if (sdebug != null) {
          sdebug.println("unable to load security properties from " + propFile);
          e.printStackTrace();
        }
      }
    }

    if ("true".equalsIgnoreCase(props.getProperty("security.overridePropertiesFile"))) {

      String extraPropFile = System.getProperty("java.security.properties");
      if (extraPropFile != null && extraPropFile.startsWith("=")) {
        overrideAll = true;
        extraPropFile = extraPropFile.substring(1);
      }

      if (overrideAll) {
        props = new Properties();
        if (sdebug != null) {
          sdebug.println("overriding other security properties files!");
        }
      }

      // now load the user-specified file so its values
      // will win if they conflict with the earlier values
      if (extraPropFile != null) {
        try {
          URL propURL;

          extraPropFile = PropertyExpander.expand(extraPropFile);
          propFile = new File(extraPropFile);
          if (propFile.exists()) {
            propURL = new URL("file:" + propFile.getCanonicalPath());
          } else {
            propURL = new URL(extraPropFile);
          }
          BufferedInputStream bis = new BufferedInputStream(propURL.openStream());
          props.load(bis);
          bis.close();
          loadedProps = true;

          if (sdebug != null) {
            sdebug.println("reading security properties file: " + propURL);
            if (overrideAll) {
              sdebug.println("overriding other security properties files!");
            }
          }
        } catch (Exception e) {
          if (sdebug != null) {
            sdebug.println("unable to load security properties from " + extraPropFile);
            e.printStackTrace();
          }
        }
      }
    }

    if (!loadedProps) {
      initializeStatic();
      if (sdebug != null) {
        sdebug.println("unable to load security properties " + "-- using defaults");
      }
    }
  }

  /*
   * Initialize to default values, if <java.home>/lib/java.security
   * is not found.
   */
  private static void initializeStatic() {
    props.put("security.provider.1", "sun.security.provider.Sun");
    props.put("security.provider.2", "sun.security.rsa.SunRsaSign");
    props.put("security.provider.3", "com.sun.net.ssl.internal.ssl.Provider");
    props.put("security.provider.4", "com.sun.crypto.provider.SunJCE");
    props.put("security.provider.5", "sun.security.jgss.SunProvider");
    props.put("security.provider.6", "com.sun.security.sasl.Provider");
  }

  /** Don't let anyone instantiate this. */
  private Security() {}

  private static File securityPropFile(String filename) {
    // maybe check for a system property which will specify where to
    // look. Someday.
    String sep = File.separator;
    return new File(
        System.getProperty("java.home") + sep + "lib" + sep + "security" + sep + filename);
  }

  /**
   * Looks up providers, and returns the property (and its associated provider) mapping the key, if
   * any. The order in which the providers are looked up is the provider-preference order, as
   * specificed in the security properties file.
   */
  private static ProviderProperty getProviderProperty(String key) {
    ProviderProperty entry = null;

    List providers = Providers.getProviderList().providers();
    for (int i = 0; i < providers.size(); i++) {

      String matchKey = null;
      Provider prov = (Provider) providers.get(i);
      String prop = prov.getProperty(key);

      if (prop == null) {
        // Is there a match if we do a case-insensitive property name
        // comparison? Let's try ...
        for (Enumeration e = prov.keys(); e.hasMoreElements() && prop == null; ) {
          matchKey = (String) e.nextElement();
          if (key.equalsIgnoreCase(matchKey)) {
            prop = prov.getProperty(matchKey);
            break;
          }
        }
      }

      if (prop != null) {
        ProviderProperty newEntry = new ProviderProperty();
        newEntry.className = prop;
        newEntry.provider = prov;
        return newEntry;
      }
    }

    return entry;
  }

  /** Returns the property (if any) mapping the key for the given provider. */
  private static String getProviderProperty(String key, Provider provider) {
    String prop = provider.getProperty(key);
    if (prop == null) {
      // Is there a match if we do a case-insensitive property name
      // comparison? Let's try ...
      for (Enumeration e = provider.keys(); e.hasMoreElements() && prop == null; ) {
        String matchKey = (String) e.nextElement();
        if (key.equalsIgnoreCase(matchKey)) {
          prop = provider.getProperty(matchKey);
          break;
        }
      }
    }
    return prop;
  }

  /**
   * Gets a specified property for an algorithm. The algorithm name should be a standard name. See
   * Appendix A in the <a href= "../../../guide/security/CryptoSpec.html#AppA"> Java Cryptography
   * Architecture API Specification &amp; Reference </a> for information about standard algorithm
   * names. One possible use is by specialized algorithm parsers, which may map classes to
   * algorithms which they understand (much like Key parsers do).
   *
   * @param algName the algorithm name.
   * @param propName the name of the property to get.
   * @return the value of the specified property.
   * @deprecated This method used to return the value of a proprietary property in the master file
   *     of the "SUN" Cryptographic Service Provider in order to determine how to parse
   *     algorithm-specific parameters. Use the new provider-based and algorithm-independent <code>
   *     AlgorithmParameters</code> and <code>KeyFactory</code> engine classes (introduced in the
   *     Java 2 platform) instead.
   */
  @Deprecated
  public static String getAlgorithmProperty(String algName, String propName) {
    ProviderProperty entry = getProviderProperty("Alg." + propName + "." + algName);
    if (entry != null) {
      return entry.className;
    } else {
      return null;
    }
  }

  /**
   * Adds a new provider, at a specified position. The position is the preference order in which
   * providers are searched for requested algorithms. Note that it is not guaranteed that this
   * preference will be respected. The position is 1-based, that is, 1 is most preferred, followed
   * by 2, and so on.
   *
   * <p>If the given provider is installed at the requested position, the provider that used to be
   * at that position, and all providers with a position greater than <code>position</code>, are
   * shifted up one position (towards the end of the list of installed providers).
   *
   * <p>A provider cannot be added if it is already installed.
   *
   * <p>First, if there is a security manager, its <code>checkSecurityAccess</code> method is called
   * with the string <code>"insertProvider."+provider.getName()</code> to see if it's ok to add a
   * new provider. If the default implementation of <code>checkSecurityAccess</code> is used (i.e.,
   * that method is not overriden), then this will result in a call to the security manager's <code>
   * checkPermission</code> method with a <code>
   * SecurityPermission("insertProvider."+provider.getName())</code> permission.
   *
   * @param provider the provider to be added.
   * @param position the preference position that the caller would like for this provider.
   * @return the actual preference position in which the provider was added, or -1 if the provider
   *     was not added because it is already installed.
   * @throws NullPointerException if provider is null
   * @throws SecurityException if a security manager exists and its <code>{@link
   *          java.lang.SecurityManager#checkSecurityAccess}</code> method denies access to add a
   *     new provider
   * @see #getProvider
   * @see #removeProvider
   * @see java.security.SecurityPermission
   */
  public static synchronized int insertProviderAt(Provider provider, int position) {
    String providerName = provider.getName();
    check("insertProvider." + providerName);
    ProviderList list = Providers.getFullProviderList();
    ProviderList newList = ProviderList.insertAt(list, provider, position - 1);
    if (list == newList) {
      return -1;
    }
    Providers.setProviderList(newList);
    return newList.getIndex(providerName) + 1;
  }

  /**
   * Adds a provider to the next position available.
   *
   * <p>First, if there is a security manager, its <code>checkSecurityAccess</code> method is called
   * with the string <code>"insertProvider."+provider.getName()</code> to see if it's ok to add a
   * new provider. If the default implementation of <code>checkSecurityAccess</code> is used (i.e.,
   * that method is not overriden), then this will result in a call to the security manager's <code>
   * checkPermission</code> method with a <code>
   * SecurityPermission("insertProvider."+provider.getName())</code> permission.
   *
   * @param provider the provider to be added.
   * @return the preference position in which the provider was added, or -1 if the provider was not
   *     added because it is already installed.
   * @throws NullPointerException if provider is null
   * @throws SecurityException if a security manager exists and its <code>{@link
   *          java.lang.SecurityManager#checkSecurityAccess}</code> method denies access to add a
   *     new provider
   * @see #getProvider
   * @see #removeProvider
   * @see java.security.SecurityPermission
   */
  public static int addProvider(Provider provider) {
    /*
     * We can't assign a position here because the statically
     * registered providers may not have been installed yet.
     * insertProviderAt() will fix that value after it has
     * loaded the static providers.
     */
    return insertProviderAt(provider, 0);
  }

  /**
   * Removes the provider with the specified name.
   *
   * <p>When the specified provider is removed, all providers located at a position greater than
   * where the specified provider was are shifted down one position (towards the head of the list of
   * installed providers).
   *
   * <p>This method returns silently if the provider is not installed or if name is null.
   *
   * <p>First, if there is a security manager, its <code>checkSecurityAccess</code> method is called
   * with the string <code>"removeProvider."+name</code> to see if it's ok to remove the provider.
   * If the default implementation of <code>checkSecurityAccess</code> is used (i.e., that method is
   * not overriden), then this will result in a call to the security manager's <code>checkPermission
   * </code> method with a <code>SecurityPermission("removeProvider."+name)</code> permission.
   *
   * @param name the name of the provider to remove.
   * @throws SecurityException if a security manager exists and its <code>{@link
   *          java.lang.SecurityManager#checkSecurityAccess}</code> method denies access to remove
   *     the provider
   * @see #getProvider
   * @see #addProvider
   */
  public static synchronized void removeProvider(String name) {
    check("removeProvider." + name);
    ProviderList list = Providers.getFullProviderList();
    ProviderList newList = ProviderList.remove(list, name);
    Providers.setProviderList(newList);
  }

  /**
   * Returns an array containing all the installed providers. The order of the providers in the
   * array is their preference order.
   *
   * @return an array of all the installed providers.
   */
  public static Provider[] getProviders() {
    return Providers.getFullProviderList().toArray();
  }

  /**
   * Returns the provider installed with the specified name, if any. Returns null if no provider
   * with the specified name is installed or if name is null.
   *
   * @param name the name of the provider to get.
   * @return the provider of the specified name.
   * @see #removeProvider
   * @see #addProvider
   */
  public static Provider getProvider(String name) {
    return Providers.getProviderList().getProvider(name);
  }

  /**
   * Returns an array containing all installed providers that satisfy the specified selection
   * criterion, or null if no such providers have been installed. The returned providers are ordered
   * according to their <a href= "#insertProviderAt(java.security.Provider, int)">preference
   * order</a>.
   *
   * <p>A cryptographic service is always associated with a particular algorithm or type. For
   * example, a digital signature service is always associated with a particular algorithm (e.g.,
   * DSA), and a CertificateFactory service is always associated with a particular certificate type
   * (e.g., X.509).
   *
   * <p>The selection criterion must be specified in one of the following two formats:
   *
   * <ul>
   *   <li><i>&lt;crypto_service>.&lt;algorithm_or_type></i>
   *       <p>The cryptographic service name must not contain any dots.
   *       <p>A provider satisfies the specified selection criterion iff the provider implements the
   *       specified algorithm or type for the specified cryptographic service.
   *       <p>For example, "CertificateFactory.X.509" would be satisfied by any provider that
   *       supplied a CertificateFactory implementation for X.509 certificates.
   *   <li><i>&lt;crypto_service>.&lt;algorithm_or_type> &lt;attribute_name>:&lt
   *       attribute_value></i>
   *       <p>The cryptographic service name must not contain any dots. There must be one or more
   *       space charaters between the the <i>&lt;algorithm_or_type></i> and the
   *       <i>&lt;attribute_name></i>.
   *       <p>A provider satisfies this selection criterion iff the provider implements the
   *       specified algorithm or type for the specified cryptographic service and its
   *       implementation meets the constraint expressed by the specified attribute name/value pair.
   *       <p>For example, "Signature.SHA1withDSA KeySize:1024" would be satisfied by any provider
   *       that implemented the SHA1withDSA signature algorithm with a keysize of 1024 (or larger).
   * </ul>
   *
   * <p>See Appendix A in the <a href= "../../../guide/security/CryptoSpec.html#AppA"> Java
   * Cryptogaphy Architecture API Specification &amp; Reference </a> for information about standard
   * cryptographic service names, standard algorithm names and standard attribute names.
   *
   * @param filter the criterion for selecting providers. The filter is case-insensitive.
   * @return all the installed providers that satisfy the selection criterion, or null if no such
   *     providers have been installed.
   * @throws InvalidParameterException if the filter is not in the required format
   * @throws NullPointerException if filter is null
   * @see #getProviders(java.util.Map)
   */
  public static Provider[] getProviders(String filter) {
    String key = null;
    String value = null;
    int index = filter.indexOf(':');

    if (index == -1) {
      key = filter;
      value = "";
    } else {
      key = filter.substring(0, index);
      value = filter.substring(index + 1);
    }

    Hashtable hashtableFilter = new Hashtable(1);
    hashtableFilter.put(key, value);

    return (getProviders(hashtableFilter));
  }

  /**
   * Returns an array containing all installed providers that satisfy the specified* selection
   * criteria, or null if no such providers have been installed. The returned providers are ordered
   * according to their <a href= "#insertProviderAt(java.security.Provider, int)">preference
   * order</a>.
   *
   * <p>The selection criteria are represented by a map. Each map entry represents a selection
   * criterion. A provider is selected iff it satisfies all selection criteria. The key for any
   * entry in such a map must be in one of the following two formats:
   *
   * <ul>
   *   <li><i>&lt;crypto_service>.&lt;algorithm_or_type></i>
   *       <p>The cryptographic service name must not contain any dots.
   *       <p>The value associated with the key must be an empty string.
   *       <p>A provider satisfies this selection criterion iff the provider implements the
   *       specified algorithm or type for the specified cryptographic service.
   *   <li><i>&lt;crypto_service>.&lt;algorithm_or_type> &lt;attribute_name></i>
   *       <p>The cryptographic service name must not contain any dots. There must be one or more
   *       space charaters between the <i>&lt;algorithm_or_type></i> and the
   *       <i>&lt;attribute_name></i>.
   *       <p>The value associated with the key must be a non-empty string. A provider satisfies
   *       this selection criterion iff the provider implements the specified algorithm or type for
   *       the specified cryptographic service and its implementation meets the constraint expressed
   *       by the specified attribute name/value pair.
   * </ul>
   *
   * <p>See Appendix A in the <a href= "../../../guide/security/CryptoSpec.html#AppA"> Java
   * Cryptogaphy Architecture API Specification &amp; Reference </a> for information about standard
   * cryptographic service names, standard algorithm names and standard attribute names.
   *
   * @param filter the criteria for selecting providers. The filter is case-insensitive.
   * @return all the installed providers that satisfy the selection criteria, or null if no such
   *     providers have been installed.
   * @throws InvalidParameterException if the filter is not in the required format
   * @throws NullPointerException if filter is null
   * @see #getProviders(java.lang.String)
   */
  public static Provider[] getProviders(Map<String, String> filter) {
    // Get all installed providers first.
    // Then only return those providers who satisfy the selection criteria.
    Provider[] allProviders = Security.getProviders();
    Set keySet = filter.keySet();
    LinkedHashSet candidates = new LinkedHashSet(5);

    // Returns all installed providers
    // if the selection criteria is null.
    if ((keySet == null) || (allProviders == null)) {
      return allProviders;
    }

    boolean firstSearch = true;

    // For each selection criterion, remove providers
    // which don't satisfy the criterion from the candidate set.
    for (Iterator ite = keySet.iterator(); ite.hasNext(); ) {
      String key = (String) ite.next();
      String value = (String) filter.get(key);

      LinkedHashSet newCandidates = getAllQualifyingCandidates(key, value, allProviders);
      if (firstSearch) {
        candidates = newCandidates;
        firstSearch = false;
      }

      if ((newCandidates != null) && !newCandidates.isEmpty()) {
        // For each provider in the candidates set, if it
        // isn't in the newCandidate set, we should remove
        // it from the candidate set.
        for (Iterator cansIte = candidates.iterator(); cansIte.hasNext(); ) {
          Provider prov = (Provider) cansIte.next();
          if (!newCandidates.contains(prov)) {
            cansIte.remove();
          }
        }
      } else {
        candidates = null;
        break;
      }
    }

    if ((candidates == null) || (candidates.isEmpty())) return null;

    Object[] candidatesArray = candidates.toArray();
    Provider[] result = new Provider[candidatesArray.length];

    for (int i = 0; i < result.length; i++) {
      result[i] = (Provider) candidatesArray[i];
    }

    return result;
  }

  // Map containing cached Spi Class objects of the specified type
  private static final Map<String, Class> spiMap = new ConcurrentHashMap<String, Class>();

  /**
   * Return the Class object for the given engine type (e.g. "MessageDigest"). Works for Spis in the
   * java.security package only.
   */
  private static Class getSpiClass(String type) {
    Class clazz = spiMap.get(type);
    if (clazz != null) {
      return clazz;
    }
    try {
      clazz = Class.forName("java.security." + type + "Spi");
      spiMap.put(type, clazz);
      return clazz;
    } catch (ClassNotFoundException e) {
      throw (Error) new AssertionError("Spi class not found").initCause(e);
    }
  }

  /*
   * Returns an array of objects: the first object in the array is
   * an instance of an implementation of the requested algorithm
   * and type, and the second object in the array identifies the provider
   * of that implementation.
   * The <code>provider</code> argument can be null, in which case all
   * configured providers will be searched in order of preference.
   */
  static Object[] getImpl(String algorithm, String type, String provider)
      throws NoSuchAlgorithmException, NoSuchProviderException {
    if (provider == null) {
      return GetInstance.getInstance(type, getSpiClass(type), algorithm).toArray();
    } else {
      return GetInstance.getInstance(type, getSpiClass(type), algorithm, provider).toArray();
    }
  }

  static Object[] getImpl(String algorithm, String type, String provider, Object params)
      throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
    if (provider == null) {
      return GetInstance.getInstance(type, getSpiClass(type), algorithm, params).toArray();
    } else {
      return GetInstance.getInstance(type, getSpiClass(type), algorithm, params, provider)
          .toArray();
    }
  }

  /*
   * Returns an array of objects: the first object in the array is
   * an instance of an implementation of the requested algorithm
   * and type, and the second object in the array identifies the provider
   * of that implementation.
   * The <code>provider</code> argument cannot be null.
   */
  static Object[] getImpl(String algorithm, String type, Provider provider)
      throws NoSuchAlgorithmException {
    return GetInstance.getInstance(type, getSpiClass(type), algorithm, provider).toArray();
  }

  static Object[] getImpl(String algorithm, String type, Provider provider, Object params)
      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
    return GetInstance.getInstance(type, getSpiClass(type), algorithm, params, provider).toArray();
  }

  /**
   * Gets a security property value.
   *
   * <p>First, if there is a security manager, its <code>checkPermission</code> method is called
   * with a <code>java.security.SecurityPermission("getProperty."+key)</code> permission to see if
   * it's ok to retrieve the specified security property value..
   *
   * @param key the key of the property being retrieved.
   * @return the value of the security property corresponding to key.
   * @throws SecurityException if a security manager exists and its <code>{@link
   *          java.lang.SecurityManager#checkPermission}</code> method denies access to retrieve the
   *     specified security property value
   * @throws NullPointerException is key is null
   * @see #setProperty
   * @see java.security.SecurityPermission
   */
  public static String getProperty(String key) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new SecurityPermission("getProperty." + key));
    }
    String name = props.getProperty(key);
    if (name != null) name = name.trim(); // could be a class name with trailing ws
    return name;
  }

  /**
   * Sets a security property value.
   *
   * <p>First, if there is a security manager, its <code>checkPermission</code> method is called
   * with a <code>java.security.SecurityPermission("setProperty."+key)</code> permission to see if
   * it's ok to set the specified security property value.
   *
   * @param key the name of the property to be set.
   * @param datum the value of the property to be set.
   * @throws SecurityException if a security manager exists and its <code>{@link
   *          java.lang.SecurityManager#checkPermission}</code> method denies access to set the
   *     specified security property value
   * @throws NullPointerException if key or datum is null
   * @see #getProperty
   * @see java.security.SecurityPermission
   */
  public static void setProperty(String key, String datum) {
    check("setProperty." + key);
    props.put(key, datum);
    invalidateSMCache(key); /* See below. */
  }

  /*
   * Implementation detail:  If the property we just set in
   * setProperty() was either "package.access" or
   * "package.definition", we need to signal to the SecurityManager
   * class that the value has just changed, and that it should
   * invalidate it's local cache values.
   *
   * Rather than create a new API entry for this function,
   * we use reflection to set a private variable.
   */
  private static void invalidateSMCache(String key) {

    final boolean pa = key.equals("package.access");
    final boolean pd = key.equals("package.definition");

    if (pa || pd) {
      AccessController.doPrivileged(
          new PrivilegedAction() {
            public Object run() {
              try {
                /* Get the class via the bootstrap class loader. */
                Class cl = Class.forName("java.lang.SecurityManager", false, null);
                Field f = null;
                boolean accessible = false;

                if (pa) {
                  f = cl.getDeclaredField("packageAccessValid");
                  accessible = f.isAccessible();
                  f.setAccessible(true);
                } else {
                  f = cl.getDeclaredField("packageDefinitionValid");
                  accessible = f.isAccessible();
                  f.setAccessible(true);
                }
                f.setBoolean(f, false);
                f.setAccessible(accessible);
              } catch (Exception e1) {
                /* If we couldn't get the class, it hasn't
                 * been loaded yet.  If there is no such
                 * field, we shouldn't try to set it.  There
                 * shouldn't be a security execption, as we
                 * are loaded by boot class loader, and we
                 * are inside a doPrivileged() here.
                 *
                 * NOOP: don't do anything...
                 */
              }
              return null;
            } /* run */
          }); /* PrivilegedAction */
    } /* if */
  }

  private static void check(String directive) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
      security.checkSecurityAccess(directive);
    }
  }

  /*
   * Returns all providers who satisfy the specified
   * criterion.
   */
  private static LinkedHashSet getAllQualifyingCandidates(
      String filterKey, String filterValue, Provider[] allProviders) {
    String[] filterComponents = getFilterComponents(filterKey, filterValue);

    // The first component is the service name.
    // The second is the algorithm name.
    // If the third isn't null, that is the attrinute name.
    String serviceName = filterComponents[0];
    String algName = filterComponents[1];
    String attrName = filterComponents[2];

    return getProvidersNotUsingCache(serviceName, algName, attrName, filterValue, allProviders);
  }

  private static LinkedHashSet getProvidersNotUsingCache(
      String serviceName,
      String algName,
      String attrName,
      String filterValue,
      Provider[] allProviders) {
    LinkedHashSet candidates = new LinkedHashSet(5);
    for (int i = 0; i < allProviders.length; i++) {
      if (isCriterionSatisfied(allProviders[i], serviceName, algName, attrName, filterValue)) {
        candidates.add(allProviders[i]);
      }
    }
    return candidates;
  }

  /*
   * Returns true if the given provider satisfies
   * the selection criterion key:value.
   */
  private static boolean isCriterionSatisfied(
      Provider prov, String serviceName, String algName, String attrName, String filterValue) {
    String key = serviceName + '.' + algName;

    if (attrName != null) {
      key += ' ' + attrName;
    }
    // Check whether the provider has a property
    // whose key is the same as the given key.
    String propValue = getProviderProperty(key, prov);

    if (propValue == null) {
      // Check whether we have an alias instead
      // of a standard name in the key.
      String standardName = getProviderProperty("Alg.Alias." + serviceName + "." + algName, prov);
      if (standardName != null) {
        key = serviceName + "." + standardName;

        if (attrName != null) {
          key += ' ' + attrName;
        }

        propValue = getProviderProperty(key, prov);
      }

      if (propValue == null) {
        // The provider doesn't have the given
        // key in its property list.
        return false;
      }
    }

    // If the key is in the format of:
    // <crypto_service>.<algorithm_or_type>,
    // there is no need to check the value.

    if (attrName == null) {
      return true;
    }

    // If we get here, the key must be in the
    // format of <crypto_service>.<algorithm_or_provider> <attribute_name>.
    if (isStandardAttr(attrName)) {
      return isConstraintSatisfied(attrName, filterValue, propValue);
    } else {
      return filterValue.equalsIgnoreCase(propValue);
    }
  }

  /*
   * Returns true if the attribute is a standard attribute;
   * otherwise, returns false.
   */
  private static boolean isStandardAttr(String attribute) {
    // For now, we just have two standard attributes:
    // KeySize and ImplementedIn.
    if (attribute.equalsIgnoreCase("KeySize")) return true;

    if (attribute.equalsIgnoreCase("ImplementedIn")) return true;

    return false;
  }

  /*
   * Returns true if the requested attribute value is supported;
   * otherwise, returns false.
   */
  private static boolean isConstraintSatisfied(String attribute, String value, String prop) {
    // For KeySize, prop is the max key size the
    // provider supports for a specific <crypto_service>.<algorithm>.
    if (attribute.equalsIgnoreCase("KeySize")) {
      int requestedSize = Integer.parseInt(value);
      int maxSize = Integer.parseInt(prop);
      if (requestedSize <= maxSize) {
        return true;
      } else {
        return false;
      }
    }

    // For Type, prop is the type of the implementation
    // for a specific <crypto service>.<algorithm>.
    if (attribute.equalsIgnoreCase("ImplementedIn")) {
      return value.equalsIgnoreCase(prop);
    }

    return false;
  }

  static String[] getFilterComponents(String filterKey, String filterValue) {
    int algIndex = filterKey.indexOf('.');

    if (algIndex < 0) {
      // There must be a dot in the filter, and the dot
      // shouldn't be at the beginning of this string.
      throw new InvalidParameterException("Invalid filter");
    }

    String serviceName = filterKey.substring(0, algIndex);
    String algName = null;
    String attrName = null;

    if (filterValue.length() == 0) {
      // The filterValue is an empty string. So the filterKey
      // should be in the format of <crypto_service>.<algorithm_or_type>.
      algName = filterKey.substring(algIndex + 1).trim();
      if (algName.length() == 0) {
        // There must be a algorithm or type name.
        throw new InvalidParameterException("Invalid filter");
      }
    } else {
      // The filterValue is a non-empty string. So the filterKey must be
      // in the format of
      // <crypto_service>.<algorithm_or_type> <attribute_name>
      int attrIndex = filterKey.indexOf(' ');

      if (attrIndex == -1) {
        // There is no attribute name in the filter.
        throw new InvalidParameterException("Invalid filter");
      } else {
        attrName = filterKey.substring(attrIndex + 1).trim();
        if (attrName.length() == 0) {
          // There is no attribute name in the filter.
          throw new InvalidParameterException("Invalid filter");
        }
      }

      // There must be an algorithm name in the filter.
      if ((attrIndex < algIndex) || (algIndex == attrIndex - 1)) {
        throw new InvalidParameterException("Invalid filter");
      } else {
        algName = filterKey.substring(algIndex + 1, attrIndex);
      }
    }

    String[] result = new String[3];
    result[0] = serviceName;
    result[1] = algName;
    result[2] = attrName;

    return result;
  }

  /**
   * Returns a Set of Strings containing the names of all available algorithms or types for the
   * specified Java cryptographic service (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore).
   * Returns an empty Set if there is no provider that supports the specified service or if
   * serviceName is null. For a complete list of Java cryptographic services, please see the <a
   * href="../../../guide/security/CryptoSpec.html">Java Cryptography Architecture API Specification
   * &amp; Reference</a>. Note: the returned set is immutable.
   *
   * @param serviceName the name of the Java cryptographic service (e.g., Signature, MessageDigest,
   *     Cipher, Mac, KeyStore). Note: this parameter is case-insensitive.
   * @return a Set of Strings containing the names of all available algorithms or types for the
   *     specified Java cryptographic service or an empty set if no provider supports the specified
   *     service.
   * @since 1.4
   */
  public static Set<String> getAlgorithms(String serviceName) {

    if ((serviceName == null) || (serviceName.length() == 0) || (serviceName.endsWith("."))) {
      return Collections.EMPTY_SET;
    }

    HashSet result = new HashSet();
    Provider[] providers = Security.getProviders();

    for (int i = 0; i < providers.length; i++) {
      // Check the keys for each provider.
      for (Enumeration e = providers[i].keys(); e.hasMoreElements(); ) {
        String currentKey = ((String) e.nextElement()).toUpperCase();
        if (currentKey.startsWith(serviceName.toUpperCase())) {
          // We should skip the currentKey if it contains a
          // whitespace. The reason is: such an entry in the
          // provider property contains attributes for the
          // implementation of an algorithm. We are only interested
          // in entries which lead to the implementation
          // classes.
          if (currentKey.indexOf(" ") < 0) {
            result.add(currentKey.substring(serviceName.length() + 1));
          }
        }
      }
    }
    return Collections.unmodifiableSet(result);
  }
}
Exemplo n.º 29
0
  /**
   * 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);
    }
  }
Exemplo n.º 30
0
/**
 * This class builds certification paths in the forward direction.
 *
 * <p>If successful, it returns a certification path which has successfully satisfied all the
 * constraints and requirements specified in the PKIXBuilderParameters object and has been validated
 * according to the PKIX path validation algorithm defined in RFC 3280.
 *
 * <p>This implementation uses a depth-first search approach to finding certification paths. If it
 * comes to a point in which it cannot find any more certificates leading to the target OR the path
 * length is too long it backtracks to previous paths until the target has been found or all
 * possible paths have been exhausted.
 *
 * <p>This implementation is not thread-safe.
 *
 * @since 1.4
 * @author Sean Mullan
 * @author Yassir Elley
 */
public final class SunCertPathBuilder extends CertPathBuilderSpi {

  private static final Debug debug = Debug.getInstance("certpath");

  /*
   * private objects shared by methods
   */
  private BuilderParams buildParams;
  private CertificateFactory cf;
  private boolean pathCompleted = false;
  private PolicyNode policyTreeResult;
  private TrustAnchor trustAnchor;
  private PublicKey finalPublicKey;

  /**
   * Create an instance of <code>SunCertPathBuilder</code>.
   *
   * @throws CertPathBuilderException if an error occurs
   */
  public SunCertPathBuilder() throws CertPathBuilderException {
    try {
      cf = CertificateFactory.getInstance("X.509");
    } catch (CertificateException e) {
      throw new CertPathBuilderException(e);
    }
  }

  @Override
  public CertPathChecker engineGetRevocationChecker() {
    return new RevocationChecker();
  }

  /**
   * Attempts to build a certification path using the Sun build algorithm from a trusted anchor(s)
   * to a target subject, which must both be specified in the input parameter set. This method will
   * attempt to build in the forward direction: from the target to the CA.
   *
   * <p>The certification path that is constructed is validated according to the PKIX specification.
   *
   * @param params the parameter set for building a path. Must be an instance of <code>
   *     PKIXBuilderParameters</code>.
   * @return a certification path builder result.
   * @exception CertPathBuilderException Exception thrown if builder is unable to build a complete
   *     certification path from the trusted anchor(s) to the target subject.
   * @throws InvalidAlgorithmParameterException if the given parameters are inappropriate for this
   *     certification path builder.
   */
  @Override
  public CertPathBuilderResult engineBuild(CertPathParameters params)
      throws CertPathBuilderException, InvalidAlgorithmParameterException {

    if (debug != null) {
      debug.println("SunCertPathBuilder.engineBuild(" + params + ")");
    }

    buildParams = PKIX.checkBuilderParams(params);
    return build();
  }

  private PKIXCertPathBuilderResult build() throws CertPathBuilderException {
    List<List<Vertex>> adjList = new ArrayList<>();
    PKIXCertPathBuilderResult result = buildCertPath(false, adjList);
    if (result == null) {
      if (debug != null) {
        debug.println(
            "SunCertPathBuilder.engineBuild: 2nd pass; "
                + "try building again searching all certstores");
      }
      // try again
      adjList.clear();
      result = buildCertPath(true, adjList);
      if (result == null) {
        throw new SunCertPathBuilderException(
            "unable to find valid " + "certification path to requested target",
            new AdjacencyList(adjList));
      }
    }
    return result;
  }

  private PKIXCertPathBuilderResult buildCertPath(
      boolean searchAllCertStores, List<List<Vertex>> adjList) throws CertPathBuilderException {
    // Init shared variables and build certification path
    pathCompleted = false;
    trustAnchor = null;
    finalPublicKey = null;
    policyTreeResult = null;
    LinkedList<X509Certificate> certPathList = new LinkedList<>();
    try {
      buildForward(adjList, certPathList, searchAllCertStores);
    } catch (GeneralSecurityException | IOException e) {
      if (debug != null) {
        debug.println("SunCertPathBuilder.engineBuild() exception in " + "build");
        e.printStackTrace();
      }
      throw new SunCertPathBuilderException(
          "unable to find valid " + "certification path to requested target",
          e,
          new AdjacencyList(adjList));
    }

    // construct SunCertPathBuilderResult
    try {
      if (pathCompleted) {
        if (debug != null) debug.println("SunCertPathBuilder.engineBuild() " + "pathCompleted");

        // we must return a certpath which has the target
        // as the first cert in the certpath - i.e. reverse
        // the certPathList
        Collections.reverse(certPathList);

        return new SunCertPathBuilderResult(
            cf.generateCertPath(certPathList),
            trustAnchor,
            policyTreeResult,
            finalPublicKey,
            new AdjacencyList(adjList));
      }
    } catch (CertificateException e) {
      if (debug != null) {
        debug.println("SunCertPathBuilder.engineBuild() exception " + "in wrap-up");
        e.printStackTrace();
      }
      throw new SunCertPathBuilderException(
          "unable to find valid " + "certification path to requested target",
          e,
          new AdjacencyList(adjList));
    }

    return null;
  }

  /*
   * Private build forward method.
   */
  private void buildForward(
      List<List<Vertex>> adjacencyList,
      LinkedList<X509Certificate> certPathList,
      boolean searchAllCertStores)
      throws GeneralSecurityException, IOException {
    if (debug != null) {
      debug.println("SunCertPathBuilder.buildForward()...");
    }

    /* Initialize current state */
    ForwardState currentState = new ForwardState();
    currentState.initState(buildParams.certPathCheckers());

    /* Initialize adjacency list */
    adjacencyList.clear();
    adjacencyList.add(new LinkedList<Vertex>());

    currentState.untrustedChecker = new UntrustedChecker();

    depthFirstSearchForward(
        buildParams.targetSubject(),
        currentState,
        new ForwardBuilder(buildParams, searchAllCertStores),
        adjacencyList,
        certPathList);
  }

  /*
   * This method performs a depth first search for a certification
   * path while building forward which meets the requirements set in
   * the parameters object.
   * It uses an adjacency list to store all certificates which were
   * tried (i.e. at one time added to the path - they may not end up in
   * the final path if backtracking occurs). This information can
   * be used later to debug or demo the build.
   *
   * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
   * for an explanation of the DFS algorithm.
   *
   * @param dN the distinguished name being currently searched for certs
   * @param currentState the current PKIX validation state
   */
  private void depthFirstSearchForward(
      X500Principal dN,
      ForwardState currentState,
      ForwardBuilder builder,
      List<List<Vertex>> adjList,
      LinkedList<X509Certificate> cpList)
      throws GeneralSecurityException, IOException {
    if (debug != null) {
      debug.println(
          "SunCertPathBuilder.depthFirstSearchForward("
              + dN
              + ", "
              + currentState.toString()
              + ")");
    }

    /*
     * Find all the certificates issued to dN which
     * satisfy the PKIX certification path constraints.
     */
    Collection<X509Certificate> certs =
        builder.getMatchingCerts(currentState, buildParams.certStores());
    List<Vertex> vertices = addVertices(certs, adjList);
    if (debug != null) {
      debug.println(
          "SunCertPathBuilder.depthFirstSearchForward(): " + "certs.size=" + vertices.size());
    }

    /*
     * For each cert in the collection, verify anything
     * that hasn't been checked yet (signature, revocation, etc)
     * and check for loops. Call depthFirstSearchForward()
     * recursively for each good cert.
     */

    vertices:
    for (Vertex vertex : vertices) {
      /**
       * Restore state to currentState each time through the loop. This is important because some of
       * the user-defined checkers modify the state, which MUST be restored if the cert eventually
       * fails to lead to the target and the next matching cert is tried.
       */
      ForwardState nextState = (ForwardState) currentState.clone();
      X509Certificate cert = vertex.getCertificate();

      try {
        builder.verifyCert(cert, nextState, cpList);
      } catch (GeneralSecurityException gse) {
        if (debug != null) {
          debug.println(
              "SunCertPathBuilder.depthFirstSearchForward()" + ": validation failed: " + gse);
          gse.printStackTrace();
        }
        vertex.setThrowable(gse);
        continue;
      }

      /*
       * Certificate is good.
       * If cert completes the path,
       *    process userCheckers that don't support forward checking
       *    and process policies over whole path
       *    and backtrack appropriately if there is a failure
       * else if cert does not complete the path,
       *    add it to the path
       */
      if (builder.isPathCompleted(cert)) {

        if (debug != null)
          debug.println(
              "SunCertPathBuilder.depthFirstSearchForward()" + ": commencing final verification");

        List<X509Certificate> appendedCerts = new ArrayList<>(cpList);

        /*
         * if the trust anchor selected is specified as a trusted
         * public key rather than a trusted cert, then verify this
         * cert (which is signed by the trusted public key), but
         * don't add it yet to the cpList
         */
        if (builder.trustAnchor.getTrustedCert() == null) {
          appendedCerts.add(0, cert);
        }

        Set<String> initExpPolSet = Collections.singleton(PolicyChecker.ANY_POLICY);

        PolicyNodeImpl rootNode =
            new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);

        List<PKIXCertPathChecker> checkers = new ArrayList<>();
        PolicyChecker policyChecker =
            new PolicyChecker(
                buildParams.initialPolicies(),
                appendedCerts.size(),
                buildParams.explicitPolicyRequired(),
                buildParams.policyMappingInhibited(),
                buildParams.anyPolicyInhibited(),
                buildParams.policyQualifiersRejected(),
                rootNode);
        checkers.add(policyChecker);

        // add the algorithm checker
        checkers.add(new AlgorithmChecker(builder.trustAnchor));

        BasicChecker basicChecker = null;
        if (nextState.keyParamsNeeded()) {
          PublicKey rootKey = cert.getPublicKey();
          if (builder.trustAnchor.getTrustedCert() == null) {
            rootKey = builder.trustAnchor.getCAPublicKey();
            if (debug != null)
              debug.println(
                  "SunCertPathBuilder.depthFirstSearchForward "
                      + "using buildParams public key: "
                      + rootKey.toString());
          }
          TrustAnchor anchor = new TrustAnchor(cert.getSubjectX500Principal(), rootKey, null);

          // add the basic checker
          basicChecker =
              new BasicChecker(anchor, buildParams.date(), buildParams.sigProvider(), true);
          checkers.add(basicChecker);
        }

        buildParams.setCertPath(cf.generateCertPath(appendedCerts));

        boolean revCheckerAdded = false;
        List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers();
        for (PKIXCertPathChecker ckr : ckrs) {
          if (ckr instanceof PKIXRevocationChecker) {
            if (revCheckerAdded) {
              throw new CertPathValidatorException(
                  "Only one PKIXRevocationChecker can be specified");
            }
            revCheckerAdded = true;
            // if it's our own, initialize it
            if (ckr instanceof RevocationChecker) {
              ((RevocationChecker) ckr).init(builder.trustAnchor, buildParams);
            }
          }
        }
        // only add a RevocationChecker if revocation is enabled and
        // a PKIXRevocationChecker has not already been added
        if (buildParams.revocationEnabled() && !revCheckerAdded) {
          checkers.add(new RevocationChecker(builder.trustAnchor, buildParams));
        }

        checkers.addAll(ckrs);

        // Why we don't need BasicChecker and RevocationChecker
        // if nextState.keyParamsNeeded() is false?

        for (int i = 0; i < appendedCerts.size(); i++) {
          X509Certificate currCert = appendedCerts.get(i);
          if (debug != null)
            debug.println("current subject = " + currCert.getSubjectX500Principal());
          Set<String> unresCritExts = currCert.getCriticalExtensionOIDs();
          if (unresCritExts == null) {
            unresCritExts = Collections.<String>emptySet();
          }

          for (PKIXCertPathChecker currChecker : checkers) {
            if (!currChecker.isForwardCheckingSupported()) {
              if (i == 0) {
                currChecker.init(false);

                // The user specified
                // AlgorithmChecker may not be
                // able to set the trust anchor until now.
                if (currChecker instanceof AlgorithmChecker) {
                  ((AlgorithmChecker) currChecker).trySetTrustAnchor(builder.trustAnchor);
                }
              }

              try {
                currChecker.check(currCert, unresCritExts);
              } catch (CertPathValidatorException cpve) {
                if (debug != null)
                  debug.println(
                      "SunCertPathBuilder.depthFirstSearchForward(): "
                          + "final verification failed: "
                          + cpve);
                // If the target cert itself is revoked, we
                // cannot trust it. We can bail out here.
                if (buildParams.targetCertConstraints().match(currCert)
                    && cpve.getReason() == BasicReason.REVOKED) {
                  throw cpve;
                }
                vertex.setThrowable(cpve);
                continue vertices;
              }
            }
          }

          /*
           * Remove extensions from user checkers that support
           * forward checking. After this step, we will have
           * removed all extensions that all user checkers
           * are capable of processing.
           */
          for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) {
            if (checker.isForwardCheckingSupported()) {
              Set<String> suppExts = checker.getSupportedExtensions();
              if (suppExts != null) {
                unresCritExts.removeAll(suppExts);
              }
            }
          }

          if (!unresCritExts.isEmpty()) {
            unresCritExts.remove(BasicConstraints_Id.toString());
            unresCritExts.remove(NameConstraints_Id.toString());
            unresCritExts.remove(CertificatePolicies_Id.toString());
            unresCritExts.remove(PolicyMappings_Id.toString());
            unresCritExts.remove(PolicyConstraints_Id.toString());
            unresCritExts.remove(InhibitAnyPolicy_Id.toString());
            unresCritExts.remove(SubjectAlternativeName_Id.toString());
            unresCritExts.remove(KeyUsage_Id.toString());
            unresCritExts.remove(ExtendedKeyUsage_Id.toString());

            if (!unresCritExts.isEmpty()) {
              throw new CertPathValidatorException(
                  "unrecognized critical extension(s)",
                  null,
                  null,
                  -1,
                  PKIXReason.UNRECOGNIZED_CRIT_EXT);
            }
          }
        }
        if (debug != null)
          debug.println(
              "SunCertPathBuilder.depthFirstSearchForward()"
                  + ": final verification succeeded - path completed!");
        pathCompleted = true;

        /*
         * if the user specified a trusted public key rather than
         * trusted certs, then add this cert (which is signed by
         * the trusted public key) to the cpList
         */
        if (builder.trustAnchor.getTrustedCert() == null) builder.addCertToPath(cert, cpList);
        // Save the trust anchor
        this.trustAnchor = builder.trustAnchor;

        /*
         * Extract and save the final target public key
         */
        if (basicChecker != null) {
          finalPublicKey = basicChecker.getPublicKey();
        } else {
          Certificate finalCert;
          if (cpList.isEmpty()) {
            finalCert = builder.trustAnchor.getTrustedCert();
          } else {
            finalCert = cpList.getLast();
          }
          finalPublicKey = finalCert.getPublicKey();
        }

        policyTreeResult = policyChecker.getPolicyTree();
        return;
      } else {
        builder.addCertToPath(cert, cpList);
      }

      /* Update the PKIX state */
      nextState.updateState(cert);

      /*
       * Append an entry for cert in adjacency list and
       * set index for current vertex.
       */
      adjList.add(new LinkedList<Vertex>());
      vertex.setIndex(adjList.size() - 1);

      /* recursively search for matching certs at next dN */
      depthFirstSearchForward(cert.getIssuerX500Principal(), nextState, builder, adjList, cpList);

      /*
       * If path has been completed, return ASAP!
       */
      if (pathCompleted) {
        return;
      } else {
        /*
         * If we get here, it means we have searched all possible
         * certs issued by the dN w/o finding any matching certs.
         * This means we have to backtrack to the previous cert in
         * the path and try some other paths.
         */
        if (debug != null)
          debug.println("SunCertPathBuilder.depthFirstSearchForward()" + ": backtracking");
        builder.removeFinalCertFromPath(cpList);
      }
    }
  }

  /*
   * Adds a collection of matching certificates to the
   * adjacency list.
   */
  private static List<Vertex> addVertices(
      Collection<X509Certificate> certs, List<List<Vertex>> adjList) {
    List<Vertex> l = adjList.get(adjList.size() - 1);

    for (X509Certificate cert : certs) {
      Vertex v = new Vertex(cert);
      l.add(v);
    }

    return l;
  }

  /** Returns true if trust anchor certificate matches specified certificate constraints. */
  private static boolean anchorIsTarget(TrustAnchor anchor, CertSelector sel) {
    X509Certificate anchorCert = anchor.getTrustedCert();
    if (anchorCert != null) {
      return sel.match(anchorCert);
    }
    return false;
  }
}