Update certpath code

Change-Id: Ie6d845095a45029b7bc8b7d120c6ed82de675e17
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/AdaptableX509CertSelector.java b/ojluni/src/main/java/sun/security/provider/certpath/AdaptableX509CertSelector.java
new file mode 100644
index 0000000..680795c
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/AdaptableX509CertSelector.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.util.Date;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509CertSelector;
+import java.security.cert.CertificateException;
+
+import sun.security.util.DerOutputStream;
+import sun.security.x509.SerialNumber;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+
+/**
+ * An adaptable X509 certificate selector for forward certification path
+ * building.
+ *
+ * @since 1.7
+ */
+class AdaptableX509CertSelector extends X509CertSelector {
+    // The start date of a validity period.
+    private Date startDate;
+
+    // The end date of a validity period.
+    private Date endDate;
+
+    // Is subject key identifier sensitive?
+    private boolean isSKIDSensitive = false;
+
+    // Is serial number sensitive?
+    private boolean isSNSensitive = false;
+
+    AdaptableX509CertSelector() {
+        super();
+    }
+
+    /**
+     * Sets the criterion of the X509Certificate validity period.
+     *
+     * Normally, we may not have to check that a certificate validity period
+     * must fall within its issuer's certificate validity period. However,
+     * when we face root CA key updates for version 1 certificates, according
+     * to scheme of RFC 4210 or 2510, the validity periods should be checked
+     * to determine the right issuer's certificate.
+     *
+     * Conservatively, we will only check the validity periods for version
+     * 1 and version 2 certificates. For version 3 certificates, we can
+     * determine the right issuer by authority and subject key identifier
+     * extensions.
+     *
+     * @param startDate the start date of a validity period that must fall
+     *        within the certificate validity period for the X509Certificate
+     * @param endDate the end date of a validity period that must fall
+     *        within the certificate validity period for the X509Certificate
+     */
+    void setValidityPeriod(Date startDate, Date endDate) {
+        this.startDate = startDate;
+        this.endDate = endDate;
+    }
+
+    /**
+     * Parse the authority key identifier extension.
+     *
+     * If the keyIdentifier field of the extension is non-null, set the
+     * subjectKeyIdentifier criterion. If the authorityCertSerialNumber
+     * field is non-null, set the serialNumber criterion.
+     *
+     * Note that we will not set the subject criterion according to the
+     * authorityCertIssuer field of the extension. The caller MUST set
+     * the subject criterion before call match().
+     *
+     * @param akidext the authorityKeyIdentifier extension
+     */
+    void parseAuthorityKeyIdentifierExtension(
+            AuthorityKeyIdentifierExtension akidext) throws IOException {
+        if (akidext != null) {
+            KeyIdentifier akid = (KeyIdentifier)akidext.get(
+                    AuthorityKeyIdentifierExtension.KEY_ID);
+            if (akid != null) {
+                // Do not override the previous setting for initial selection.
+                if (isSKIDSensitive || getSubjectKeyIdentifier() == null) {
+                    DerOutputStream derout = new DerOutputStream();
+                    derout.putOctetString(akid.getIdentifier());
+                    super.setSubjectKeyIdentifier(derout.toByteArray());
+
+                    isSKIDSensitive = true;
+                }
+            }
+
+            SerialNumber asn = (SerialNumber)akidext.get(
+                    AuthorityKeyIdentifierExtension.SERIAL_NUMBER);
+            if (asn != null) {
+                // Do not override the previous setting for initial selection.
+                if (isSNSensitive || getSerialNumber() == null) {
+                    super.setSerialNumber(asn.getNumber());
+                    isSNSensitive = true;
+                }
+            }
+
+            // the subject criterion should be set by the caller.
+        }
+    }
+
+    /**
+     * Decides whether a <code>Certificate</code> should be selected.
+     *
+     * For the purpose of compatibility, when a certificate is of
+     * version 1 and version 2, or the certificate does not include
+     * a subject key identifier extension, the selection criterion
+     * of subjectKeyIdentifier will be disabled.
+     */
+    @Override
+    public boolean match(Certificate cert) {
+        if (!(cert instanceof X509Certificate)) {
+            return false;
+        }
+
+        X509Certificate xcert = (X509Certificate)cert;
+        int version = xcert.getVersion();
+
+        // Check the validity period for version 1 and 2 certificate.
+        if (version < 3) {
+            if (startDate != null) {
+                try {
+                    xcert.checkValidity(startDate);
+                } catch (CertificateException ce) {
+                    return false;
+                }
+            }
+
+            if (endDate != null) {
+                try {
+                    xcert.checkValidity(endDate);
+                } catch (CertificateException ce) {
+                    return false;
+                }
+            }
+        }
+
+        // If no SubjectKeyIdentifier extension, don't bother to check it.
+        if (isSKIDSensitive &&
+            (version < 3 || xcert.getExtensionValue("2.5.29.14") == null)) {
+            setSubjectKeyIdentifier(null);
+        }
+
+        // In practice, a CA may replace its root certificate and require that
+        // the existing certificate is still valid, even if the AKID extension
+        // does not match the replacement root certificate fields.
+        //
+        // Conservatively, we only support the replacement for version 1 and
+        // version 2 certificate. As for version 2, the certificate extension
+        // may contain sensitive information (for example, policies), the
+        // AKID need to be respected to seek the exact certificate in case
+        // of key or certificate abuse.
+        if (isSNSensitive && version < 3) {
+            setSerialNumber(null);
+        }
+
+        return super.match(cert);
+    }
+
+    @Override
+    public Object clone() {
+        AdaptableX509CertSelector copy =
+                        (AdaptableX509CertSelector)super.clone();
+        if (startDate != null) {
+            copy.startDate = (Date)startDate.clone();
+        }
+
+        if (endDate != null) {
+            copy.endDate = (Date)endDate.clone();
+        }
+
+        return copy;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/AdjacencyList.java b/ojluni/src/main/java/sun/security/provider/certpath/AdjacencyList.java
new file mode 100644
index 0000000..f26919b
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/AdjacencyList.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.provider.certpath;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An AdjacencyList is used to store the history of certification paths
+ * attempted in constructing a path from an initiator to a target. The
+ * AdjacencyList is initialized with a <code>List</code> of
+ * <code>List</code>s, where each sub-<code>List</code> contains objects of
+ * type <code>Vertex</code>. A <code>Vertex</code> describes one possible or
+ * actual step in the chain building process, and the associated
+ * <code>Certificate</code>. Specifically, a <code>Vertex</code> object
+ * contains a <code>Certificate</code> and an index value referencing the
+ * next sub-list in the process. If the index value is -1 then this
+ * <code>Vertex</code> doesn't continue the attempted build path.
+ * <p>
+ * Example:
+ * <p>
+ * Attempted Paths:<ul>
+ * <li>C1-&gt;C2-&gt;C3
+ * <li>C1-&gt;C4-&gt;C5
+ * <li>C1-&gt;C4-&gt;C6
+ * <li>C1-&gt;C4-&gt;C7
+ * <li>C1-&gt;C8-&gt;C9
+ * <li>C1-&gt;C10-&gt;C11
+ * </ul>
+ * <p>
+ * AdjacencyList structure:<ul>
+ * <li>AL[0] = C1,1
+ * <li>AL[1] = C2,2   =&gt;C4,3   =&gt;C8,4     =&gt;C10,5
+ * <li>AL[2] = C3,-1
+ * <li>AL[3] = C5,-1  =&gt;C6,-1  =&gt;C7,-1
+ * <li>AL[4] = C9,-1
+ * <li>AL[5] = C11,-1
+ * </ul>
+ * <p>
+ * The iterator method returns objects of type <code>BuildStep</code>, not
+ * objects of type <code>Vertex</code>.
+ * A <code>BuildStep</code> contains a <code>Vertex</code> and a result code,
+ * accessible via getResult method. There are five result values.
+ * <code>POSSIBLE</code> denotes that the current step represents a
+ * <code>Certificate</code> that the builder is considering at this point in
+ * the build. <code>FOLLOW</code> denotes a <code>Certificate</code> (one of
+ * those noted as <code>POSSIBLE</code>) that the builder is using to try
+ * extending the chain. <code>BACK</code> represents that a
+ * <code>FOLLOW</code> was incorrect, and is being removed from the chain.
+ * There is exactly one <code>FOLLOW</code> for each <code>BACK</code>. The
+ * values <code>SUCCEED</code> and <code>FAIL</code> mean that we've come to
+ * the end of the build process, and there will not be any more entries in
+ * the list.
+ * <p>
+ * @see sun.security.provider.certpath.BuildStep
+ * @see sun.security.provider.certpath.Vertex
+ * <p>
+ * @author  seth proctor
+ * @since   1.4
+ */
+public class AdjacencyList {
+
+    // the actual set of steps the AdjacencyList represents
+    private ArrayList<BuildStep> mStepList;
+
+    // the original list, just for the toString method
+    private List<List<Vertex>> mOrigList;
+
+    /**
+     * Constructs a new <code>AdjacencyList</code> based on the specified
+     * <code>List</code>. See the example above.
+     *
+     * @param list a <code>List</code> of <code>List</code>s of
+     *             <code>Vertex</code> objects
+     */
+    public AdjacencyList(List<List<Vertex>> list) {
+        mStepList = new ArrayList<BuildStep>();
+        mOrigList = list;
+        buildList(list, 0, null);
+    }
+
+    /**
+     * Gets an <code>Iterator</code> to iterate over the set of
+     * <code>BuildStep</code>s in build-order. Any attempts to change
+     * the list through the remove method will fail.
+     *
+     * @return an <code>Iterator</code> over the <code>BuildStep</code>s
+     */
+    public Iterator<BuildStep> iterator() {
+        return Collections.unmodifiableList(mStepList).iterator();
+    }
+
+    /**
+     * Recursive, private method which actually builds the step list from
+     * the given adjacency list. <code>Follow</code> is the parent BuildStep
+     * that we followed to get here, and if it's null, it means that we're
+     * at the start.
+     */
+    private boolean buildList(List<List<Vertex>> theList, int index,
+                              BuildStep follow) {
+
+        // Each time this method is called, we're examining a new list
+        // from the global list. So, we have to start by getting the list
+        // that contains the set of Vertexes we're considering.
+        List<Vertex> l = theList.get(index);
+
+        // we're interested in the case where all indexes are -1...
+        boolean allNegOne = true;
+        // ...and in the case where every entry has a Throwable
+        boolean allXcps = true;
+
+        for (Vertex v : l) {
+            if (v.getIndex() != -1) {
+                // count an empty list the same as an index of -1...this
+                // is to patch a bug somewhere in the builder
+                if (theList.get(v.getIndex()).size() != 0)
+                    allNegOne = false;
+            } else {
+                if (v.getThrowable() == null)
+                    allXcps = false;
+            }
+            // every entry, regardless of the final use for it, is always
+            // entered as a possible step before we take any actions
+            mStepList.add(new BuildStep(v, BuildStep.POSSIBLE));
+        }
+
+        if (allNegOne) {
+            // There are two cases that we could be looking at here. We
+            // may need to back up, or the build may have succeeded at
+            // this point. This is based on whether or not any
+            // exceptions were found in the list.
+            if (allXcps) {
+                // we need to go back...see if this is the last one
+                if (follow == null)
+                    mStepList.add(new BuildStep(null, BuildStep.FAIL));
+                else
+                    mStepList.add(new BuildStep(follow.getVertex(),
+                                                BuildStep.BACK));
+
+                return false;
+            } else {
+                // we succeeded...now the only question is which is the
+                // successful step? If there's only one entry without
+                // a throwable, then that's the successful step. Otherwise,
+                // we'll have to make some guesses...
+                List<Vertex> possibles = new ArrayList<>();
+                for (Vertex v : l) {
+                    if (v.getThrowable() == null)
+                        possibles.add(v);
+                }
+
+                if (possibles.size() == 1) {
+                    // real easy...we've found the final Vertex
+                    mStepList.add(new BuildStep(possibles.get(0),
+                                                BuildStep.SUCCEED));
+                } else {
+                    // ok...at this point, there is more than one Cert
+                    // which might be the succeed step...how do we know
+                    // which it is? I'm going to assume that our builder
+                    // algorithm is good enough to know which is the
+                    // correct one, and put it first...but a FIXME goes
+                    // here anyway, and we should be comparing to the
+                    // target/initiator Cert...
+                    mStepList.add(new BuildStep(possibles.get(0),
+                                                BuildStep.SUCCEED));
+                }
+
+                return true;
+            }
+        } else {
+            // There's at least one thing that we can try before we give
+            // up and go back. Run through the list now, and enter a new
+            // BuildStep for each path that we try to follow. If none of
+            // the paths we try produce a successful end, we're going to
+            // have to back out ourselves.
+            boolean success = false;
+
+            for (Vertex v : l) {
+
+                // Note that we'll only find a SUCCEED case when we're
+                // looking at the last possible path, so we don't need to
+                // consider success in the while loop
+
+                if (v.getIndex() != -1) {
+                    if (theList.get(v.getIndex()).size() != 0) {
+                        // If the entry we're looking at doesn't have an
+                        // index of -1, and doesn't lead to an empty list,
+                        // then it's something we follow!
+                        BuildStep bs = new BuildStep(v, BuildStep.FOLLOW);
+                        mStepList.add(bs);
+                        success = buildList(theList, v.getIndex(), bs);
+                    }
+                }
+            }
+
+            if (success) {
+                // We're already finished!
+                return true;
+            } else {
+                // We failed, and we've exhausted all the paths that we
+                // could take. The only choice is to back ourselves out.
+                if (follow == null)
+                    mStepList.add(new BuildStep(null, BuildStep.FAIL));
+                else
+                    mStepList.add(new BuildStep(follow.getVertex(),
+                                                BuildStep.BACK));
+
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Prints out a string representation of this AdjacencyList.
+     *
+     * @return String representation
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("[\n");
+
+        int i = 0;
+        for (List<Vertex> l : mOrigList) {
+            sb.append("LinkedList[").append(i++).append("]:\n");
+
+            for (Vertex step : l) {
+                sb.append(step.toString()).append("\n");
+            }
+        }
+        sb.append("]\n");
+
+        return sb.toString();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java
old mode 100755
new mode 100644
index 27a9caa..ab75ff0
--- a/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -259,8 +259,7 @@
             }
 
             // Inherit key parameters from previous key
-            if (currPubKey instanceof DSAPublicKey &&
-                ((DSAPublicKey)currPubKey).getParams() == null) {
+            if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) {
                 // Inherit DSA parameters from previous key
                 if (!(prevPubKey instanceof DSAPublicKey)) {
                     throw new CertPathValidatorException("Input key is not " +
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/BasicChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/BasicChecker.java
new file mode 100644
index 0000000..49a0368
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/BasicChecker.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Set;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.X509Certificate;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXReason;
+import java.security.cert.TrustAnchor;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import javax.security.auth.x500.X500Principal;
+import sun.security.x509.X500Name;
+import sun.security.util.Debug;
+
+/**
+ * BasicChecker is a PKIXCertPathChecker that checks the basic information
+ * on a PKIX certificate, namely the signature, timestamp, and subject/issuer
+ * name chaining.
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+class BasicChecker extends PKIXCertPathChecker {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private final PublicKey trustedPubKey;
+    private final X500Principal caName;
+    private final Date date;
+    private final String sigProvider;
+    private final boolean sigOnly;
+    private X500Principal prevSubject;
+    private PublicKey prevPubKey;
+
+    /**
+     * Constructor that initializes the input parameters.
+     *
+     * @param anchor the anchor selected to validate the target certificate
+     * @param testDate the time for which the validity of the certificate
+     *        should be determined
+     * @param sigProvider the name of the signature provider
+     * @param sigOnly true if only signature checking is to be done;
+     *        if false, all checks are done
+     */
+    BasicChecker(TrustAnchor anchor, Date date, String sigProvider,
+                 boolean sigOnly) {
+        if (anchor.getTrustedCert() != null) {
+            this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
+            this.caName = anchor.getTrustedCert().getSubjectX500Principal();
+        } else {
+            this.trustedPubKey = anchor.getCAPublicKey();
+            this.caName = anchor.getCA();
+        }
+        this.date = date;
+        this.sigProvider = sigProvider;
+        this.sigOnly = sigOnly;
+        this.prevPubKey = trustedPubKey;
+    }
+
+    /**
+     * Initializes the internal state of the checker from parameters
+     * specified in the constructor.
+     */
+    @Override
+    public void init(boolean forward) throws CertPathValidatorException {
+        if (!forward) {
+            prevPubKey = trustedPubKey;
+            if (PKIX.isDSAPublicKeyWithoutParams(prevPubKey)) {
+                // If TrustAnchor is a DSA public key and it has no params, it
+                // cannot be used to verify the signature of the first cert,
+                // so throw exception
+                throw new CertPathValidatorException("Key parameters missing");
+            }
+            prevSubject = caName;
+        } else {
+            throw new
+                CertPathValidatorException("forward checking not supported");
+        }
+    }
+
+    @Override
+    public boolean isForwardCheckingSupported() {
+        return false;
+    }
+
+    @Override
+    public Set<String> getSupportedExtensions() {
+        return null;
+    }
+
+    /**
+     * Performs the signature, timestamp, and subject/issuer name chaining
+     * checks on the certificate using its internal state. This method does
+     * not remove any critical extensions from the Collection.
+     *
+     * @param cert the Certificate
+     * @param unresolvedCritExts a Collection of the unresolved critical
+     * extensions
+     * @throws CertPathValidatorException if certificate does not verify
+     */
+    @Override
+    public void check(Certificate cert, Collection<String> unresolvedCritExts)
+        throws CertPathValidatorException
+    {
+        X509Certificate currCert = (X509Certificate)cert;
+
+        if (!sigOnly) {
+            verifyTimestamp(currCert);
+            verifyNameChaining(currCert);
+        }
+        verifySignature(currCert);
+
+        updateState(currCert);
+    }
+
+    /**
+     * Verifies the signature on the certificate using the previous public key.
+     *
+     * @param cert the X509Certificate
+     * @throws CertPathValidatorException if certificate does not verify
+     */
+    private void verifySignature(X509Certificate cert)
+        throws CertPathValidatorException
+    {
+        String msg = "signature";
+        if (debug != null)
+            debug.println("---checking " + msg + "...");
+
+        try {
+            cert.verify(prevPubKey, sigProvider);
+        } catch (SignatureException e) {
+            throw new CertPathValidatorException
+                (msg + " check failed", e, null, -1,
+                 BasicReason.INVALID_SIGNATURE);
+        } catch (GeneralSecurityException e) {
+            throw new CertPathValidatorException(msg + " check failed", e);
+        }
+
+        if (debug != null)
+            debug.println(msg + " verified.");
+    }
+
+    /**
+     * Internal method to verify the timestamp on a certificate
+     */
+    private void verifyTimestamp(X509Certificate cert)
+        throws CertPathValidatorException
+    {
+        String msg = "timestamp";
+        if (debug != null)
+            debug.println("---checking " + msg + ":" + date.toString() + "...");
+
+        try {
+            cert.checkValidity(date);
+        } catch (CertificateExpiredException e) {
+            throw new CertPathValidatorException
+                (msg + " check failed", e, null, -1, BasicReason.EXPIRED);
+        } catch (CertificateNotYetValidException e) {
+            throw new CertPathValidatorException
+                (msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID);
+        }
+
+        if (debug != null)
+            debug.println(msg + " verified.");
+    }
+
+    /**
+     * Internal method to check that cert has a valid DN to be next in a chain
+     */
+    private void verifyNameChaining(X509Certificate cert)
+        throws CertPathValidatorException
+    {
+        if (prevSubject != null) {
+
+            String msg = "subject/issuer name chaining";
+            if (debug != null)
+                debug.println("---checking " + msg + "...");
+
+            X500Principal currIssuer = cert.getIssuerX500Principal();
+
+            // reject null or empty issuer DNs
+            if (X500Name.asX500Name(currIssuer).isEmpty()) {
+                throw new CertPathValidatorException
+                    (msg + " check failed: " +
+                     "empty/null issuer DN in certificate is invalid", null,
+                     null, -1, PKIXReason.NAME_CHAINING);
+            }
+
+            if (!(currIssuer.equals(prevSubject))) {
+                throw new CertPathValidatorException
+                    (msg + " check failed", null, null, -1,
+                     PKIXReason.NAME_CHAINING);
+            }
+
+            if (debug != null)
+                debug.println(msg + " verified.");
+        }
+    }
+
+    /**
+     * Internal method to manage state information at each iteration
+     */
+    private void updateState(X509Certificate currCert)
+        throws CertPathValidatorException
+    {
+        PublicKey cKey = currCert.getPublicKey();
+        if (debug != null) {
+            debug.println("BasicChecker.updateState issuer: " +
+                currCert.getIssuerX500Principal().toString() + "; subject: " +
+                currCert.getSubjectX500Principal() + "; serial#: " +
+                currCert.getSerialNumber().toString());
+        }
+        if (PKIX.isDSAPublicKeyWithoutParams(cKey)) {
+            // cKey needs to inherit DSA parameters from prev key
+            cKey = makeInheritedParamsKey(cKey, prevPubKey);
+            if (debug != null) debug.println("BasicChecker.updateState Made " +
+                                             "key with inherited params");
+        }
+        prevPubKey = cKey;
+        prevSubject = currCert.getSubjectX500Principal();
+    }
+
+    /**
+     * Internal method to create a new key with inherited key parameters.
+     *
+     * @param keyValueKey key from which to obtain key value
+     * @param keyParamsKey key from which to obtain key parameters
+     * @return new public key having value and parameters
+     * @throws CertPathValidatorException if keys are not appropriate types
+     * for this operation
+     */
+    static PublicKey makeInheritedParamsKey(PublicKey keyValueKey,
+        PublicKey keyParamsKey) throws CertPathValidatorException
+    {
+        if (!(keyValueKey instanceof DSAPublicKey) ||
+            !(keyParamsKey instanceof DSAPublicKey))
+            throw new CertPathValidatorException("Input key is not " +
+                                                 "appropriate type for " +
+                                                 "inheriting parameters");
+        DSAParams params = ((DSAPublicKey)keyParamsKey).getParams();
+        if (params == null)
+            throw new CertPathValidatorException("Key parameters missing");
+        try {
+            BigInteger y = ((DSAPublicKey)keyValueKey).getY();
+            KeyFactory kf = KeyFactory.getInstance("DSA");
+            DSAPublicKeySpec ks = new DSAPublicKeySpec(y,
+                                                       params.getP(),
+                                                       params.getQ(),
+                                                       params.getG());
+            return kf.generatePublic(ks);
+        } catch (GeneralSecurityException e) {
+            throw new CertPathValidatorException("Unable to generate key with" +
+                                                 " inherited parameters: " +
+                                                 e.getMessage(), e);
+        }
+    }
+
+    /**
+     * return the public key associated with the last certificate processed
+     *
+     * @return PublicKey the last public key processed
+     */
+    PublicKey getPublicKey() {
+        return prevPubKey;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/BuildStep.java b/ojluni/src/main/java/sun/security/provider/certpath/BuildStep.java
new file mode 100644
index 0000000..bf06411
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/BuildStep.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * 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 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 = 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 getIssuerName(null);
+    }
+
+    /**
+     * 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 getSubjectName(null);
+    }
+
+    /**
+     * 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 POSSIBLE:
+                resultString = "Certificate to be tried.\n";
+                break;
+            case BACK:
+                resultString = "Certificate backed out since path does not "
+                    + "satisfy build requirements.\n";
+                break;
+            case FOLLOW:
+                resultString = "Certificate satisfies conditions.\n";
+                break;
+            case FAIL:
+                resultString = "Certificate backed out since path does not "
+                    + "satisfy conditions.\n";
+                break;
+            case 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
+     */
+    @Override
+    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() {
+        return resultToString(getResult()) + vertex.toString();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/Builder.java b/ojluni/src/main/java/sun/security/provider/certpath/Builder.java
new file mode 100644
index 0000000..e053b20
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/Builder.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.GeneralSecurityException;
+import java.security.cert.*;
+import java.util.*;
+
+import sun.security.action.GetBooleanAction;
+import sun.security.provider.certpath.PKIX.BuilderParams;
+import sun.security.util.Debug;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.GeneralNameInterface;
+import sun.security.x509.GeneralSubtrees;
+import sun.security.x509.NameConstraintsExtension;
+import sun.security.x509.SubjectAlternativeNameExtension;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509CertImpl;
+
+/**
+ * Abstract class representing a builder, which is able to retrieve
+ * matching certificates and is able to verify a particular certificate.
+ *
+ * @since       1.4
+ * @author      Sean Mullan
+ * @author      Yassir Elley
+ */
+
+public abstract class Builder {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private Set<String> matchingPolicies;
+    final BuilderParams buildParams;
+    final X509CertSelector targetCertConstraints;
+
+    /**
+     * Flag indicating whether support for the caIssuers field of the
+     * Authority Information Access extension shall be enabled. Currently
+     * disabled by default for compatibility reasons.
+     */
+    final static boolean USE_AIA = AccessController.doPrivileged
+        (new GetBooleanAction("com.sun.security.enableAIAcaIssuers"));
+
+    /**
+     * Initialize the builder with the input parameters.
+     *
+     * @param params the parameter set used to build a certification path
+     */
+    Builder(BuilderParams buildParams) {
+        this.buildParams = buildParams;
+        this.targetCertConstraints =
+            (X509CertSelector)buildParams.targetCertConstraints();
+    }
+
+    /**
+     * Retrieves certificates from the list of certStores using the buildParams
+     * and the currentState as a filter
+     *
+     * @param currentState the current State
+     * @param certStores list of CertStores
+     */
+    abstract Collection<X509Certificate> getMatchingCerts
+        (State currentState, List<CertStore> certStores)
+        throws CertStoreException, CertificateException, IOException;
+
+    /**
+     * Verifies the cert against the currentState, using the certPathList
+     * generated thus far to help with loop detection
+     *
+     * @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
+     */
+    abstract void verifyCert(X509Certificate cert, State currentState,
+                             List<X509Certificate> certPathList)
+        throws GeneralSecurityException;
+
+    /**
+     * Verifies whether the input certificate completes the path.
+     * When building forward, a trust anchor will complete the path.
+     * When building reverse, the target certificate will complete the path.
+     *
+     * @param cert the certificate to test
+     * @return a boolean value indicating whether the cert completes the path.
+     */
+    abstract boolean isPathCompleted(X509Certificate cert);
+
+    /**
+     * Adds the certificate to the certPathList
+     *
+     * @param cert the certificate to be added
+     * @param certPathList the certification path list
+     */
+    abstract void addCertToPath(X509Certificate cert,
+                                LinkedList<X509Certificate> certPathList);
+
+    /**
+     * Removes final certificate from the certPathList
+     *
+     * @param certPathList the certification path list
+     */
+    abstract void removeFinalCertFromPath
+        (LinkedList<X509Certificate> certPathList);
+
+    /**
+     * get distance of one GeneralName from another
+     *
+     * @param base GeneralName at base of subtree
+     * @param test GeneralName to be tested against base
+     * @param incomparable the value to return if the names are
+     *  incomparable
+     * @return distance of test name from base, where 0
+     *         means exact match, 1 means test is an immediate
+     *         child of base, 2 means test is a grandchild, etc.
+     *         -1 means test is a parent of base, -2 means test
+     *         is a grandparent, etc.
+     */
+    static int distance(GeneralNameInterface base,
+                        GeneralNameInterface test, int incomparable)
+    {
+        switch (base.constrains(test)) {
+        case GeneralNameInterface.NAME_DIFF_TYPE:
+            if (debug != null) {
+                debug.println("Builder.distance(): Names are different types");
+            }
+            return incomparable;
+        case GeneralNameInterface.NAME_SAME_TYPE:
+            if (debug != null) {
+                debug.println("Builder.distance(): Names are same type but " +
+                    "in different subtrees");
+            }
+            return incomparable;
+        case GeneralNameInterface.NAME_MATCH:
+            return 0;
+        case GeneralNameInterface.NAME_WIDENS:
+            break;
+        case GeneralNameInterface.NAME_NARROWS:
+            break;
+        default: // should never occur
+            return incomparable;
+        }
+
+        /* names are in same subtree */
+        return test.subtreeDepth() - base.subtreeDepth();
+    }
+
+    /**
+     * get hop distance of one GeneralName from another in links where
+     * the names need not have an ancestor/descendant relationship.
+     * For example, the hop distance from ou=D,ou=C,o=B,c=US to
+     * ou=F,ou=E,ou=C,o=B,c=US is 3: D->C, C->E, E->F.  The hop distance
+     * from ou=C,o=B,c=US to ou=D,ou=C,o=B,c=US is -1: C->D
+     *
+     * @param base GeneralName
+     * @param test GeneralName to be tested against base
+     * @param incomparable the value to return if the names are
+     *  incomparable
+     * @return distance of test name from base measured in hops in the
+     *         namespace hierarchy, where 0 means exact match.  Result
+     *         is positive if path is some number of up hops followed by
+     *         some number of down hops; result is negative if path is
+     *         some number of down hops.
+     */
+    static int hops(GeneralNameInterface base, GeneralNameInterface test,
+                    int incomparable)
+    {
+        int baseRtest = base.constrains(test);
+        switch (baseRtest) {
+        case GeneralNameInterface.NAME_DIFF_TYPE:
+            if (debug != null) {
+                debug.println("Builder.hops(): Names are different types");
+            }
+            return incomparable;
+        case GeneralNameInterface.NAME_SAME_TYPE:
+            /* base and test are in different subtrees */
+            break;
+        case GeneralNameInterface.NAME_MATCH:
+            /* base matches test */
+            return 0;
+        case GeneralNameInterface.NAME_WIDENS:
+            /* base is ancestor of test */
+            return (test.subtreeDepth()-base.subtreeDepth());
+        case GeneralNameInterface.NAME_NARROWS:
+            /* base is descendant of test */
+            return (test.subtreeDepth()-base.subtreeDepth());
+        default: // should never occur
+            return incomparable;
+        }
+
+        /* names are in different subtrees */
+        if (base.getType() != GeneralNameInterface.NAME_DIRECTORY) {
+            if (debug != null) {
+                debug.println("Builder.hops(): hopDistance not implemented " +
+                    "for this name type");
+            }
+            return incomparable;
+        }
+        X500Name baseName = (X500Name)base;
+        X500Name testName = (X500Name)test;
+        X500Name commonName = baseName.commonAncestor(testName);
+        if (commonName == null) {
+            if (debug != null) {
+                debug.println("Builder.hops(): Names are in different " +
+                    "namespaces");
+            }
+            return incomparable;
+        } else {
+            int commonDistance = commonName.subtreeDepth();
+            int baseDistance = baseName.subtreeDepth();
+            int testDistance = testName.subtreeDepth();
+            return (baseDistance + testDistance - (2 * commonDistance));
+        }
+    }
+
+    /**
+     * Determine how close a given certificate gets you toward
+     * a given target.
+     *
+     * @param constraints Current NameConstraints; if null,
+     *        then caller must verify NameConstraints
+     *        independently, realizing that this certificate
+     *        may not actually lead to the target at all.
+     * @param cert Candidate certificate for chain
+     * @param target GeneralNameInterface name of target
+     * @return distance from this certificate to target:
+     * <ul>
+     * <li>-1 means certificate could be CA for target, but
+     *     there are no NameConstraints limiting how close
+     * <li> 0 means certificate subject or subjectAltName
+     *      matches target
+     * <li> 1 means certificate is permitted to be CA for
+     *      target.
+     * <li> 2 means certificate is permitted to be CA for
+     *      parent of target.
+     * <li>&gt;0 in general, means certificate is permitted
+     *     to be a CA for this distance higher in the naming
+     *     hierarchy than the target, plus 1.
+     * </ul>
+     * <p>Note that the subject and/or subjectAltName of the
+     * candidate cert does not have to be an ancestor of the
+     * target in order to be a CA that can issue a certificate to
+     * the target. In these cases, the target distance is calculated
+     * by inspecting the NameConstraints extension in the candidate
+     * certificate. For example, suppose the target is an X.500 DN with
+     * a value of "CN=mullan,OU=ireland,O=sun,C=us" and the
+     * NameConstraints extension in the candidate certificate
+     * includes a permitted component of "O=sun,C=us", which implies
+     * that the candidate certificate is allowed to issue certs in
+     * the "O=sun,C=us" namespace. The target distance is 3
+     * ((distance of permitted NC from target) + 1).
+     * The (+1) is added to distinguish the result from the case
+     * which returns (0).
+     * @throws IOException if certificate does not get closer
+     */
+    static int targetDistance(NameConstraintsExtension constraints,
+                              X509Certificate cert, GeneralNameInterface target)
+            throws IOException
+    {
+        /* ensure that certificate satisfies existing name constraints */
+        if (constraints != null && !constraints.verify(cert)) {
+            throw new IOException("certificate does not satisfy existing name "
+                + "constraints");
+        }
+
+        X509CertImpl certImpl;
+        try {
+            certImpl = X509CertImpl.toImpl(cert);
+        } catch (CertificateException e) {
+            throw new IOException("Invalid certificate", e);
+        }
+        /* see if certificate subject matches target */
+        X500Name subject = X500Name.asX500Name(certImpl.getSubjectX500Principal());
+        if (subject.equals(target)) {
+            /* match! */
+            return 0;
+        }
+
+        SubjectAlternativeNameExtension altNameExt =
+            certImpl.getSubjectAlternativeNameExtension();
+        if (altNameExt != null) {
+            GeneralNames altNames = altNameExt.get(
+                    SubjectAlternativeNameExtension.SUBJECT_NAME);
+            /* see if any alternative name matches target */
+            if (altNames != null) {
+                for (int j = 0, n = altNames.size(); j < n; j++) {
+                    GeneralNameInterface altName = altNames.get(j).getName();
+                    if (altName.equals(target)) {
+                        return 0;
+                    }
+                }
+            }
+        }
+
+
+        /* no exact match; see if certificate can get us to target */
+
+        /* first, get NameConstraints out of certificate */
+        NameConstraintsExtension ncExt = certImpl.getNameConstraintsExtension();
+        if (ncExt == null) {
+            return -1;
+        }
+
+        /* merge certificate's NameConstraints with current NameConstraints */
+        if (constraints != null) {
+            constraints.merge(ncExt);
+        } else {
+            // Make sure we do a clone here, because we're probably
+            // going to modify this object later and we don't want to
+            // be sharing it with a Certificate object!
+            constraints = (NameConstraintsExtension) ncExt.clone();
+        }
+
+        if (debug != null) {
+            debug.println("Builder.targetDistance() merged constraints: "
+                + String.valueOf(constraints));
+        }
+        /* reduce permitted by excluded */
+        GeneralSubtrees permitted =
+                constraints.get(NameConstraintsExtension.PERMITTED_SUBTREES);
+        GeneralSubtrees excluded =
+                constraints.get(NameConstraintsExtension.EXCLUDED_SUBTREES);
+        if (permitted != null) {
+            permitted.reduce(excluded);
+        }
+        if (debug != null) {
+            debug.println("Builder.targetDistance() reduced constraints: "
+                + permitted);
+        }
+        /* see if new merged constraints allow target */
+        if (!constraints.verify(target)) {
+            throw new IOException("New certificate not allowed to sign "
+                + "certificate for target");
+        }
+        /* find distance to target, if any, in permitted */
+        if (permitted == null) {
+            /* certificate is unconstrained; could sign for anything */
+            return -1;
+        }
+        for (int i = 0, n = permitted.size(); i < n; i++) {
+            GeneralNameInterface perName = permitted.get(i).getName().getName();
+            int distance = distance(perName, target, -1);
+            if (distance >= 0) {
+                return (distance + 1);
+            }
+        }
+        /* no matching type in permitted; cert holder could certify target */
+        return -1;
+    }
+
+    /**
+     * This method can be used as an optimization to filter out
+     * certificates that do not have policies which are valid.
+     * It returns the set of policies (String OIDs) that should exist in
+     * the certificate policies extension of the certificate that is
+     * needed by the builder. The logic applied is as follows:
+     * <p>
+     *   1) If some initial policies have been set *and* policy mappings are
+     *   inhibited, then acceptable certificates are those that include
+     *   the ANY_POLICY OID or with policies that intersect with the
+     *   initial policies.
+     *   2) If no initial policies have been set *or* policy mappings are
+     *   not inhibited then we don't have much to work with. All we know is
+     *   that a certificate must have *some* policy because if it didn't
+     *   have any policy then the policy tree would become null (and validation
+     *   would fail).
+     *
+     * @return the Set of policies any of which must exist in a
+     * cert's certificate policies extension in order for a cert to be selected.
+     */
+    Set<String> getMatchingPolicies() {
+        if (matchingPolicies != null) {
+            Set<String> initialPolicies = buildParams.initialPolicies();
+            if ((!initialPolicies.isEmpty()) &&
+                (!initialPolicies.contains(PolicyChecker.ANY_POLICY)) &&
+                (buildParams.policyMappingInhibited()))
+            {
+                matchingPolicies = new HashSet<>(initialPolicies);
+                matchingPolicies.add(PolicyChecker.ANY_POLICY);
+            } else {
+                // we just return an empty set to make sure that there is
+                // at least a certificate policies extension in the cert
+                matchingPolicies = Collections.<String>emptySet();
+            }
+        }
+        return matchingPolicies;
+    }
+
+    /**
+     * Search the specified CertStores and add all certificates matching
+     * selector to resultCerts. Self-signed certs are not useful here
+     * and therefore ignored.
+     *
+     * If the targetCert criterion of the selector is set, only that cert
+     * is examined and the CertStores are not searched.
+     *
+     * If checkAll is true, all CertStores are searched for matching certs.
+     * If false, the method returns as soon as the first CertStore returns
+     * a matching cert(s).
+     *
+     * Returns true iff resultCerts changed (a cert was added to the collection)
+     */
+    boolean addMatchingCerts(X509CertSelector selector,
+                             Collection<CertStore> certStores,
+                             Collection<X509Certificate> resultCerts,
+                             boolean checkAll)
+    {
+        X509Certificate targetCert = selector.getCertificate();
+        if (targetCert != null) {
+            // no need to search CertStores
+            if (selector.match(targetCert) && !X509CertImpl.isSelfSigned
+                (targetCert, buildParams.sigProvider())) {
+                if (debug != null) {
+                    debug.println("Builder.addMatchingCerts: adding target cert");
+                }
+                return resultCerts.add(targetCert);
+            }
+            return false;
+        }
+        boolean add = false;
+        for (CertStore store : certStores) {
+            try {
+                Collection<? extends Certificate> certs =
+                                        store.getCertificates(selector);
+                for (Certificate cert : certs) {
+                    if (!X509CertImpl.isSelfSigned
+                        ((X509Certificate)cert, buildParams.sigProvider())) {
+                        if (resultCerts.add((X509Certificate)cert)) {
+                            add = true;
+                        }
+                    }
+                }
+                if (!checkAll && add) {
+                    return true;
+                }
+            } catch (CertStoreException cse) {
+                // if getCertificates throws a CertStoreException, we ignore
+                // it and move on to the next CertStore
+                if (debug != null) {
+                    debug.println("Builder.addMatchingCerts, non-fatal " +
+                        "exception retrieving certs: " + cse);
+                    cse.printStackTrace();
+                }
+            }
+        }
+        return add;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/CertId.java b/ojluni/src/main/java/sun/security/provider/certpath/CertId.java
new file mode 100644
index 0000000..ff7be69
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/CertId.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import javax.security.auth.x500.X500Principal;
+import sun.misc.HexDumpEncoder;
+import sun.security.x509.*;
+import sun.security.util.*;
+
+/**
+ * This class corresponds to the CertId field in OCSP Request
+ * and the OCSP Response. The ASN.1 definition for CertID is defined
+ * in RFC 2560 as:
+ * <pre>
+ *
+ * CertID          ::=     SEQUENCE {
+ *      hashAlgorithm       AlgorithmIdentifier,
+ *      issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+ *      issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+ *      serialNumber        CertificateSerialNumber
+ *      }
+ *
+ * </pre>
+ *
+ * @author      Ram Marti
+ */
+
+public class CertId {
+
+    private static final boolean debug = false;
+    private static final AlgorithmId SHA1_ALGID
+        = new AlgorithmId(AlgorithmId.SHA_oid);
+    private final AlgorithmId hashAlgId;
+    private final byte[] issuerNameHash;
+    private final byte[] issuerKeyHash;
+    private final SerialNumber certSerialNumber;
+    private int myhash = -1; // hashcode for this CertId
+
+    /**
+     * Creates a CertId. The hash algorithm used is SHA-1.
+     */
+    public CertId(X509Certificate issuerCert, SerialNumber serialNumber)
+        throws IOException {
+
+        this(issuerCert.getSubjectX500Principal(),
+             issuerCert.getPublicKey(), serialNumber);
+    }
+
+    public CertId(X500Principal issuerName, PublicKey issuerKey,
+                  SerialNumber serialNumber) throws IOException {
+
+        // compute issuerNameHash
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("SHA1");
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new IOException("Unable to create CertId", nsae);
+        }
+        hashAlgId = SHA1_ALGID;
+        md.update(issuerName.getEncoded());
+        issuerNameHash = md.digest();
+
+        // compute issuerKeyHash (remove the tag and length)
+        byte[] pubKey = issuerKey.getEncoded();
+        DerValue val = new DerValue(pubKey);
+        DerValue[] seq = new DerValue[2];
+        seq[0] = val.data.getDerValue(); // AlgorithmID
+        seq[1] = val.data.getDerValue(); // Key
+        byte[] keyBytes = seq[1].getBitString();
+        md.update(keyBytes);
+        issuerKeyHash = md.digest();
+        certSerialNumber = serialNumber;
+
+        if (debug) {
+            HexDumpEncoder encoder = new HexDumpEncoder();
+            System.out.println("Issuer Name is " + issuerName);
+            System.out.println("issuerNameHash is " +
+                encoder.encodeBuffer(issuerNameHash));
+            System.out.println("issuerKeyHash is " +
+                encoder.encodeBuffer(issuerKeyHash));
+            System.out.println("SerialNumber is " + serialNumber.getNumber());
+        }
+    }
+
+    /**
+     * Creates a CertId from its ASN.1 DER encoding.
+     */
+    public CertId(DerInputStream derIn) throws IOException {
+        hashAlgId = AlgorithmId.parse(derIn.getDerValue());
+        issuerNameHash = derIn.getOctetString();
+        issuerKeyHash = derIn.getOctetString();
+        certSerialNumber = new SerialNumber(derIn);
+    }
+
+    /**
+     * Return the hash algorithm identifier.
+     */
+    public AlgorithmId getHashAlgorithm() {
+        return hashAlgId;
+    }
+
+    /**
+     * Return the hash value for the issuer name.
+     */
+    public byte[] getIssuerNameHash() {
+        return issuerNameHash;
+    }
+
+    /**
+     * Return the hash value for the issuer key.
+     */
+    public byte[] getIssuerKeyHash() {
+        return issuerKeyHash;
+    }
+
+    /**
+     * Return the serial number.
+     */
+    public BigInteger getSerialNumber() {
+        return certSerialNumber.getNumber();
+    }
+
+    /**
+     * Encode the CertId using ASN.1 DER.
+     * The hash algorithm used is SHA-1.
+     */
+    public void encode(DerOutputStream out) throws IOException {
+
+        DerOutputStream tmp = new DerOutputStream();
+        hashAlgId.encode(tmp);
+        tmp.putOctetString(issuerNameHash);
+        tmp.putOctetString(issuerKeyHash);
+        certSerialNumber.encode(tmp);
+        out.write(DerValue.tag_Sequence, tmp);
+
+        if (debug) {
+            HexDumpEncoder encoder = new HexDumpEncoder();
+            System.out.println("Encoded certId is " +
+                encoder.encode(out.toByteArray()));
+        }
+    }
+
+   /**
+     * Returns a hashcode value for this CertId.
+     *
+     * @return the hashcode value.
+     */
+    @Override public int hashCode() {
+        if (myhash == -1) {
+            myhash = hashAlgId.hashCode();
+            for (int i = 0; i < issuerNameHash.length; i++) {
+                myhash += issuerNameHash[i] * i;
+            }
+            for (int i = 0; i < issuerKeyHash.length; i++) {
+                myhash += issuerKeyHash[i] * i;
+            }
+            myhash += certSerialNumber.getNumber().hashCode();
+        }
+        return myhash;
+    }
+
+    /**
+     * Compares this CertId for equality with the specified
+     * object. Two CertId objects are considered equal if their hash algorithms,
+     * their issuer name and issuer key hash values and their serial numbers
+     * are equal.
+     *
+     * @param other the object to test for equality with this object.
+     * @return true if the objects are considered equal, false otherwise.
+     */
+    @Override public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other == null || (!(other instanceof CertId))) {
+            return false;
+        }
+
+        CertId that = (CertId) other;
+        if (hashAlgId.equals(that.getHashAlgorithm()) &&
+            Arrays.equals(issuerNameHash, that.getIssuerNameHash()) &&
+            Arrays.equals(issuerKeyHash, that.getIssuerKeyHash()) &&
+            certSerialNumber.getNumber().equals(that.getSerialNumber())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Create a string representation of the CertId.
+     */
+    @Override public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("CertId \n");
+        sb.append("Algorithm: " + hashAlgId.toString() +"\n");
+        sb.append("issuerNameHash \n");
+        HexDumpEncoder encoder = new HexDumpEncoder();
+        sb.append(encoder.encode(issuerNameHash));
+        sb.append("\nissuerKeyHash: \n");
+        sb.append(encoder.encode(issuerKeyHash));
+        sb.append("\n" +  certSerialNumber.toString());
+        return sb.toString();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/CertPathHelper.java b/ojluni/src/main/java/sun/security/provider/certpath/CertPathHelper.java
old mode 100755
new mode 100644
index bd3a342..af6da8a
--- a/ojluni/src/main/java/sun/security/provider/certpath/CertPathHelper.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/CertPathHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/CertStoreHelper.java b/ojluni/src/main/java/sun/security/provider/certpath/CertStoreHelper.java
new file mode 100644
index 0000000..b8b562e
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/CertStoreHelper.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509CRLSelector;
+import javax.security.auth.x500.X500Principal;
+import java.io.IOException;
+
+import sun.security.util.Cache;
+
+/**
+ * Helper used by URICertStore and others when delegating to another CertStore
+ * to fetch certs and CRLs.
+ */
+
+public abstract class CertStoreHelper {
+
+    private static final int NUM_TYPES = 2;
+    private final static Map<String,String> classMap = new HashMap<>(NUM_TYPES);
+    static {
+        classMap.put(
+            "LDAP",
+            "sun.security.provider.certpath.ldap.LDAPCertStoreHelper");
+        classMap.put(
+            "SSLServer",
+            "sun.security.provider.certpath.ssl.SSLServerCertStoreHelper");
+    };
+    private static Cache<String, CertStoreHelper> cache
+        = Cache.newSoftMemoryCache(NUM_TYPES);
+
+    public static CertStoreHelper getInstance(final String type)
+        throws NoSuchAlgorithmException
+    {
+        CertStoreHelper helper = cache.get(type);
+        if (helper != null) {
+            return helper;
+        }
+        final String cl = classMap.get(type);
+        if (cl == null) {
+            throw new NoSuchAlgorithmException(type + " not available");
+        }
+        try {
+            helper = AccessController.doPrivileged(
+                new PrivilegedExceptionAction<CertStoreHelper>() {
+                    public CertStoreHelper run() throws ClassNotFoundException {
+                        try {
+                            Class<?> c = Class.forName(cl, true, null);
+                            CertStoreHelper csh
+                                = (CertStoreHelper)c.newInstance();
+                            cache.put(type, csh);
+                            return csh;
+                        } catch (InstantiationException |
+                                 IllegalAccessException e) {
+                            throw new AssertionError(e);
+                        }
+                    }
+            });
+            return helper;
+        } catch (PrivilegedActionException e) {
+            throw new NoSuchAlgorithmException(type + " not available",
+                                               e.getException());
+        }
+    }
+
+    static boolean isCausedByNetworkIssue(String type, CertStoreException cse) {
+        switch (type) {
+            case "LDAP":
+            case "SSLServer":
+                try {
+                    CertStoreHelper csh = CertStoreHelper.getInstance(type);
+                    return csh.isCausedByNetworkIssue(cse);
+                } catch (NoSuchAlgorithmException nsae) {
+                    return false;
+                }
+            case "URI":
+                Throwable t = cse.getCause();
+                return (t != null && t instanceof IOException);
+            default:
+                // we don't know about any other remote CertStore types
+                return false;
+        }
+    }
+
+    /**
+     * Returns a CertStore using the given URI as parameters.
+     */
+    public abstract CertStore getCertStore(URI uri)
+        throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
+
+    /**
+     * Wraps an existing X509CertSelector when needing to avoid DN matching
+     * issues.
+     */
+    public abstract X509CertSelector wrap(X509CertSelector selector,
+                          X500Principal certSubject,
+                          String dn)
+        throws IOException;
+
+    /**
+     * Wraps an existing X509CRLSelector when needing to avoid DN matching
+     * issues.
+     */
+    public abstract X509CRLSelector wrap(X509CRLSelector selector,
+                         Collection<X500Principal> certIssuers,
+                         String dn)
+        throws IOException;
+
+    /**
+     * Returns true if the cause of the CertStoreException is a network
+     * related issue.
+     */
+    public abstract boolean isCausedByNetworkIssue(CertStoreException e);
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/CollectionCertStore.java b/ojluni/src/main/java/sun/security/provider/certpath/CollectionCertStore.java
new file mode 100644
index 0000000..f991434
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/CollectionCertStore.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.Certificate;
+import java.security.cert.CRL;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.HashSet;
+import java.security.cert.CertSelector;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.CRLSelector;
+import java.security.cert.CertStoreSpi;
+
+/**
+ * A <code>CertStore</code> that retrieves <code>Certificates</code> and
+ * <code>CRL</code>s from a <code>Collection</code>.
+ * <p>
+ * Before calling the {@link #engineGetCertificates engineGetCertificates} or
+ * {@link #engineGetCRLs engineGetCRLs} methods, the
+ * {@link #CollectionCertStore(CertStoreParameters)
+ * CollectionCertStore(CertStoreParameters)} constructor is called to
+ * create the <code>CertStore</code> and establish the
+ * <code>Collection</code> from which <code>Certificate</code>s and
+ * <code>CRL</code>s will be retrieved. If the specified
+ * <code>Collection</code> contains an object that is not a
+ * <code>Certificate</code> or <code>CRL</code>, that object will be
+ * ignored.
+ * <p>
+ * <b>Concurrent Access</b>
+ * <p>
+ * As described in the javadoc for <code>CertStoreSpi</code>, the
+ * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods
+ * must be thread-safe. That is, multiple threads may concurrently
+ * invoke these methods on a single <code>CollectionCertStore</code>
+ * object (or more than one) with no ill effects.
+ * <p>
+ * This is achieved by requiring that the <code>Collection</code> passed to
+ * the {@link #CollectionCertStore(CertStoreParameters)
+ * CollectionCertStore(CertStoreParameters)} constructor (via the
+ * <code>CollectionCertStoreParameters</code> object) must have fail-fast
+ * iterators. Simultaneous modifications to the <code>Collection</code> can thus be
+ * detected and certificate or CRL retrieval can be retried. The fact that
+ * <code>Certificate</code>s and <code>CRL</code>s must be thread-safe is also
+ * essential.
+ *
+ * @see java.security.cert.CertStore
+ *
+ * @since       1.4
+ * @author      Steve Hanna
+ */
+public class CollectionCertStore extends CertStoreSpi {
+
+    private Collection<?> coll;
+
+    /**
+     * Creates a <code>CertStore</code> with the specified parameters.
+     * For this class, the parameters object must be an instance of
+     * <code>CollectionCertStoreParameters</code>. The <code>Collection</code>
+     * included in the <code>CollectionCertStoreParameters</code> object
+     * must be thread-safe.
+     *
+     * @param params the algorithm parameters
+     * @exception InvalidAlgorithmParameterException if params is not an
+     *   instance of <code>CollectionCertStoreParameters</code>
+     */
+    public CollectionCertStore(CertStoreParameters params)
+        throws InvalidAlgorithmParameterException
+    {
+        super(params);
+        if (!(params instanceof CollectionCertStoreParameters))
+            throw new InvalidAlgorithmParameterException(
+                "parameters must be CollectionCertStoreParameters");
+        coll = ((CollectionCertStoreParameters) params).getCollection();
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>Certificate</code>s that
+     * match the specified selector. If no <code>Certificate</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     *
+     * @param selector a <code>CertSelector</code> used to select which
+     *  <code>Certificate</code>s should be returned. Specify <code>null</code>
+     *  to return all <code>Certificate</code>s.
+     * @return a <code>Collection</code> of <code>Certificate</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    @Override
+    public Collection<Certificate> engineGetCertificates
+            (CertSelector selector) throws CertStoreException {
+        if (coll == null) {
+            throw new CertStoreException("Collection is null");
+        }
+        // Tolerate a few ConcurrentModificationExceptions
+        for (int c = 0; c < 10; c++) {
+            try {
+                HashSet<Certificate> result = new HashSet<>();
+                if (selector != null) {
+                    for (Object o : coll) {
+                        if ((o instanceof Certificate) &&
+                            selector.match((Certificate) o))
+                            result.add((Certificate)o);
+                    }
+                } else {
+                    for (Object o : coll) {
+                        if (o instanceof Certificate)
+                            result.add((Certificate)o);
+                    }
+                }
+                return(result);
+            } catch (ConcurrentModificationException e) { }
+        }
+        throw new ConcurrentModificationException("Too many "
+            + "ConcurrentModificationExceptions");
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>CRL</code>s that
+     * match the specified selector. If no <code>CRL</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     *
+     * @param selector a <code>CRLSelector</code> used to select which
+     *  <code>CRL</code>s should be returned. Specify <code>null</code>
+     *  to return all <code>CRL</code>s.
+     * @return a <code>Collection</code> of <code>CRL</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    @Override
+    public Collection<CRL> engineGetCRLs(CRLSelector selector)
+        throws CertStoreException
+    {
+        if (coll == null)
+            throw new CertStoreException("Collection is null");
+
+        // Tolerate a few ConcurrentModificationExceptions
+        for (int c = 0; c < 10; c++) {
+            try {
+                HashSet<CRL> result = new HashSet<>();
+                if (selector != null) {
+                    for (Object o : coll) {
+                        if ((o instanceof CRL) && selector.match((CRL) o))
+                            result.add((CRL)o);
+                    }
+                } else {
+                    for (Object o : coll) {
+                        if (o instanceof CRL)
+                            result.add((CRL)o);
+                    }
+                }
+                return result;
+            } catch (ConcurrentModificationException e) { }
+        }
+        throw new ConcurrentModificationException("Too many "
+            + "ConcurrentModificationExceptions");
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ConstraintsChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/ConstraintsChecker.java
new file mode 100644
index 0000000..a08c990
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ConstraintsChecker.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXReason;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import sun.security.util.Debug;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.x509.NameConstraintsExtension;
+import sun.security.x509.X509CertImpl;
+
+/**
+ * ConstraintsChecker is a <code>PKIXCertPathChecker</code> that checks
+ * constraints information on a PKIX certificate, namely basic constraints
+ * and name constraints.
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+class ConstraintsChecker extends PKIXCertPathChecker {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    /* length of cert path */
+    private final int certPathLength;
+    /* current maximum path length (as defined in PKIX) */
+    private int maxPathLength;
+    /* current index of cert */
+    private int i;
+    private NameConstraintsExtension prevNC;
+
+    private Set<String> supportedExts;
+
+    /**
+     * Creates a ConstraintsChecker.
+     *
+     * @param certPathLength the length of the certification path
+     */
+    ConstraintsChecker(int certPathLength) {
+        this.certPathLength = certPathLength;
+    }
+
+    @Override
+    public void init(boolean forward) throws CertPathValidatorException {
+        if (!forward) {
+            i = 0;
+            maxPathLength = certPathLength;
+            prevNC = null;
+        } else {
+            throw new CertPathValidatorException
+                ("forward checking not supported");
+        }
+    }
+
+    @Override
+    public boolean isForwardCheckingSupported() {
+        return false;
+    }
+
+    @Override
+    public Set<String> getSupportedExtensions() {
+        if (supportedExts == null) {
+            supportedExts = new HashSet<String>(2);
+            supportedExts.add(BasicConstraints_Id.toString());
+            supportedExts.add(NameConstraints_Id.toString());
+            supportedExts = Collections.unmodifiableSet(supportedExts);
+        }
+        return supportedExts;
+    }
+
+    /**
+     * Performs the basic constraints and name constraints
+     * checks on the certificate using its internal state.
+     *
+     * @param cert the <code>Certificate</code> to be checked
+     * @param unresCritExts a <code>Collection</code> of OID strings
+     *        representing the current set of unresolved critical extensions
+     * @throws CertPathValidatorException if the specified certificate
+     *         does not pass the check
+     */
+    @Override
+    public void check(Certificate cert, Collection<String> unresCritExts)
+        throws CertPathValidatorException
+    {
+        X509Certificate currCert = (X509Certificate)cert;
+
+        i++;
+        // MUST run NC check second, since it depends on BC check to
+        // update remainingCerts
+        checkBasicConstraints(currCert);
+        verifyNameConstraints(currCert);
+
+        if (unresCritExts != null && !unresCritExts.isEmpty()) {
+            unresCritExts.remove(BasicConstraints_Id.toString());
+            unresCritExts.remove(NameConstraints_Id.toString());
+        }
+    }
+
+    /**
+     * Internal method to check the name constraints against a cert
+     */
+    private void verifyNameConstraints(X509Certificate currCert)
+        throws CertPathValidatorException
+    {
+        String msg = "name constraints";
+        if (debug != null) {
+            debug.println("---checking " + msg + "...");
+        }
+
+        // check name constraints only if there is a previous name constraint
+        // and either the currCert is the final cert or the currCert is not
+        // self-issued
+        if (prevNC != null && ((i == certPathLength) ||
+                !X509CertImpl.isSelfIssued(currCert))) {
+            if (debug != null) {
+                debug.println("prevNC = " + prevNC);
+                debug.println("currDN = " + currCert.getSubjectX500Principal());
+            }
+
+            try {
+                if (!prevNC.verify(currCert)) {
+                    throw new CertPathValidatorException(msg + " check failed",
+                        null, null, -1, PKIXReason.INVALID_NAME);
+                }
+            } catch (IOException ioe) {
+                throw new CertPathValidatorException(ioe);
+            }
+        }
+
+        // merge name constraints regardless of whether cert is self-issued
+        prevNC = mergeNameConstraints(currCert, prevNC);
+
+        if (debug != null)
+            debug.println(msg + " verified.");
+    }
+
+    /**
+     * Helper to fold sets of name constraints together
+     */
+    static NameConstraintsExtension mergeNameConstraints(
+        X509Certificate currCert, NameConstraintsExtension prevNC)
+        throws CertPathValidatorException
+    {
+        X509CertImpl currCertImpl;
+        try {
+            currCertImpl = X509CertImpl.toImpl(currCert);
+        } catch (CertificateException ce) {
+            throw new CertPathValidatorException(ce);
+        }
+
+        NameConstraintsExtension newConstraints =
+            currCertImpl.getNameConstraintsExtension();
+
+        if (debug != null) {
+            debug.println("prevNC = " + prevNC);
+            debug.println("newNC = " + String.valueOf(newConstraints));
+        }
+
+        // if there are no previous name constraints, we just return the
+        // new name constraints.
+        if (prevNC == null) {
+            if (debug != null) {
+                debug.println("mergedNC = " + String.valueOf(newConstraints));
+            }
+            if (newConstraints == null) {
+                return newConstraints;
+            } else {
+                // Make sure we do a clone here, because we're probably
+                // going to modify this object later and we don't want to
+                // be sharing it with a Certificate object!
+                return (NameConstraintsExtension)newConstraints.clone();
+            }
+        } else {
+            try {
+                // after merge, prevNC should contain the merged constraints
+                prevNC.merge(newConstraints);
+            } catch (IOException ioe) {
+                throw new CertPathValidatorException(ioe);
+            }
+            if (debug != null) {
+                debug.println("mergedNC = " + prevNC);
+            }
+            return prevNC;
+        }
+    }
+
+    /**
+     * Internal method to check that a given cert meets basic constraints.
+     */
+    private void checkBasicConstraints(X509Certificate currCert)
+        throws CertPathValidatorException
+    {
+        String msg = "basic constraints";
+        if (debug != null) {
+            debug.println("---checking " + msg + "...");
+            debug.println("i = " + i);
+            debug.println("maxPathLength = " + maxPathLength);
+        }
+
+        /* check if intermediate cert */
+        if (i < certPathLength) {
+            // RFC5280: If certificate i is a version 3 certificate, verify
+            // that the basicConstraints extension is present and that cA is
+            // set to TRUE.  (If certificate i is a version 1 or version 2
+            // certificate, then the application MUST either verify that
+            // certificate i is a CA certificate through out-of-band means
+            // or reject the certificate.  Conforming implementations may
+            // choose to reject all version 1 and version 2 intermediate
+            // certificates.)
+            //
+            // We choose to reject all version 1 and version 2 intermediate
+            // certificates except that it is self issued by the trust
+            // anchor in order to support key rollover or changes in
+            // certificate policies.
+            int pathLenConstraint = -1;
+            if (currCert.getVersion() < 3) {    // version 1 or version 2
+                if (i == 1) {                   // issued by a trust anchor
+                    if (X509CertImpl.isSelfIssued(currCert)) {
+                        pathLenConstraint = Integer.MAX_VALUE;
+                    }
+                }
+            } else {
+                pathLenConstraint = currCert.getBasicConstraints();
+            }
+
+            if (pathLenConstraint == -1) {
+                throw new CertPathValidatorException
+                    (msg + " check failed: this is not a CA certificate",
+                     null, null, -1, PKIXReason.NOT_CA_CERT);
+            }
+
+            if (!X509CertImpl.isSelfIssued(currCert)) {
+                if (maxPathLength <= 0) {
+                   throw new CertPathValidatorException
+                        (msg + " check failed: pathLenConstraint violated - "
+                         + "this cert must be the last cert in the "
+                         + "certification path", null, null, -1,
+                         PKIXReason.PATH_TOO_LONG);
+                }
+                maxPathLength--;
+            }
+            if (pathLenConstraint < maxPathLength)
+                maxPathLength = pathLenConstraint;
+        }
+
+        if (debug != null) {
+            debug.println("after processing, maxPathLength = " + maxPathLength);
+            debug.println(msg + " verified.");
+        }
+    }
+
+    /**
+     * Merges the specified maxPathLength with the pathLenConstraint
+     * obtained from the certificate.
+     *
+     * @param cert the <code>X509Certificate</code>
+     * @param maxPathLength the previous maximum path length
+     * @return the new maximum path length constraint (-1 means no more
+     * certificates can follow, Integer.MAX_VALUE means path length is
+     * unconstrained)
+     */
+    static int mergeBasicConstraints(X509Certificate cert, int maxPathLength) {
+
+        int pathLenConstraint = cert.getBasicConstraints();
+
+        if (!X509CertImpl.isSelfIssued(cert)) {
+            maxPathLength--;
+        }
+
+        if (pathLenConstraint < maxPathLength) {
+            maxPathLength = pathLenConstraint;
+        }
+
+        return maxPathLength;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java b/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java
new file mode 100644
index 0000000..ecf609b
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.*;
+import java.net.URI;
+import java.security.*;
+import java.security.cert.*;
+import javax.security.auth.x500.X500Principal;
+import java.util.*;
+
+import sun.security.util.Debug;
+import sun.security.util.DerOutputStream;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.x509.*;
+
+/**
+ * Class to obtain CRLs via the CRLDistributionPoints extension.
+ * Note that the functionality of this class must be explicitly enabled
+ * via a system property, see the USE_CRLDP variable below.
+ *
+ * This class uses the URICertStore class to fetch CRLs. The URICertStore
+ * class also implements CRL caching: see the class description for more
+ * information.
+ *
+ * @author Andreas Sterbenz
+ * @author Sean Mullan
+ * @since 1.4.2
+ */
+public class DistributionPointFetcher {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    private static final boolean[] ALL_REASONS =
+        {true, true, true, true, true, true, true, true, true};
+
+    /**
+     * Private instantiation only.
+     */
+    private DistributionPointFetcher() {}
+
+    /**
+     * Return the X509CRLs matching this selector. The selector must be
+     * an X509CRLSelector with certificateChecking set.
+     */
+    public static Collection<X509CRL> getCRLs(X509CRLSelector selector,
+                                              boolean signFlag,
+                                              PublicKey prevKey,
+                                              String provider,
+                                              List<CertStore> certStores,
+                                              boolean[] reasonsMask,
+                                              Set<TrustAnchor> trustAnchors,
+                                              Date validity)
+        throws CertStoreException
+    {
+        return getCRLs(selector, signFlag, prevKey, null, provider, certStores,
+                       reasonsMask, trustAnchors, validity);
+    }
+
+    /**
+     * Return the X509CRLs matching this selector. The selector must be
+     * an X509CRLSelector with certificateChecking set.
+     */
+    public static Collection<X509CRL> getCRLs(X509CRLSelector selector,
+                                              boolean signFlag,
+                                              PublicKey prevKey,
+                                              X509Certificate prevCert,
+                                              String provider,
+                                              List<CertStore> certStores,
+                                              boolean[] reasonsMask,
+                                              Set<TrustAnchor> trustAnchors,
+                                              Date validity)
+        throws CertStoreException
+    {
+        X509Certificate cert = selector.getCertificateChecking();
+        if (cert == null) {
+            return Collections.emptySet();
+        }
+        try {
+            X509CertImpl certImpl = X509CertImpl.toImpl(cert);
+            if (debug != null) {
+                debug.println("DistributionPointFetcher.getCRLs: Checking "
+                        + "CRLDPs for " + certImpl.getSubjectX500Principal());
+            }
+            CRLDistributionPointsExtension ext =
+                certImpl.getCRLDistributionPointsExtension();
+            if (ext == null) {
+                if (debug != null) {
+                    debug.println("No CRLDP ext");
+                }
+                return Collections.emptySet();
+            }
+            List<DistributionPoint> points =
+                    ext.get(CRLDistributionPointsExtension.POINTS);
+            Set<X509CRL> results = new HashSet<>();
+            for (Iterator<DistributionPoint> t = points.iterator();
+                 t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) {
+                DistributionPoint point = t.next();
+                Collection<X509CRL> crls = getCRLs(selector, certImpl,
+                    point, reasonsMask, signFlag, prevKey, prevCert, provider,
+                    certStores, trustAnchors, validity);
+                results.addAll(crls);
+            }
+            if (debug != null) {
+                debug.println("Returning " + results.size() + " CRLs");
+            }
+            return results;
+        } catch (CertificateException | IOException e) {
+            return Collections.emptySet();
+        }
+    }
+
+    /**
+     * Download CRLs from the given distribution point, verify and return them.
+     * See the top of the class for current limitations.
+     *
+     * @throws CertStoreException if there is an error retrieving the CRLs
+     *         from one of the GeneralNames and no other CRLs are retrieved from
+     *         the other GeneralNames. If more than one GeneralName throws an
+     *         exception then the one from the last GeneralName is thrown.
+     */
+    private static Collection<X509CRL> getCRLs(X509CRLSelector selector,
+        X509CertImpl certImpl, DistributionPoint point, boolean[] reasonsMask,
+        boolean signFlag, PublicKey prevKey, X509Certificate prevCert,
+        String provider, List<CertStore> certStores,
+        Set<TrustAnchor> trustAnchors, Date validity)
+            throws CertStoreException {
+
+        // check for full name
+        GeneralNames fullName = point.getFullName();
+        if (fullName == null) {
+            // check for relative name
+            RDN relativeName = point.getRelativeName();
+            if (relativeName == null) {
+                return Collections.emptySet();
+            }
+            try {
+                GeneralNames crlIssuers = point.getCRLIssuer();
+                if (crlIssuers == null) {
+                    fullName = getFullNames
+                        ((X500Name) certImpl.getIssuerDN(), relativeName);
+                } else {
+                    // should only be one CRL Issuer
+                    if (crlIssuers.size() != 1) {
+                        return Collections.emptySet();
+                    } else {
+                        fullName = getFullNames
+                            ((X500Name) crlIssuers.get(0).getName(), relativeName);
+                    }
+                }
+            } catch (IOException ioe) {
+                return Collections.emptySet();
+            }
+        }
+        Collection<X509CRL> possibleCRLs = new ArrayList<>();
+        CertStoreException savedCSE = null;
+        for (Iterator<GeneralName> t = fullName.iterator(); t.hasNext(); ) {
+            try {
+                GeneralName name = t.next();
+                if (name.getType() == GeneralNameInterface.NAME_DIRECTORY) {
+                    X500Name x500Name = (X500Name) name.getName();
+                    possibleCRLs.addAll(
+                        getCRLs(x500Name, certImpl.getIssuerX500Principal(),
+                                certStores));
+                } else if (name.getType() == GeneralNameInterface.NAME_URI) {
+                    URIName uriName = (URIName)name.getName();
+                    X509CRL crl = getCRL(uriName);
+                    if (crl != null) {
+                        possibleCRLs.add(crl);
+                    }
+                }
+            } catch (CertStoreException cse) {
+                savedCSE = cse;
+            }
+        }
+        // only throw CertStoreException if no CRLs are retrieved
+        if (possibleCRLs.isEmpty() && savedCSE != null) {
+            throw savedCSE;
+        }
+
+        Collection<X509CRL> crls = new ArrayList<>(2);
+        for (X509CRL crl : possibleCRLs) {
+            try {
+                // make sure issuer is not set
+                // we check the issuer in verifyCRLs method
+                selector.setIssuerNames(null);
+                if (selector.match(crl) && verifyCRL(certImpl, point, crl,
+                        reasonsMask, signFlag, prevKey, prevCert, provider,
+                        trustAnchors, certStores, validity)) {
+                    crls.add(crl);
+                }
+            } catch (IOException | CRLException e) {
+                // don't add the CRL
+                if (debug != null) {
+                    debug.println("Exception verifying CRL: " + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+        }
+        return crls;
+    }
+
+    /**
+     * Download CRL from given URI.
+     */
+    private static X509CRL getCRL(URIName name) throws CertStoreException {
+        URI uri = name.getURI();
+        if (debug != null) {
+            debug.println("Trying to fetch CRL from DP " + uri);
+        }
+        CertStore ucs = null;
+        try {
+            ucs = URICertStore.getInstance
+                (new URICertStore.URICertStoreParameters(uri));
+        } catch (InvalidAlgorithmParameterException |
+                 NoSuchAlgorithmException e) {
+            if (debug != null) {
+                debug.println("Can't create URICertStore: " + e.getMessage());
+            }
+            return null;
+        }
+
+        Collection<? extends CRL> crls = ucs.getCRLs(null);
+        if (crls.isEmpty()) {
+            return null;
+        } else {
+            return (X509CRL) crls.iterator().next();
+        }
+    }
+
+    /**
+     * Fetch CRLs from certStores.
+     *
+     * @throws CertStoreException if there is an error retrieving the CRLs from
+     *         one of the CertStores and no other CRLs are retrieved from
+     *         the other CertStores. If more than one CertStore throws an
+     *         exception then the one from the last CertStore is thrown.
+     */
+    private static Collection<X509CRL> getCRLs(X500Name name,
+                                               X500Principal certIssuer,
+                                               List<CertStore> certStores)
+        throws CertStoreException
+    {
+        if (debug != null) {
+            debug.println("Trying to fetch CRL from DP " + name);
+        }
+        X509CRLSelector xcs = new X509CRLSelector();
+        xcs.addIssuer(name.asX500Principal());
+        xcs.addIssuer(certIssuer);
+        Collection<X509CRL> crls = new ArrayList<>();
+        CertStoreException savedCSE = null;
+        for (CertStore store : certStores) {
+            try {
+                for (CRL crl : store.getCRLs(xcs)) {
+                    crls.add((X509CRL)crl);
+                }
+            } catch (CertStoreException cse) {
+                if (debug != null) {
+                    debug.println("Exception while retrieving " +
+                        "CRLs: " + cse);
+                    cse.printStackTrace();
+                }
+                savedCSE = new PKIX.CertStoreTypeException(store.getType(),cse);
+            }
+        }
+        // only throw CertStoreException if no CRLs are retrieved
+        if (crls.isEmpty() && savedCSE != null) {
+            throw savedCSE;
+        } else {
+            return crls;
+        }
+    }
+
+    /**
+     * Verifies a CRL for the given certificate's Distribution Point to
+     * ensure it is appropriate for checking the revocation status.
+     *
+     * @param certImpl the certificate whose revocation status is being checked
+     * @param point one of the distribution points of the certificate
+     * @param crl the CRL
+     * @param reasonsMask the interim reasons mask
+     * @param signFlag true if prevKey can be used to verify the CRL
+     * @param prevKey the public key that verifies the certificate's signature
+     * @param prevCert the certificate whose public key verifies
+     *        {@code certImpl}'s signature
+     * @param provider the Signature provider to use
+     * @param trustAnchors a {@code Set} of {@code TrustAnchor}s
+     * @param certStores a {@code List} of {@code CertStore}s to be used in
+     *        finding certificates and CRLs
+     * @param validity the time for which the validity of the CRL issuer's
+     *        certification path should be determined
+     * @return true if ok, false if not
+     */
+    static boolean verifyCRL(X509CertImpl certImpl, DistributionPoint point,
+        X509CRL crl, boolean[] reasonsMask, boolean signFlag,
+        PublicKey prevKey, X509Certificate prevCert, String provider,
+        Set<TrustAnchor> trustAnchors, List<CertStore> certStores,
+        Date validity) throws CRLException, IOException {
+
+        boolean indirectCRL = false;
+        X509CRLImpl crlImpl = X509CRLImpl.toImpl(crl);
+        IssuingDistributionPointExtension idpExt =
+            crlImpl.getIssuingDistributionPointExtension();
+        X500Name certIssuer = (X500Name) certImpl.getIssuerDN();
+        X500Name crlIssuer = (X500Name) crlImpl.getIssuerDN();
+
+        // if crlIssuer is set, verify that it matches the issuer of the
+        // CRL and the CRL contains an IDP extension with the indirectCRL
+        // boolean asserted. Otherwise, verify that the CRL issuer matches the
+        // certificate issuer.
+        GeneralNames pointCrlIssuers = point.getCRLIssuer();
+        X500Name pointCrlIssuer = null;
+        if (pointCrlIssuers != null) {
+            if (idpExt == null ||
+                ((Boolean) idpExt.get
+                    (IssuingDistributionPointExtension.INDIRECT_CRL)).equals
+                        (Boolean.FALSE)) {
+                return false;
+            }
+            boolean match = false;
+            for (Iterator<GeneralName> t = pointCrlIssuers.iterator();
+                 !match && t.hasNext(); ) {
+                GeneralNameInterface name = t.next().getName();
+                if (crlIssuer.equals(name) == true) {
+                    pointCrlIssuer = (X500Name) name;
+                    match = true;
+                }
+            }
+            if (match == false) {
+                return false;
+            }
+
+            // we accept the case that a CRL issuer provide status
+            // information for itself.
+            if (issues(certImpl, crlImpl, provider)) {
+                // reset the public key used to verify the CRL's signature
+                prevKey = certImpl.getPublicKey();
+            } else {
+                indirectCRL = true;
+            }
+        } else if (crlIssuer.equals(certIssuer) == false) {
+            if (debug != null) {
+                debug.println("crl issuer does not equal cert issuer");
+            }
+            return false;
+        } else {
+            // in case of self-issued indirect CRL issuer.
+            KeyIdentifier certAKID = certImpl.getAuthKeyId();
+            KeyIdentifier crlAKID = crlImpl.getAuthKeyId();
+
+            if (certAKID == null || crlAKID == null) {
+                // cannot recognize indirect CRL without AKID
+
+                // we accept the case that a CRL issuer provide status
+                // information for itself.
+                if (issues(certImpl, crlImpl, provider)) {
+                    // reset the public key used to verify the CRL's signature
+                    prevKey = certImpl.getPublicKey();
+                }
+            } else if (!certAKID.equals(crlAKID)) {
+                // we accept the case that a CRL issuer provide status
+                // information for itself.
+                if (issues(certImpl, crlImpl, provider)) {
+                    // reset the public key used to verify the CRL's signature
+                    prevKey = certImpl.getPublicKey();
+                } else {
+                    indirectCRL = true;
+                }
+            }
+        }
+
+        if (!indirectCRL && !signFlag) {
+            // cert's key cannot be used to verify the CRL
+            return false;
+        }
+
+        if (idpExt != null) {
+            DistributionPointName idpPoint = (DistributionPointName)
+                idpExt.get(IssuingDistributionPointExtension.POINT);
+            if (idpPoint != null) {
+                GeneralNames idpNames = idpPoint.getFullName();
+                if (idpNames == null) {
+                    RDN relativeName = idpPoint.getRelativeName();
+                    if (relativeName == null) {
+                        if (debug != null) {
+                           debug.println("IDP must be relative or full DN");
+                        }
+                        return false;
+                    }
+                    if (debug != null) {
+                        debug.println("IDP relativeName:" + relativeName);
+                    }
+                    idpNames = getFullNames(crlIssuer, relativeName);
+                }
+                // if the DP name is present in the IDP CRL extension and the
+                // DP field is present in the DP, then verify that one of the
+                // names in the IDP matches one of the names in the DP
+                if (point.getFullName() != null ||
+                    point.getRelativeName() != null) {
+                    GeneralNames pointNames = point.getFullName();
+                    if (pointNames == null) {
+                        RDN relativeName = point.getRelativeName();
+                        if (relativeName == null) {
+                            if (debug != null) {
+                                debug.println("DP must be relative or full DN");
+                            }
+                            return false;
+                        }
+                        if (debug != null) {
+                            debug.println("DP relativeName:" + relativeName);
+                        }
+                        if (indirectCRL) {
+                            if (pointCrlIssuers.size() != 1) {
+                                // RFC 3280: there must be only 1 CRL issuer
+                                // name when relativeName is present
+                                if (debug != null) {
+                                    debug.println("must only be one CRL " +
+                                        "issuer when relative name present");
+                                }
+                                return false;
+                            }
+                            pointNames = getFullNames
+                                (pointCrlIssuer, relativeName);
+                        } else {
+                            pointNames = getFullNames(certIssuer, relativeName);
+                        }
+                    }
+                    boolean match = false;
+                    for (Iterator<GeneralName> i = idpNames.iterator();
+                         !match && i.hasNext(); ) {
+                        GeneralNameInterface idpName = i.next().getName();
+                        if (debug != null) {
+                            debug.println("idpName: " + idpName);
+                        }
+                        for (Iterator<GeneralName> p = pointNames.iterator();
+                             !match && p.hasNext(); ) {
+                            GeneralNameInterface pointName = p.next().getName();
+                            if (debug != null) {
+                                debug.println("pointName: " + pointName);
+                            }
+                            match = idpName.equals(pointName);
+                        }
+                    }
+                    if (!match) {
+                        if (debug != null) {
+                            debug.println("IDP name does not match DP name");
+                        }
+                        return false;
+                    }
+                // if the DP name is present in the IDP CRL extension and the
+                // DP field is absent from the DP, then verify that one of the
+                // names in the IDP matches one of the names in the crlIssuer
+                // field of the DP
+                } else {
+                    // verify that one of the names in the IDP matches one of
+                    // the names in the cRLIssuer of the cert's DP
+                    boolean match = false;
+                    for (Iterator<GeneralName> t = pointCrlIssuers.iterator();
+                         !match && t.hasNext(); ) {
+                        GeneralNameInterface crlIssuerName = t.next().getName();
+                        for (Iterator<GeneralName> i = idpNames.iterator();
+                             !match && i.hasNext(); ) {
+                            GeneralNameInterface idpName = i.next().getName();
+                            match = crlIssuerName.equals(idpName);
+                        }
+                    }
+                    if (!match) {
+                        return false;
+                    }
+                }
+            }
+
+            // if the onlyContainsUserCerts boolean is asserted, verify that the
+            // cert is not a CA cert
+            Boolean b = (Boolean)
+                idpExt.get(IssuingDistributionPointExtension.ONLY_USER_CERTS);
+            if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() != -1) {
+                if (debug != null) {
+                    debug.println("cert must be a EE cert");
+                }
+                return false;
+            }
+
+            // if the onlyContainsCACerts boolean is asserted, verify that the
+            // cert is a CA cert
+            b = (Boolean)
+                idpExt.get(IssuingDistributionPointExtension.ONLY_CA_CERTS);
+            if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() == -1) {
+                if (debug != null) {
+                    debug.println("cert must be a CA cert");
+                }
+                return false;
+            }
+
+            // verify that the onlyContainsAttributeCerts boolean is not
+            // asserted
+            b = (Boolean) idpExt.get
+                (IssuingDistributionPointExtension.ONLY_ATTRIBUTE_CERTS);
+            if (b.equals(Boolean.TRUE)) {
+                if (debug != null) {
+                    debug.println("cert must not be an AA cert");
+                }
+                return false;
+            }
+        }
+
+        // compute interim reasons mask
+        boolean[] interimReasonsMask = new boolean[9];
+        ReasonFlags reasons = null;
+        if (idpExt != null) {
+            reasons = (ReasonFlags)
+                idpExt.get(IssuingDistributionPointExtension.REASONS);
+        }
+
+        boolean[] pointReasonFlags = point.getReasonFlags();
+        if (reasons != null) {
+            if (pointReasonFlags != null) {
+                // set interim reasons mask to the intersection of
+                // reasons in the DP and onlySomeReasons in the IDP
+                boolean[] idpReasonFlags = reasons.getFlags();
+                for (int i = 0; i < idpReasonFlags.length; i++) {
+                    if (idpReasonFlags[i] && pointReasonFlags[i]) {
+                        interimReasonsMask[i] = true;
+                    }
+                }
+            } else {
+                // set interim reasons mask to the value of
+                // onlySomeReasons in the IDP (and clone it since we may
+                // modify it)
+                interimReasonsMask = reasons.getFlags().clone();
+            }
+        } else if (idpExt == null || reasons == null) {
+            if (pointReasonFlags != null) {
+                // set interim reasons mask to the value of DP reasons
+                interimReasonsMask = pointReasonFlags.clone();
+            } else {
+                // set interim reasons mask to the special value all-reasons
+                interimReasonsMask = new boolean[9];
+                Arrays.fill(interimReasonsMask, true);
+            }
+        }
+
+        // verify that interim reasons mask includes one or more reasons
+        // not included in the reasons mask
+        boolean oneOrMore = false;
+        for (int i = 0; i < interimReasonsMask.length && !oneOrMore; i++) {
+            if (!reasonsMask[i] && interimReasonsMask[i]) {
+                oneOrMore = true;
+            }
+        }
+        if (!oneOrMore) {
+            return false;
+        }
+
+        // Obtain and validate the certification path for the complete
+        // CRL issuer (if indirect CRL). If a key usage extension is present
+        // in the CRL issuer's certificate, verify that the cRLSign bit is set.
+        if (indirectCRL) {
+            X509CertSelector certSel = new X509CertSelector();
+            certSel.setSubject(crlIssuer.asX500Principal());
+            boolean[] crlSign = {false,false,false,false,false,false,true};
+            certSel.setKeyUsage(crlSign);
+
+            // Currently by default, forward builder does not enable
+            // subject/authority key identifier identifying for target
+            // certificate, instead, it only compares the CRL issuer and
+            // the target certificate subject. If the certificate of the
+            // delegated CRL issuer is a self-issued certificate, the
+            // builder is unable to find the proper CRL issuer by issuer
+            // name only, there is a potential dead loop on finding the
+            // proper issuer. It is of great help to narrow the target
+            // scope down to aware of authority key identifiers in the
+            // selector, for the purposes of breaking the dead loop.
+            AuthorityKeyIdentifierExtension akidext =
+                                            crlImpl.getAuthKeyIdExtension();
+            if (akidext != null) {
+                KeyIdentifier akid = (KeyIdentifier)akidext.get(
+                        AuthorityKeyIdentifierExtension.KEY_ID);
+                if (akid != null) {
+                    DerOutputStream derout = new DerOutputStream();
+                    derout.putOctetString(akid.getIdentifier());
+                    certSel.setSubjectKeyIdentifier(derout.toByteArray());
+                }
+
+                SerialNumber asn = (SerialNumber)akidext.get(
+                        AuthorityKeyIdentifierExtension.SERIAL_NUMBER);
+                if (asn != null) {
+                    certSel.setSerialNumber(asn.getNumber());
+                }
+                // the subject criterion will be set by builder automatically.
+            }
+
+            // By now, we have validated the previous certificate, so we can
+            // trust it during the validation of the CRL issuer.
+            // In addition to the performance improvement, another benefit is to
+            // break the dead loop while looking for the issuer back and forth
+            // between the delegated self-issued certificate and its issuer.
+            Set<TrustAnchor> newTrustAnchors = new HashSet<>(trustAnchors);
+
+            if (prevKey != null) {
+                // Add the previous certificate as a trust anchor.
+                // If prevCert is not null, we want to construct a TrustAnchor
+                // using the cert object because when the certpath for the CRL
+                // is built later, the CertSelector will make comparisons with
+                // the TrustAnchor's trustedCert member rather than its pubKey.
+                TrustAnchor temporary;
+                if (prevCert != null) {
+                    temporary = new TrustAnchor(prevCert, null);
+                } else {
+                    X500Principal principal = certImpl.getIssuerX500Principal();
+                    temporary = new TrustAnchor(principal, prevKey, null);
+                }
+                newTrustAnchors.add(temporary);
+            }
+
+            PKIXBuilderParameters params = null;
+            try {
+                params = new PKIXBuilderParameters(newTrustAnchors, certSel);
+            } catch (InvalidAlgorithmParameterException iape) {
+                throw new CRLException(iape);
+            }
+            params.setCertStores(certStores);
+            params.setSigProvider(provider);
+            params.setDate(validity);
+            try {
+                CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
+                PKIXCertPathBuilderResult result =
+                    (PKIXCertPathBuilderResult) builder.build(params);
+                prevKey = result.getPublicKey();
+            } catch (GeneralSecurityException e) {
+                throw new CRLException(e);
+            }
+        }
+
+        // check the crl signature algorithm
+        try {
+            AlgorithmChecker.check(prevKey, crl);
+        } catch (CertPathValidatorException cpve) {
+            if (debug != null) {
+                debug.println("CRL signature algorithm check failed: " + cpve);
+            }
+            return false;
+        }
+
+        // validate the signature on the CRL
+        try {
+            crl.verify(prevKey, provider);
+        } catch (GeneralSecurityException e) {
+            if (debug != null) {
+                debug.println("CRL signature failed to verify");
+            }
+            return false;
+        }
+
+        // reject CRL if any unresolved critical extensions remain in the CRL.
+        Set<String> unresCritExts = crl.getCriticalExtensionOIDs();
+        // remove any that we have processed
+        if (unresCritExts != null) {
+            unresCritExts.remove(IssuingDistributionPoint_Id.toString());
+            if (!unresCritExts.isEmpty()) {
+                if (debug != null) {
+                    debug.println("Unrecognized critical extension(s) in CRL: "
+                        + unresCritExts);
+                    for (String ext : unresCritExts) {
+                        debug.println(ext);
+                    }
+                }
+                return false;
+            }
+        }
+
+        // update reasonsMask
+        for (int i = 0; i < interimReasonsMask.length; i++) {
+            if (!reasonsMask[i] && interimReasonsMask[i]) {
+                reasonsMask[i] = true;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Append relative name to the issuer name and return a new
+     * GeneralNames object.
+     */
+    private static GeneralNames getFullNames(X500Name issuer, RDN rdn)
+        throws IOException
+    {
+        List<RDN> rdns = new ArrayList<>(issuer.rdns());
+        rdns.add(rdn);
+        X500Name fullName = new X500Name(rdns.toArray(new RDN[0]));
+        GeneralNames fullNames = new GeneralNames();
+        fullNames.add(new GeneralName(fullName));
+        return fullNames;
+    }
+
+    /**
+     * Verifies whether a CRL is issued by a certain certificate
+     *
+     * @param cert the certificate
+     * @param crl the CRL to be verified
+     * @param provider the name of the signature provider
+     */
+    private static boolean issues(X509CertImpl cert, X509CRLImpl crl,
+                                  String provider) throws IOException
+    {
+        boolean matched = false;
+
+        AdaptableX509CertSelector issuerSelector =
+                                    new AdaptableX509CertSelector();
+
+        // check certificate's key usage
+        boolean[] usages = cert.getKeyUsage();
+        if (usages != null) {
+            usages[6] = true;       // cRLSign
+            issuerSelector.setKeyUsage(usages);
+        }
+
+        // check certificate's subject
+        X500Principal crlIssuer = crl.getIssuerX500Principal();
+        issuerSelector.setSubject(crlIssuer);
+
+        /*
+         * Facilitate certification path construction with authority
+         * key identifier and subject key identifier.
+         *
+         * In practice, conforming CAs MUST use the key identifier method,
+         * and MUST include authority key identifier extension in all CRLs
+         * issued. [section 5.2.1, RFC 2459]
+         */
+        AuthorityKeyIdentifierExtension crlAKID = crl.getAuthKeyIdExtension();
+        if (crlAKID != null) {
+            issuerSelector.parseAuthorityKeyIdentifierExtension(crlAKID);
+        }
+
+        matched = issuerSelector.match(cert);
+
+        // if AKID is unreliable, verify the CRL signature with the cert
+        if (matched && (crlAKID == null ||
+                cert.getAuthorityKeyIdentifierExtension() == null)) {
+            try {
+                crl.verify(cert.getPublicKey(), provider);
+                matched = true;
+            } catch (GeneralSecurityException e) {
+                matched = false;
+            }
+        }
+
+        return matched;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java b/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java
new file mode 100644
index 0000000..9523e9c
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXReason;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509CertSelector;
+import java.util.*;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.provider.certpath.PKIX.BuilderParams;
+import sun.security.util.Debug;
+import sun.security.x509.AccessDescription;
+import sun.security.x509.AuthorityInfoAccessExtension;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.x509.X500Name;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+
+/**
+ * This class represents a forward builder, which is able to retrieve
+ * matching certificates from CertStores and verify a particular certificate
+ * against a ForwardState.
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ * @author      Sean Mullan
+ */
+class ForwardBuilder extends Builder {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private final Set<X509Certificate> trustedCerts;
+    private final Set<X500Principal> trustedSubjectDNs;
+    private final Set<TrustAnchor> trustAnchors;
+    private X509CertSelector eeSelector;
+    private AdaptableX509CertSelector caSelector;
+    private X509CertSelector caTargetSelector;
+    TrustAnchor trustAnchor;
+    private Comparator<X509Certificate> comparator;
+    private boolean searchAllCertStores = true;
+
+    /**
+     * Initialize the builder with the input parameters.
+     *
+     * @param params the parameter set used to build a certification path
+     */
+    ForwardBuilder(BuilderParams buildParams, boolean searchAllCertStores) {
+        super(buildParams);
+
+        // populate sets of trusted certificates and subject DNs
+        trustAnchors = buildParams.trustAnchors();
+        trustedCerts = new HashSet<X509Certificate>(trustAnchors.size());
+        trustedSubjectDNs = new HashSet<X500Principal>(trustAnchors.size());
+        for (TrustAnchor anchor : trustAnchors) {
+            X509Certificate trustedCert = anchor.getTrustedCert();
+            if (trustedCert != null) {
+                trustedCerts.add(trustedCert);
+                trustedSubjectDNs.add(trustedCert.getSubjectX500Principal());
+            } else {
+                trustedSubjectDNs.add(anchor.getCA());
+            }
+        }
+        comparator = new PKIXCertComparator(trustedSubjectDNs);
+        this.searchAllCertStores = searchAllCertStores;
+    }
+
+    /**
+     * Retrieves all certs from the specified CertStores that 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>ForwardState</code>
+     * @param certStores list of CertStores
+     */
+    @Override
+    Collection<X509Certificate> getMatchingCerts(State currentState,
+                                                 List<CertStore> certStores)
+        throws CertStoreException, CertificateException, IOException
+    {
+        if (debug != null) {
+            debug.println("ForwardBuilder.getMatchingCerts()...");
+        }
+
+        ForwardState currState = (ForwardState) currentState;
+
+        /*
+         * We store certs in a Set because we don't want duplicates.
+         * As each cert is added, it is sorted based on the PKIXCertComparator
+         * algorithm.
+         */
+        Set<X509Certificate> certs = new TreeSet<>(comparator);
+
+        /*
+         * Only look for EE certs if search has just started.
+         */
+        if (currState.isInitial()) {
+            getMatchingEECerts(currState, certStores, certs);
+        }
+        getMatchingCACerts(currState, certStores, certs);
+
+        return certs;
+    }
+
+    /*
+     * Retrieves all end-entity certificates which satisfy constraints
+     * and requirements specified in the parameters and PKIX state.
+     */
+    private void getMatchingEECerts(ForwardState currentState,
+                                    List<CertStore> certStores,
+                                    Collection<X509Certificate> eeCerts)
+        throws IOException
+    {
+        if (debug != null) {
+            debug.println("ForwardBuilder.getMatchingEECerts()...");
+        }
+        /*
+         * Compose a certificate matching rule to filter out
+         * certs which don't satisfy constraints
+         *
+         * First, retrieve clone of current target cert constraints,
+         * and then add more selection criteria based on current validation
+         * state. Since selector never changes, cache local copy & reuse.
+         */
+        if (eeSelector == null) {
+            eeSelector = (X509CertSelector) targetCertConstraints.clone();
+
+            /*
+             * Match on certificate validity date
+             */
+            eeSelector.setCertificateValid(buildParams.date());
+
+            /*
+             * Policy processing optimizations
+             */
+            if (buildParams.explicitPolicyRequired()) {
+                eeSelector.setPolicy(getMatchingPolicies());
+            }
+            /*
+             * Require EE certs
+             */
+            eeSelector.setBasicConstraints(-2);
+        }
+
+        /* Retrieve matching EE certs from CertStores */
+        addMatchingCerts(eeSelector, certStores, eeCerts, searchAllCertStores);
+    }
+
+    /**
+     * Retrieves all CA certificates which satisfy constraints
+     * and requirements specified in the parameters and PKIX state.
+     */
+    private void getMatchingCACerts(ForwardState currentState,
+                                    List<CertStore> certStores,
+                                    Collection<X509Certificate> caCerts)
+        throws IOException
+    {
+        if (debug != null) {
+            debug.println("ForwardBuilder.getMatchingCACerts()...");
+        }
+        int initialSize = caCerts.size();
+
+        /*
+         * Compose a CertSelector to filter out
+         * certs which do not satisfy requirements.
+         */
+        X509CertSelector sel = null;
+
+        if (currentState.isInitial()) {
+            if (targetCertConstraints.getBasicConstraints() == -2) {
+                // no need to continue: this means we never can match a CA cert
+                return;
+            }
+
+            /* This means a CA is the target, so match on same stuff as
+             * getMatchingEECerts
+             */
+            if (debug != null) {
+                debug.println("ForwardBuilder.getMatchingCACerts(): ca is target");
+            }
+
+            if (caTargetSelector == null) {
+                caTargetSelector =
+                    (X509CertSelector) targetCertConstraints.clone();
+
+                /*
+                 * Since we don't check the validity period of trusted
+                 * certificates, please don't set the certificate valid
+                 * criterion unless the trusted certificate matching is
+                 * completed.
+                 */
+
+                /*
+                 * Policy processing optimizations
+                 */
+                if (buildParams.explicitPolicyRequired())
+                    caTargetSelector.setPolicy(getMatchingPolicies());
+            }
+
+            sel = caTargetSelector;
+        } else {
+
+            if (caSelector == null) {
+                caSelector = new AdaptableX509CertSelector();
+
+                /*
+                 * Since we don't check the validity period of trusted
+                 * certificates, please don't set the certificate valid
+                 * criterion unless the trusted certificate matching is
+                 * completed.
+                 */
+
+                /*
+                 * Policy processing optimizations
+                 */
+                if (buildParams.explicitPolicyRequired())
+                    caSelector.setPolicy(getMatchingPolicies());
+            }
+
+            /*
+             * Match on subject (issuer of previous cert)
+             */
+            caSelector.setSubject(currentState.issuerDN);
+
+            /*
+             * Match on subjectNamesTraversed (both DNs and AltNames)
+             * (checks that current cert's name constraints permit it
+             * to certify all the DNs and AltNames that have been traversed)
+             */
+            CertPathHelper.setPathToNames
+                (caSelector, currentState.subjectNamesTraversed);
+
+            /*
+             * Facilitate certification path construction with authority
+             * key identifier and subject key identifier.
+             */
+            AuthorityKeyIdentifierExtension akidext =
+                    currentState.cert.getAuthorityKeyIdentifierExtension();
+            caSelector.parseAuthorityKeyIdentifierExtension(akidext);
+
+            /*
+             * check the validity period
+             */
+            caSelector.setValidityPeriod(currentState.cert.getNotBefore(),
+                                         currentState.cert.getNotAfter());
+
+            sel = caSelector;
+        }
+
+        /*
+         * For compatibility, conservatively, we don't check the path
+         * length constraint of trusted anchors.  Please don't set the
+         * basic constraints criterion unless the trusted certificate
+         * matching is completed.
+         */
+        sel.setBasicConstraints(-1);
+
+        for (X509Certificate trustedCert : trustedCerts) {
+            if (sel.match(trustedCert)) {
+                if (debug != null) {
+                    debug.println("ForwardBuilder.getMatchingCACerts: "
+                        + "found matching trust anchor");
+                }
+                if (caCerts.add(trustedCert) && !searchAllCertStores) {
+                    return;
+                }
+            }
+        }
+
+        /*
+         * The trusted certificate matching is completed. We need to match
+         * on certificate validity date.
+         */
+        sel.setCertificateValid(buildParams.date());
+
+        /*
+         * Require CA certs with a pathLenConstraint that allows
+         * at least as many CA certs that have already been traversed
+         */
+        sel.setBasicConstraints(currentState.traversedCACerts);
+
+        /*
+         * If we have already traversed as many CA certs as the maxPathLength
+         * will allow us to, then we don't bother looking through these
+         * certificate pairs. If maxPathLength has a value of -1, this
+         * means it is unconstrained, so we always look through the
+         * certificate pairs.
+         */
+        if (currentState.isInitial() ||
+           (buildParams.maxPathLength() == -1) ||
+           (buildParams.maxPathLength() > currentState.traversedCACerts))
+        {
+            if (addMatchingCerts(sel, certStores,
+                                 caCerts, searchAllCertStores)
+                && !searchAllCertStores) {
+                return;
+            }
+        }
+
+        if (!currentState.isInitial() && Builder.USE_AIA) {
+            // check for AuthorityInformationAccess extension
+            AuthorityInfoAccessExtension aiaExt =
+                currentState.cert.getAuthorityInfoAccessExtension();
+            if (aiaExt != null) {
+                getCerts(aiaExt, caCerts);
+            }
+        }
+
+        if (debug != null) {
+            int numCerts = caCerts.size() - initialSize;
+            debug.println("ForwardBuilder.getMatchingCACerts: found " +
+                numCerts + " CA certs");
+        }
+    }
+
+    /**
+     * Download Certificates from the given AIA and add them to the
+     * specified Collection.
+     */
+    // cs.getCertificates(caSelector) returns a collection of X509Certificate's
+    // because of the selector, so the cast is safe
+    @SuppressWarnings("unchecked")
+    private boolean getCerts(AuthorityInfoAccessExtension aiaExt,
+                             Collection<X509Certificate> certs)
+    {
+        if (Builder.USE_AIA == false) {
+            return false;
+        }
+        List<AccessDescription> adList = aiaExt.getAccessDescriptions();
+        if (adList == null || adList.isEmpty()) {
+            return false;
+        }
+
+        boolean add = false;
+        for (AccessDescription ad : adList) {
+            CertStore cs = URICertStore.getInstance(ad);
+            if (cs != null) {
+                try {
+                    if (certs.addAll((Collection<X509Certificate>)
+                        cs.getCertificates(caSelector))) {
+                        add = true;
+                        if (!searchAllCertStores) {
+                            return true;
+                        }
+                    }
+                } catch (CertStoreException cse) {
+                    if (debug != null) {
+                        debug.println("exception getting certs from CertStore:");
+                        cse.printStackTrace();
+                    }
+                }
+            }
+        }
+        return add;
+    }
+
+    /**
+     * This inner class compares 2 PKIX certificates according to which
+     * should be tried first when building a path from the target.
+     * The preference order is as follows:
+     *
+     * Given trusted certificate(s):
+     *    Subject:ou=D,ou=C,o=B,c=A
+     *
+     * Preference order for current cert:
+     *
+     * 1) Issuer matches a trusted subject
+     *    Issuer: ou=D,ou=C,o=B,c=A
+     *
+     * 2) Issuer is a descendant of a trusted subject (in order of
+     *    number of links to the trusted subject)
+     *    a) Issuer: ou=E,ou=D,ou=C,o=B,c=A        [links=1]
+     *    b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A  [links=2]
+     *
+     * 3) Issuer is an ancestor of a trusted subject (in order of number of
+     *    links to the trusted subject)
+     *    a) Issuer: ou=C,o=B,c=A [links=1]
+     *    b) Issuer: o=B,c=A      [links=2]
+     *
+     * 4) Issuer is in the same namespace as a trusted subject (in order of
+     *    number of links to the trusted subject)
+     *    a) Issuer: ou=G,ou=C,o=B,c=A  [links=2]
+     *    b) Issuer: ou=H,o=B,c=A       [links=3]
+     *
+     * 5) Issuer is an ancestor of certificate subject (in order of number
+     *    of links to the certificate subject)
+     *    a) Issuer:  ou=K,o=J,c=A
+     *       Subject: ou=L,ou=K,o=J,c=A
+     *    b) Issuer:  o=J,c=A
+     *       Subject: ou=L,ou=K,0=J,c=A
+     *
+     * 6) Any other certificates
+     */
+    static class PKIXCertComparator implements Comparator<X509Certificate> {
+
+        final static String METHOD_NME = "PKIXCertComparator.compare()";
+
+        private final Set<X500Principal> trustedSubjectDNs;
+
+        PKIXCertComparator(Set<X500Principal> trustedSubjectDNs) {
+            this.trustedSubjectDNs = trustedSubjectDNs;
+        }
+
+        /**
+         * @param oCert1 First X509Certificate to be compared
+         * @param oCert2 Second X509Certificate to be compared
+         * @return -1 if oCert1 is preferable to oCert2, or
+         *            if oCert1 and oCert2 are equally preferable (in this
+         *            case it doesn't matter which is preferable, but we don't
+         *            return 0 because the comparator would behave strangely
+         *            when used in a SortedSet).
+         *          1 if oCert2 is preferable to oCert1
+         *          0 if oCert1.equals(oCert2). We only return 0 if the
+         *          certs are equal so that this comparator behaves
+         *          correctly when used in a SortedSet.
+         * @throws ClassCastException if either argument is not of type
+         * X509Certificate
+         */
+        @Override
+        public int compare(X509Certificate oCert1, X509Certificate oCert2) {
+
+            // if certs are the same, return 0
+            if (oCert1.equals(oCert2)) return 0;
+
+            X500Principal cIssuer1 = oCert1.getIssuerX500Principal();
+            X500Principal cIssuer2 = oCert2.getIssuerX500Principal();
+            X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1);
+            X500Name cIssuer2Name = X500Name.asX500Name(cIssuer2);
+
+            if (debug != null) {
+                debug.println(METHOD_NME + " o1 Issuer:  " + cIssuer1);
+                debug.println(METHOD_NME + " o2 Issuer:  " + cIssuer2);
+            }
+
+            /* If one cert's issuer matches a trusted subject, then it is
+             * preferable.
+             */
+            if (debug != null) {
+                debug.println(METHOD_NME + " MATCH TRUSTED SUBJECT TEST...");
+            }
+
+            boolean m1 = trustedSubjectDNs.contains(cIssuer1);
+            boolean m2 = trustedSubjectDNs.contains(cIssuer2);
+            if (debug != null) {
+                debug.println(METHOD_NME + " m1: " + m1);
+                debug.println(METHOD_NME + " m2: " + m2);
+            }
+            if (m1 && m2) {
+                return -1;
+            } else if (m1) {
+                return -1;
+            } else if (m2) {
+                return 1;
+            }
+
+            /* If one cert's issuer is a naming descendant of a trusted subject,
+             * then it is preferable, in order of increasing naming distance.
+             */
+            if (debug != null) {
+                debug.println(METHOD_NME + " NAMING DESCENDANT TEST...");
+            }
+            for (X500Principal tSubject : trustedSubjectDNs) {
+                X500Name tSubjectName = X500Name.asX500Name(tSubject);
+                int distanceTto1 =
+                    Builder.distance(tSubjectName, cIssuer1Name, -1);
+                int distanceTto2 =
+                    Builder.distance(tSubjectName, cIssuer2Name, -1);
+                if (debug != null) {
+                    debug.println(METHOD_NME +" distanceTto1: " + distanceTto1);
+                    debug.println(METHOD_NME +" distanceTto2: " + distanceTto2);
+                }
+                if (distanceTto1 > 0 || distanceTto2 > 0) {
+                    if (distanceTto1 == distanceTto2) {
+                        return -1;
+                    } else if (distanceTto1 > 0 && distanceTto2 <= 0) {
+                        return -1;
+                    } else if (distanceTto1 <= 0 && distanceTto2 > 0) {
+                        return 1;
+                    } else if (distanceTto1 < distanceTto2) {
+                        return -1;
+                    } else {    // distanceTto1 > distanceTto2
+                        return 1;
+                    }
+                }
+            }
+
+            /* If one cert's issuer is a naming ancestor of a trusted subject,
+             * then it is preferable, in order of increasing naming distance.
+             */
+            if (debug != null) {
+                debug.println(METHOD_NME + " NAMING ANCESTOR TEST...");
+            }
+            for (X500Principal tSubject : trustedSubjectDNs) {
+                X500Name tSubjectName = X500Name.asX500Name(tSubject);
+
+                int distanceTto1 = Builder.distance
+                    (tSubjectName, cIssuer1Name, Integer.MAX_VALUE);
+                int distanceTto2 = Builder.distance
+                    (tSubjectName, cIssuer2Name, Integer.MAX_VALUE);
+                if (debug != null) {
+                    debug.println(METHOD_NME +" distanceTto1: " + distanceTto1);
+                    debug.println(METHOD_NME +" distanceTto2: " + distanceTto2);
+                }
+                if (distanceTto1 < 0 || distanceTto2 < 0) {
+                    if (distanceTto1 == distanceTto2) {
+                        return -1;
+                    } else if (distanceTto1 < 0 && distanceTto2 >= 0) {
+                        return -1;
+                    } else if (distanceTto1 >= 0 && distanceTto2 < 0) {
+                        return 1;
+                    } else if (distanceTto1 > distanceTto2) {
+                        return -1;
+                    } else {
+                        return 1;
+                    }
+                }
+            }
+
+            /* If one cert's issuer is in the same namespace as a trusted
+             * subject, then it is preferable, in order of increasing naming
+             * distance.
+             */
+            if (debug != null) {
+                debug.println(METHOD_NME +" SAME NAMESPACE AS TRUSTED TEST...");
+            }
+            for (X500Principal tSubject : trustedSubjectDNs) {
+                X500Name tSubjectName = X500Name.asX500Name(tSubject);
+                X500Name tAo1 = tSubjectName.commonAncestor(cIssuer1Name);
+                X500Name tAo2 = tSubjectName.commonAncestor(cIssuer2Name);
+                if (debug != null) {
+                    debug.println(METHOD_NME +" tAo1: " + String.valueOf(tAo1));
+                    debug.println(METHOD_NME +" tAo2: " + String.valueOf(tAo2));
+                }
+                if (tAo1 != null || tAo2 != null) {
+                    if (tAo1 != null && tAo2 != null) {
+                        int hopsTto1 = Builder.hops
+                            (tSubjectName, cIssuer1Name, Integer.MAX_VALUE);
+                        int hopsTto2 = Builder.hops
+                            (tSubjectName, cIssuer2Name, Integer.MAX_VALUE);
+                        if (debug != null) {
+                            debug.println(METHOD_NME +" hopsTto1: " + hopsTto1);
+                            debug.println(METHOD_NME +" hopsTto2: " + hopsTto2);
+                        }
+                        if (hopsTto1 == hopsTto2) {
+                        } else if (hopsTto1 > hopsTto2) {
+                            return 1;
+                        } else {  // hopsTto1 < hopsTto2
+                            return -1;
+                        }
+                    } else if (tAo1 == null) {
+                        return 1;
+                    } else {
+                        return -1;
+                    }
+                }
+            }
+
+
+            /* If one cert's issuer is an ancestor of that cert's subject,
+             * then it is preferable, in order of increasing naming distance.
+             */
+            if (debug != null) {
+                debug.println(METHOD_NME+" CERT ISSUER/SUBJECT COMPARISON TEST...");
+            }
+            X500Principal cSubject1 = oCert1.getSubjectX500Principal();
+            X500Principal cSubject2 = oCert2.getSubjectX500Principal();
+            X500Name cSubject1Name = X500Name.asX500Name(cSubject1);
+            X500Name cSubject2Name = X500Name.asX500Name(cSubject2);
+
+            if (debug != null) {
+                debug.println(METHOD_NME + " o1 Subject: " + cSubject1);
+                debug.println(METHOD_NME + " o2 Subject: " + cSubject2);
+            }
+            int distanceStoI1 = Builder.distance
+                (cSubject1Name, cIssuer1Name, Integer.MAX_VALUE);
+            int distanceStoI2 = Builder.distance
+                (cSubject2Name, cIssuer2Name, Integer.MAX_VALUE);
+            if (debug != null) {
+                debug.println(METHOD_NME + " distanceStoI1: " + distanceStoI1);
+                debug.println(METHOD_NME + " distanceStoI2: " + distanceStoI2);
+            }
+            if (distanceStoI2 > distanceStoI1) {
+                return -1;
+            } else if (distanceStoI2 < distanceStoI1) {
+                return 1;
+            }
+
+            /* Otherwise, certs are equally preferable.
+             */
+            if (debug != null) {
+                debug.println(METHOD_NME + " no tests matched; RETURN 0");
+            }
+            return -1;
+        }
+    }
+
+    /**
+     * Verifies a matching certificate.
+     *
+     * This method executes the validation steps in the PKIX path
+     * validation algorithm <draft-ietf-pkix-new-part1-08.txt> which were
+     * not satisfied by the selection criteria used by getCertificates()
+     * to find the certs and only the steps that can be executed in a
+     * forward direction (target to trust anchor). Those steps that can
+     * only be executed in a reverse direction are deferred until the
+     * complete path has been built.
+     *
+     * Trust anchor certs are not validated, but are used to verify the
+     * signature and revocation status of the previous cert.
+     *
+     * If the last certificate is being verified (the one whose subject
+     * matches the target subject, then steps in 6.1.4 of the PKIX
+     * 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
+     */
+    @Override
+    void verifyCert(X509Certificate cert, State currentState,
+                    List<X509Certificate> certPathList)
+        throws GeneralSecurityException
+    {
+        if (debug != null) {
+            debug.println("ForwardBuilder.verifyCert(SN: "
+                + Debug.toHexString(cert.getSerialNumber())
+                + "\n  Issuer: " + cert.getIssuerX500Principal() + ")"
+                + "\n  Subject: " + cert.getSubjectX500Principal() + ")");
+        }
+
+        ForwardState currState = (ForwardState)currentState;
+
+        // Don't bother to verify untrusted certificate more.
+        currState.untrustedChecker.check(cert, Collections.<String>emptySet());
+
+        /*
+         * check for looping - abort a loop if we encounter the same
+         * certificate twice
+         */
+        if (certPathList != null) {
+            for (X509Certificate cpListCert : certPathList) {
+                if (cert.equals(cpListCert)) {
+                    if (debug != null) {
+                        debug.println("loop detected!!");
+                    }
+                    throw new CertPathValidatorException("loop detected");
+                }
+            }
+        }
+
+        /* check if trusted cert */
+        boolean isTrustedCert = trustedCerts.contains(cert);
+
+        /* we don't perform any validation of the trusted cert */
+        if (!isTrustedCert) {
+            /*
+             * Check CRITICAL private extensions for user checkers that
+             * support forward checking (forwardCheckers) and remove
+             * ones we know how to check.
+             */
+            Set<String> unresCritExts = cert.getCriticalExtensionOIDs();
+            if (unresCritExts == null) {
+                unresCritExts = Collections.<String>emptySet();
+            }
+            for (PKIXCertPathChecker checker : currState.forwardCheckers) {
+                checker.check(cert, unresCritExts);
+            }
+
+            /*
+             * Remove extensions from user checkers that don't 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> supportedExts = checker.getSupportedExtensions();
+                    if (supportedExts != null) {
+                        unresCritExts.removeAll(supportedExts);
+                    }
+                }
+            }
+
+            /*
+             * Look at the remaining extensions and remove any ones we know how
+             * to check. If there are any left, throw an exception!
+             */
+            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 this is the target certificate (init=true), then we are
+         * not able to do any more verification, so just return
+         */
+        if (currState.isInitial()) {
+            return;
+        }
+
+        /* we don't perform any validation of the trusted cert */
+        if (!isTrustedCert) {
+            /* Make sure this is a CA cert */
+            if (cert.getBasicConstraints() == -1) {
+                throw new CertificateException("cert is NOT a CA cert");
+            }
+
+            /*
+             * Check keyUsage extension
+             */
+            KeyChecker.verifyCAKeyUsage(cert);
+        }
+
+        /*
+         * the following checks are performed even when the cert
+         * is a trusted cert, since we are only extracting the
+         * subjectDN, and publicKey from the cert
+         * in order to verify a previous cert
+         */
+
+        /*
+         * Check signature only if no key requiring key parameters has been
+         * encountered.
+         */
+        if (!currState.keyParamsNeeded()) {
+            (currState.cert).verify(cert.getPublicKey(),
+                                    buildParams.sigProvider());
+        }
+    }
+
+    /**
+     * Verifies whether the input certificate completes the path.
+     * Checks the cert against each trust anchor that was specified, in order,
+     * and returns true as soon as it finds a valid anchor.
+     * Returns true if the cert matches a trust anchor specified as a
+     * certificate or if the cert verifies with a trust anchor that
+     * was specified as a trusted {pubkey, caname} pair. Returns false if none
+     * of the trust anchors are valid for this cert.
+     *
+     * @param cert the certificate to test
+     * @return a boolean value indicating whether the cert completes the path.
+     */
+    @Override
+    boolean isPathCompleted(X509Certificate cert) {
+        for (TrustAnchor anchor : trustAnchors) {
+            if (anchor.getTrustedCert() != null) {
+                if (cert.equals(anchor.getTrustedCert())) {
+                    this.trustAnchor = anchor;
+                    return true;
+                } else {
+                    continue;
+                }
+            }
+            X500Principal principal = anchor.getCA();
+            PublicKey publicKey = anchor.getCAPublicKey();
+
+            if (principal != null && publicKey != null &&
+                    principal.equals(cert.getSubjectX500Principal())) {
+                if (publicKey.equals(cert.getPublicKey())) {
+                    // the cert itself is a trust anchor
+                    this.trustAnchor = anchor;
+                    return true;
+                }
+                // else, it is a self-issued certificate of the anchor
+            }
+
+            // Check subject/issuer name chaining
+            if (principal == null ||
+                    !principal.equals(cert.getIssuerX500Principal())) {
+                continue;
+            }
+
+            // skip anchor if it contains a DSA key with no DSA params
+            if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) {
+                continue;
+            }
+
+            /*
+             * Check signature
+             */
+            try {
+                cert.verify(publicKey, buildParams.sigProvider());
+            } catch (InvalidKeyException ike) {
+                if (debug != null) {
+                    debug.println("ForwardBuilder.isPathCompleted() invalid "
+                                  + "DSA key found");
+                }
+                continue;
+            } catch (GeneralSecurityException e){
+                if (debug != null) {
+                    debug.println("ForwardBuilder.isPathCompleted() " +
+                                  "unexpected exception");
+                    e.printStackTrace();
+                }
+                continue;
+            }
+
+            this.trustAnchor = anchor;
+            return true;
+        }
+
+        return false;
+    }
+
+    /** Adds the certificate to the certPathList
+     *
+     * @param cert the certificate to be added
+     * @param certPathList the certification path list
+     */
+    @Override
+    void addCertToPath(X509Certificate cert,
+                       LinkedList<X509Certificate> certPathList)
+    {
+        certPathList.addFirst(cert);
+    }
+
+    /** Removes final certificate from the certPathList
+     *
+     * @param certPathList the certification path list
+     */
+    @Override
+    void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
+        certPathList.removeFirst();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ForwardState.java b/ojluni/src/main/java/sun/security/provider/certpath/ForwardState.java
new file mode 100644
index 0000000..2dc9e20
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ForwardState.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.util.Debug;
+import sun.security.x509.SubjectAlternativeNameExtension;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNameInterface;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509CertImpl;
+
+/**
+ * A specification of a forward PKIX validation state
+ * which is initialized by each build and updated each time a
+ * certificate is added to the current path.
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+class ForwardState implements State {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    /* The issuer DN of the last cert in the path */
+    X500Principal issuerDN;
+
+    /* The last cert in the path */
+    X509CertImpl cert;
+
+    /* The set of subjectDNs and subjectAltNames of all certs in the path */
+    HashSet<GeneralNameInterface> subjectNamesTraversed;
+
+    /*
+     * The number of intermediate CA certs which have been traversed so
+     * far in the path
+     */
+    int traversedCACerts;
+
+    /* Flag indicating if state is initial (path is just starting) */
+    private boolean init = true;
+
+
+    /* the untrusted certificates checker */
+    UntrustedChecker untrustedChecker;
+
+    /* The list of user-defined checkers that support forward checking */
+    ArrayList<PKIXCertPathChecker> forwardCheckers;
+
+    /* Flag indicating if key needing to inherit key parameters has been
+     * encountered.
+     */
+    boolean keyParamsNeededFlag = false;
+
+    /**
+     * Returns a boolean flag indicating if the state is initial
+     * (just starting)
+     *
+     * @return boolean flag indicating if the state is initial (just starting)
+     */
+    @Override
+    public boolean isInitial() {
+        return init;
+    }
+
+    /**
+     * Return boolean flag indicating whether a public key that needs to inherit
+     * key parameters has been encountered.
+     *
+     * @return boolean true if key needing to inherit parameters has been
+     * encountered; false otherwise.
+     */
+    @Override
+    public boolean keyParamsNeeded() {
+        return keyParamsNeededFlag;
+    }
+
+    /**
+     * Display state for debugging purposes
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("State [");
+        sb.append("\n  issuerDN of last cert: ").append(issuerDN);
+        sb.append("\n  traversedCACerts: ").append(traversedCACerts);
+        sb.append("\n  init: ").append(String.valueOf(init));
+        sb.append("\n  keyParamsNeeded: ").append
+                 (String.valueOf(keyParamsNeededFlag));
+        sb.append("\n  subjectNamesTraversed: \n").append
+                 (subjectNamesTraversed);
+        sb.append("]\n");
+        return sb.toString();
+    }
+
+    /**
+     * Initialize the state.
+     *
+     * @param certPathCheckers the list of user-defined PKIXCertPathCheckers
+     */
+    public void initState(List<PKIXCertPathChecker> certPathCheckers)
+        throws CertPathValidatorException
+    {
+        subjectNamesTraversed = new HashSet<GeneralNameInterface>();
+        traversedCACerts = 0;
+
+        /*
+         * Populate forwardCheckers with every user-defined checker
+         * that supports forward checking and initialize the forwardCheckers
+         */
+        forwardCheckers = new ArrayList<PKIXCertPathChecker>();
+        for (PKIXCertPathChecker checker : certPathCheckers) {
+            if (checker.isForwardCheckingSupported()) {
+                checker.init(true);
+                forwardCheckers.add(checker);
+            }
+        }
+
+        init = true;
+    }
+
+    /**
+     * Update the state with the next certificate added to the path.
+     *
+     * @param cert the certificate which is used to update the state
+     */
+    @Override
+    public void updateState(X509Certificate cert)
+        throws CertificateException, IOException, CertPathValidatorException {
+
+        if (cert == null)
+            return;
+
+        X509CertImpl icert = X509CertImpl.toImpl(cert);
+
+        /* see if certificate key has null parameters */
+        if (PKIX.isDSAPublicKeyWithoutParams(icert.getPublicKey())) {
+            keyParamsNeededFlag = true;
+        }
+
+        /* update certificate */
+        this.cert = icert;
+
+        /* update issuer DN */
+        issuerDN = cert.getIssuerX500Principal();
+
+        if (!X509CertImpl.isSelfIssued(cert)) {
+
+            /*
+             * update traversedCACerts only if this is a non-self-issued
+             * intermediate CA cert
+             */
+            if (!init && cert.getBasicConstraints() != -1) {
+                traversedCACerts++;
+            }
+        }
+
+        /* update subjectNamesTraversed only if this is the EE cert or if
+           this cert is not self-issued */
+        if (init || !X509CertImpl.isSelfIssued(cert)){
+            X500Principal subjName = cert.getSubjectX500Principal();
+            subjectNamesTraversed.add(X500Name.asX500Name(subjName));
+
+            try {
+                SubjectAlternativeNameExtension subjAltNameExt
+                    = icert.getSubjectAlternativeNameExtension();
+                if (subjAltNameExt != null) {
+                    GeneralNames gNames = subjAltNameExt.get(
+                            SubjectAlternativeNameExtension.SUBJECT_NAME);
+                    for (GeneralName gName : gNames.names()) {
+                        subjectNamesTraversed.add(gName.getName());
+                    }
+                }
+            } catch (IOException e) {
+                if (debug != null) {
+                    debug.println("ForwardState.updateState() unexpected "
+                        + "exception");
+                    e.printStackTrace();
+                }
+                throw new CertPathValidatorException(e);
+            }
+        }
+
+        init = false;
+    }
+
+    /*
+     * Clone current state. The state is cloned as each cert is
+     * added to the path. This is necessary if backtracking occurs,
+     * and a prior state needs to be restored.
+     *
+     * Note that this is a SMART clone. Not all fields are fully copied,
+     * because some of them will
+     * not have their contents modified by subsequent calls to updateState.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
+    public Object clone() {
+        try {
+            ForwardState clonedState = (ForwardState) super.clone();
+
+            /* clone checkers, if cloneable */
+            clonedState.forwardCheckers = (ArrayList<PKIXCertPathChecker>)
+                                                forwardCheckers.clone();
+            ListIterator<PKIXCertPathChecker> li =
+                                clonedState.forwardCheckers.listIterator();
+            while (li.hasNext()) {
+                PKIXCertPathChecker checker = li.next();
+                if (checker instanceof Cloneable) {
+                    li.set((PKIXCertPathChecker)checker.clone());
+                }
+            }
+
+            /*
+             * Shallow copy traversed names. There is no need to
+             * deep copy contents, since the elements of the Set
+             * are never modified by subsequent calls to updateState().
+             */
+            clonedState.subjectNamesTraversed
+                = (HashSet<GeneralNameInterface>)subjectNamesTraversed.clone();
+            return clonedState;
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError(e.toString(), e);
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/IndexedCollectionCertStore.java b/ojluni/src/main/java/sun/security/provider/certpath/IndexedCollectionCertStore.java
new file mode 100644
index 0000000..64fcc56
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/IndexedCollectionCertStore.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.util.*;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.*;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A <code>CertStore</code> that retrieves <code>Certificates</code> and
+ * <code>CRL</code>s from a <code>Collection</code>.
+ * <p>
+ * This implementation is functionally equivalent to CollectionCertStore
+ * with two differences:
+ * <ol>
+ * <li>Upon construction, the elements in the specified Collection are
+ * partially indexed. X509Certificates are indexed by subject, X509CRLs
+ * by issuer, non-X509 Certificates and CRLs are copied without indexing,
+ * other objects are ignored. This increases CertStore construction time
+ * but allows significant speedups for searches which specify the indexed
+ * attributes, in particular for large Collections (reduction from linear
+ * time to effectively constant time). Searches for non-indexed queries
+ * are as fast (or marginally faster) than for the standard
+ * CollectionCertStore. Certificate subjects and CRL issuers
+ * were found to be specified in most searches used internally by the
+ * CertPath provider. Additional attributes could indexed if there are
+ * queries that justify the effort.
+ *
+ * <li>Changes to the specified Collection after construction time are
+ * not detected and ignored. This is because there is no way to efficiently
+ * detect if a Collection has been modified, a full traversal would be
+ * required. That would degrade lookup performance to linear time and
+ * eliminated the benefit of indexing. We may fix this via the introduction
+ * of new public APIs in the future.
+ * </ol>
+ * <p>
+ * Before calling the {@link #engineGetCertificates engineGetCertificates} or
+ * {@link #engineGetCRLs engineGetCRLs} methods, the
+ * {@link #CollectionCertStore(CertStoreParameters)
+ * CollectionCertStore(CertStoreParameters)} constructor is called to
+ * create the <code>CertStore</code> and establish the
+ * <code>Collection</code> from which <code>Certificate</code>s and
+ * <code>CRL</code>s will be retrieved. If the specified
+ * <code>Collection</code> contains an object that is not a
+ * <code>Certificate</code> or <code>CRL</code>, that object will be
+ * ignored.
+ * <p>
+ * <b>Concurrent Access</b>
+ * <p>
+ * As described in the javadoc for <code>CertStoreSpi</code>, the
+ * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods
+ * must be thread-safe. That is, multiple threads may concurrently
+ * invoke these methods on a single <code>CollectionCertStore</code>
+ * object (or more than one) with no ill effects.
+ * <p>
+ * This is achieved by requiring that the <code>Collection</code> passed to
+ * the {@link #CollectionCertStore(CertStoreParameters)
+ * CollectionCertStore(CertStoreParameters)} constructor (via the
+ * <code>CollectionCertStoreParameters</code> object) must have fail-fast
+ * iterators. Simultaneous modifications to the <code>Collection</code> can thus be
+ * detected and certificate or CRL retrieval can be retried. The fact that
+ * <code>Certificate</code>s and <code>CRL</code>s must be thread-safe is also
+ * essential.
+ *
+ * @see java.security.cert.CertStore
+ * @see CollectionCertStore
+ *
+ * @author Andreas Sterbenz
+ */
+public class IndexedCollectionCertStore extends CertStoreSpi {
+
+    /**
+     * Map X500Principal(subject) -> X509Certificate | List of X509Certificate
+     */
+    private Map<X500Principal, Object> certSubjects;
+    /**
+     * Map X500Principal(issuer) -> X509CRL | List of X509CRL
+     */
+    private Map<X500Principal, Object> crlIssuers;
+    /**
+     * Sets of non-X509 certificates and CRLs
+     */
+    private Set<Certificate> otherCertificates;
+    private Set<CRL> otherCRLs;
+
+    /**
+     * Creates a <code>CertStore</code> with the specified parameters.
+     * For this class, the parameters object must be an instance of
+     * <code>CollectionCertStoreParameters</code>.
+     *
+     * @param params the algorithm parameters
+     * @exception InvalidAlgorithmParameterException if params is not an
+     *   instance of <code>CollectionCertStoreParameters</code>
+     */
+    public IndexedCollectionCertStore(CertStoreParameters params)
+            throws InvalidAlgorithmParameterException {
+        super(params);
+        if (!(params instanceof CollectionCertStoreParameters)) {
+            throw new InvalidAlgorithmParameterException(
+                "parameters must be CollectionCertStoreParameters");
+        }
+        Collection<?> coll = ((CollectionCertStoreParameters)params).getCollection();
+        if (coll == null) {
+            throw new InvalidAlgorithmParameterException
+                                        ("Collection must not be null");
+        }
+        buildIndex(coll);
+    }
+
+    /**
+     * Index the specified Collection copying all references to Certificates
+     * and CRLs.
+     */
+    private void buildIndex(Collection<?> coll) {
+        certSubjects = new HashMap<X500Principal, Object>();
+        crlIssuers = new HashMap<X500Principal, Object>();
+        otherCertificates = null;
+        otherCRLs = null;
+        for (Object obj : coll) {
+            if (obj instanceof X509Certificate) {
+                indexCertificate((X509Certificate)obj);
+            } else if (obj instanceof X509CRL) {
+                indexCRL((X509CRL)obj);
+            } else if (obj instanceof Certificate) {
+                if (otherCertificates == null) {
+                    otherCertificates = new HashSet<Certificate>();
+                }
+                otherCertificates.add((Certificate)obj);
+            } else if (obj instanceof CRL) {
+                if (otherCRLs == null) {
+                    otherCRLs = new HashSet<CRL>();
+                }
+                otherCRLs.add((CRL)obj);
+            } else {
+                // ignore
+            }
+        }
+        if (otherCertificates == null) {
+            otherCertificates = Collections.<Certificate>emptySet();
+        }
+        if (otherCRLs == null) {
+            otherCRLs = Collections.<CRL>emptySet();
+        }
+    }
+
+    /**
+     * Add an X509Certificate to the index.
+     */
+    private void indexCertificate(X509Certificate cert) {
+        X500Principal subject = cert.getSubjectX500Principal();
+        Object oldEntry = certSubjects.put(subject, cert);
+        if (oldEntry != null) { // assume this is unlikely
+            if (oldEntry instanceof X509Certificate) {
+                if (cert.equals(oldEntry)) {
+                    return;
+                }
+                List<X509Certificate> list = new ArrayList<>(2);
+                list.add(cert);
+                list.add((X509Certificate)oldEntry);
+                certSubjects.put(subject, list);
+            } else {
+                @SuppressWarnings("unchecked") // See certSubjects javadoc.
+                List<X509Certificate> list = (List<X509Certificate>)oldEntry;
+                if (list.contains(cert) == false) {
+                    list.add(cert);
+                }
+                certSubjects.put(subject, list);
+            }
+        }
+    }
+
+    /**
+     * Add an X509CRL to the index.
+     */
+    private void indexCRL(X509CRL crl) {
+        X500Principal issuer = crl.getIssuerX500Principal();
+        Object oldEntry = crlIssuers.put(issuer, crl);
+        if (oldEntry != null) { // assume this is unlikely
+            if (oldEntry instanceof X509CRL) {
+                if (crl.equals(oldEntry)) {
+                    return;
+                }
+                List<X509CRL> list = new ArrayList<>(2);
+                list.add(crl);
+                list.add((X509CRL)oldEntry);
+                crlIssuers.put(issuer, list);
+            } else {
+                // See crlIssuers javadoc.
+                @SuppressWarnings("unchecked")
+                List<X509CRL> list = (List<X509CRL>)oldEntry;
+                if (list.contains(crl) == false) {
+                    list.add(crl);
+                }
+                crlIssuers.put(issuer, list);
+            }
+        }
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>Certificate</code>s that
+     * match the specified selector. If no <code>Certificate</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     *
+     * @param selector a <code>CertSelector</code> used to select which
+     *  <code>Certificate</code>s should be returned. Specify <code>null</code>
+     *  to return all <code>Certificate</code>s.
+     * @return a <code>Collection</code> of <code>Certificate</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    @Override
+    public Collection<? extends Certificate> engineGetCertificates(CertSelector selector)
+            throws CertStoreException {
+
+        // no selector means match all
+        if (selector == null) {
+            Set<Certificate> matches = new HashSet<>();
+            matchX509Certs(new X509CertSelector(), matches);
+            matches.addAll(otherCertificates);
+            return matches;
+        }
+
+        if (selector instanceof X509CertSelector == false) {
+            Set<Certificate> matches = new HashSet<>();
+            matchX509Certs(selector, matches);
+            for (Certificate cert : otherCertificates) {
+                if (selector.match(cert)) {
+                    matches.add(cert);
+                }
+            }
+            return matches;
+        }
+
+        if (certSubjects.isEmpty()) {
+            return Collections.<X509Certificate>emptySet();
+        }
+        X509CertSelector x509Selector = (X509CertSelector)selector;
+        // see if the subject is specified
+        X500Principal subject;
+        X509Certificate matchCert = x509Selector.getCertificate();
+        if (matchCert != null) {
+            subject = matchCert.getSubjectX500Principal();
+        } else {
+            subject = x509Selector.getSubject();
+        }
+        if (subject != null) {
+            // yes, narrow down candidates to indexed possibilities
+            Object entry = certSubjects.get(subject);
+            if (entry == null) {
+                return Collections.<X509Certificate>emptySet();
+            }
+            if (entry instanceof X509Certificate) {
+                X509Certificate x509Entry = (X509Certificate)entry;
+                if (x509Selector.match(x509Entry)) {
+                    return Collections.singleton(x509Entry);
+                } else {
+                    return Collections.<X509Certificate>emptySet();
+                }
+            } else {
+                // See certSubjects javadoc.
+                @SuppressWarnings("unchecked")
+                List<X509Certificate> list = (List<X509Certificate>)entry;
+                Set<X509Certificate> matches = new HashSet<>(16);
+                for (X509Certificate cert : list) {
+                    if (x509Selector.match(cert)) {
+                        matches.add(cert);
+                    }
+                }
+                return matches;
+            }
+        }
+        // cannot use index, iterate all
+        Set<Certificate> matches = new HashSet<>(16);
+        matchX509Certs(x509Selector, matches);
+        return matches;
+    }
+
+    /**
+     * Iterate through all the X509Certificates and add matches to the
+     * collection.
+     */
+    private void matchX509Certs(CertSelector selector,
+        Collection<Certificate> matches) {
+
+        for (Object obj : certSubjects.values()) {
+            if (obj instanceof X509Certificate) {
+                X509Certificate cert = (X509Certificate)obj;
+                if (selector.match(cert)) {
+                    matches.add(cert);
+                }
+            } else {
+                // See certSubjects javadoc.
+                @SuppressWarnings("unchecked")
+                List<X509Certificate> list = (List<X509Certificate>)obj;
+                for (X509Certificate cert : list) {
+                    if (selector.match(cert)) {
+                        matches.add(cert);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>CRL</code>s that
+     * match the specified selector. If no <code>CRL</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     *
+     * @param selector a <code>CRLSelector</code> used to select which
+     *  <code>CRL</code>s should be returned. Specify <code>null</code>
+     *  to return all <code>CRL</code>s.
+     * @return a <code>Collection</code> of <code>CRL</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    @Override
+    public Collection<CRL> engineGetCRLs(CRLSelector selector)
+            throws CertStoreException {
+
+        if (selector == null) {
+            Set<CRL> matches = new HashSet<>();
+            matchX509CRLs(new X509CRLSelector(), matches);
+            matches.addAll(otherCRLs);
+            return matches;
+        }
+
+        if (selector instanceof X509CRLSelector == false) {
+            Set<CRL> matches = new HashSet<>();
+            matchX509CRLs(selector, matches);
+            for (CRL crl : otherCRLs) {
+                if (selector.match(crl)) {
+                    matches.add(crl);
+                }
+            }
+            return matches;
+        }
+
+        if (crlIssuers.isEmpty()) {
+            return Collections.<CRL>emptySet();
+        }
+        X509CRLSelector x509Selector = (X509CRLSelector)selector;
+        // see if the issuer is specified
+        Collection<X500Principal> issuers = x509Selector.getIssuers();
+        if (issuers != null) {
+            HashSet<CRL> matches = new HashSet<>(16);
+            for (X500Principal issuer : issuers) {
+                Object entry = crlIssuers.get(issuer);
+                if (entry == null) {
+                    // empty
+                } else if (entry instanceof X509CRL) {
+                    X509CRL crl = (X509CRL)entry;
+                    if (x509Selector.match(crl)) {
+                        matches.add(crl);
+                    }
+                } else { // List
+                    // See crlIssuers javadoc.
+                    @SuppressWarnings("unchecked")
+                    List<X509CRL> list = (List<X509CRL>)entry;
+                    for (X509CRL crl : list) {
+                        if (x509Selector.match(crl)) {
+                            matches.add(crl);
+                        }
+                    }
+                }
+            }
+            return matches;
+        }
+        // cannot use index, iterate all
+        Set<CRL> matches = new HashSet<>(16);
+        matchX509CRLs(x509Selector, matches);
+        return matches;
+    }
+
+    /**
+     * Iterate through all the X509CRLs and add matches to the
+     * collection.
+     */
+    private void matchX509CRLs(CRLSelector selector, Collection<CRL> matches) {
+        for (Object obj : crlIssuers.values()) {
+            if (obj instanceof X509CRL) {
+                X509CRL crl = (X509CRL)obj;
+                if (selector.match(crl)) {
+                    matches.add(crl);
+                }
+            } else {
+                // See crlIssuers javadoc.
+                @SuppressWarnings("unchecked")
+                List<X509CRL> list = (List<X509CRL>)obj;
+                for (X509CRL crl : list) {
+                    if (selector.match(crl)) {
+                        matches.add(crl);
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/KeyChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/KeyChecker.java
new file mode 100644
index 0000000..2d45d95
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/KeyChecker.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.util.*;
+import java.security.cert.*;
+import java.security.cert.PKIXReason;
+
+import sun.security.util.Debug;
+import static sun.security.x509.PKIXExtensions.*;
+
+/**
+ * KeyChecker is a <code>PKIXCertPathChecker</code> that checks that the
+ * keyCertSign bit is set in the keyUsage extension in an intermediate CA
+ * certificate. It also checks whether the final certificate in a
+ * certification path meets the specified target constraints specified as
+ * a CertSelector in the PKIXParameters passed to the CertPathValidator.
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+class KeyChecker extends PKIXCertPathChecker {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private final int certPathLen;
+    private final CertSelector targetConstraints;
+    private int remainingCerts;
+
+    private Set<String> supportedExts;
+
+    /**
+     * Creates a KeyChecker.
+     *
+     * @param certPathLen allowable cert path length
+     * @param targetCertSel a CertSelector object specifying the constraints
+     * on the target certificate
+     */
+    KeyChecker(int certPathLen, CertSelector targetCertSel) {
+        this.certPathLen = certPathLen;
+        this.targetConstraints = targetCertSel;
+    }
+
+    /**
+     * Initializes the internal state of the checker from parameters
+     * specified in the constructor
+     */
+    @Override
+    public void init(boolean forward) throws CertPathValidatorException {
+        if (!forward) {
+            remainingCerts = certPathLen;
+        } else {
+            throw new CertPathValidatorException
+                ("forward checking not supported");
+        }
+    }
+
+    @Override
+    public boolean isForwardCheckingSupported() {
+        return false;
+    }
+
+    @Override
+    public Set<String> getSupportedExtensions() {
+        if (supportedExts == null) {
+            supportedExts = new HashSet<String>(3);
+            supportedExts.add(KeyUsage_Id.toString());
+            supportedExts.add(ExtendedKeyUsage_Id.toString());
+            supportedExts.add(SubjectAlternativeName_Id.toString());
+            supportedExts = Collections.unmodifiableSet(supportedExts);
+        }
+        return supportedExts;
+    }
+
+    /**
+     * Checks that keyUsage and target constraints are satisfied by
+     * the specified certificate.
+     *
+     * @param cert the Certificate
+     * @param unresolvedCritExts the unresolved critical extensions
+     * @throws CertPathValidatorException if certificate does not verify
+     */
+    @Override
+    public void check(Certificate cert, Collection<String> unresCritExts)
+        throws CertPathValidatorException
+    {
+        X509Certificate currCert = (X509Certificate)cert;
+
+        remainingCerts--;
+
+        // if final certificate, check that target constraints are satisfied
+        if (remainingCerts == 0) {
+            if (targetConstraints != null &&
+                targetConstraints.match(currCert) == false) {
+                throw new CertPathValidatorException("target certificate " +
+                    "constraints check failed");
+            }
+        } else {
+            // otherwise, verify that keyCertSign bit is set in CA certificate
+            verifyCAKeyUsage(currCert);
+        }
+
+        // remove the extensions that we have checked
+        if (unresCritExts != null && !unresCritExts.isEmpty()) {
+            unresCritExts.remove(KeyUsage_Id.toString());
+            unresCritExts.remove(ExtendedKeyUsage_Id.toString());
+            unresCritExts.remove(SubjectAlternativeName_Id.toString());
+        }
+    }
+
+    // the index of keyCertSign in the boolean KeyUsage array
+    private static final int KEY_CERT_SIGN = 5;
+    /**
+     * Verifies the key usage extension in a CA cert.
+     * The key usage extension, if present, must assert the keyCertSign bit.
+     * The extended key usage extension is not checked (see CR 4776794 for
+     * more information).
+     */
+    static void verifyCAKeyUsage(X509Certificate cert)
+            throws CertPathValidatorException {
+        String msg = "CA key usage";
+        if (debug != null) {
+            debug.println("KeyChecker.verifyCAKeyUsage() ---checking " + msg
+                          + "...");
+        }
+
+        boolean[] keyUsageBits = cert.getKeyUsage();
+
+        // getKeyUsage returns null if the KeyUsage extension is not present
+        // in the certificate - in which case there is nothing to check
+        if (keyUsageBits == null) {
+            return;
+        }
+
+        // throw an exception if the keyCertSign bit is not set
+        if (!keyUsageBits[KEY_CERT_SIGN]) {
+            throw new CertPathValidatorException
+                (msg + " check failed: keyCertSign bit is not set", null,
+                 null, -1, PKIXReason.INVALID_KEY_USAGE);
+        }
+
+        if (debug != null) {
+            debug.println("KeyChecker.verifyCAKeyUsage() " + msg
+                          + " verified.");
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/OCSP.java b/ojluni/src/main/java/sun/security/provider/certpath/OCSP.java
new file mode 100644
index 0000000..dce8fd6
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/OCSP.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.provider.certpath;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.CRLReason;
+import java.security.cert.Extension;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static sun.security.provider.certpath.OCSPResponse.*;
+import sun.security.action.GetIntegerAction;
+import sun.security.util.Debug;
+import sun.security.util.ObjectIdentifier;
+import sun.security.x509.AccessDescription;
+import sun.security.x509.AuthorityInfoAccessExtension;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNameInterface;
+import sun.security.x509.URIName;
+import sun.security.x509.X509CertImpl;
+
+/**
+ * This is a class that checks the revocation status of a certificate(s) using
+ * OCSP. It is not a PKIXCertPathChecker and therefore can be used outside of
+ * the CertPathValidator framework. It is useful when you want to
+ * just check the revocation status of a certificate, and you don't want to
+ * incur the overhead of validating all of the certificates in the
+ * associated certificate chain.
+ *
+ * @author Sean Mullan
+ */
+public final class OCSP {
+
+    static final ObjectIdentifier NONCE_EXTENSION_OID =
+        ObjectIdentifier.newInternal(new int[]{ 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    private static final int DEFAULT_CONNECT_TIMEOUT = 15000;
+
+    /**
+     * Integer value indicating the timeout length, in seconds, to be
+     * used for the OCSP check. A timeout of zero is interpreted as
+     * an infinite timeout.
+     */
+    private static final int CONNECT_TIMEOUT = initializeTimeout();
+
+    /**
+     * Initialize the timeout length by getting the OCSP timeout
+     * system property. If the property has not been set, or if its
+     * value is negative, set the timeout length to the default.
+     */
+    private static int initializeTimeout() {
+        Integer tmp = java.security.AccessController.doPrivileged(
+                new GetIntegerAction("com.sun.security.ocsp.timeout"));
+        if (tmp == null || tmp < 0) {
+            return DEFAULT_CONNECT_TIMEOUT;
+        }
+        // Convert to milliseconds, as the system property will be
+        // specified in seconds
+        return tmp * 1000;
+    }
+
+    private OCSP() {}
+
+    /**
+     * Obtains the revocation status of a certificate using OCSP using the most
+     * common defaults. The OCSP responder URI is retrieved from the
+     * certificate's AIA extension. The OCSP responder certificate is assumed
+     * to be the issuer's certificate (or issued by the issuer CA).
+     *
+     * @param cert the certificate to be checked
+     * @param issuerCert the issuer certificate
+     * @return the RevocationStatus
+     * @throws IOException if there is an exception connecting to or
+     *    communicating with the OCSP responder
+     * @throws CertPathValidatorException if an exception occurs while
+     *    encoding the OCSP Request or validating the OCSP Response
+     */
+    public static RevocationStatus check(X509Certificate cert,
+                                         X509Certificate issuerCert)
+        throws IOException, CertPathValidatorException {
+        CertId certId = null;
+        URI responderURI = null;
+        try {
+            X509CertImpl certImpl = X509CertImpl.toImpl(cert);
+            responderURI = getResponderURI(certImpl);
+            if (responderURI == null) {
+                throw new CertPathValidatorException
+                    ("No OCSP Responder URI in certificate");
+            }
+            certId = new CertId(issuerCert, certImpl.getSerialNumberObject());
+        } catch (CertificateException | IOException e) {
+            throw new CertPathValidatorException
+                ("Exception while encoding OCSPRequest", e);
+        }
+        OCSPResponse ocspResponse = check(Collections.singletonList(certId),
+            responderURI, issuerCert, null, null,
+            Collections.<Extension>emptyList());
+        return (RevocationStatus)ocspResponse.getSingleResponse(certId);
+    }
+
+    /**
+     * Obtains the revocation status of a certificate using OCSP.
+     *
+     * @param cert the certificate to be checked
+     * @param issuerCert the issuer certificate
+     * @param responderURI the URI of the OCSP responder
+     * @param responderCert the OCSP responder's certificate
+     * @param date the time the validity of the OCSP responder's certificate
+     *    should be checked against. If null, the current time is used.
+     * @return the RevocationStatus
+     * @throws IOException if there is an exception connecting to or
+     *    communicating with the OCSP responder
+     * @throws CertPathValidatorException if an exception occurs while
+     *    encoding the OCSP Request or validating the OCSP Response
+     */
+    public static RevocationStatus check(X509Certificate cert,
+                                         X509Certificate issuerCert,
+                                         URI responderURI,
+                                         X509Certificate responderCert,
+                                         Date date)
+        throws IOException, CertPathValidatorException
+    {
+        return check(cert, issuerCert, responderURI, responderCert, date,
+                     Collections.<Extension>emptyList());
+    }
+
+    // Called by com.sun.deploy.security.TrustDecider
+    public static RevocationStatus check(X509Certificate cert,
+                                         X509Certificate issuerCert,
+                                         URI responderURI,
+                                         X509Certificate responderCert,
+                                         Date date, List<Extension> extensions)
+        throws IOException, CertPathValidatorException
+    {
+        CertId certId = null;
+        try {
+            X509CertImpl certImpl = X509CertImpl.toImpl(cert);
+            certId = new CertId(issuerCert, certImpl.getSerialNumberObject());
+        } catch (CertificateException | IOException e) {
+            throw new CertPathValidatorException
+                ("Exception while encoding OCSPRequest", e);
+        }
+        OCSPResponse ocspResponse = check(Collections.singletonList(certId),
+            responderURI, issuerCert, responderCert, date, extensions);
+        return (RevocationStatus) ocspResponse.getSingleResponse(certId);
+    }
+
+    /**
+     * Checks the revocation status of a list of certificates using OCSP.
+     *
+     * @param certs the CertIds to be checked
+     * @param responderURI the URI of the OCSP responder
+     * @param issuerCert the issuer's certificate
+     * @param responderCert the OCSP responder's certificate
+     * @param date the time the validity of the OCSP responder's certificate
+     *    should be checked against. If null, the current time is used.
+     * @return the OCSPResponse
+     * @throws IOException if there is an exception connecting to or
+     *    communicating with the OCSP responder
+     * @throws CertPathValidatorException if an exception occurs while
+     *    encoding the OCSP Request or validating the OCSP Response
+     */
+    static OCSPResponse check(List<CertId> certIds, URI responderURI,
+                              X509Certificate issuerCert,
+                              X509Certificate responderCert, Date date,
+                              List<Extension> extensions)
+        throws IOException, CertPathValidatorException
+    {
+        byte[] bytes = null;
+        OCSPRequest request = null;
+        try {
+            request = new OCSPRequest(certIds, extensions);
+            bytes = request.encodeBytes();
+        } catch (IOException ioe) {
+            throw new CertPathValidatorException
+                ("Exception while encoding OCSPRequest", ioe);
+        }
+
+        InputStream in = null;
+        OutputStream out = null;
+        byte[] response = null;
+        try {
+            URL url = responderURI.toURL();
+            if (debug != null) {
+                debug.println("connecting to OCSP service at: " + url);
+            }
+            HttpURLConnection con = (HttpURLConnection)url.openConnection();
+            con.setConnectTimeout(CONNECT_TIMEOUT);
+            con.setReadTimeout(CONNECT_TIMEOUT);
+            con.setDoOutput(true);
+            con.setDoInput(true);
+            con.setRequestMethod("POST");
+            con.setRequestProperty
+                ("Content-type", "application/ocsp-request");
+            con.setRequestProperty
+                ("Content-length", String.valueOf(bytes.length));
+            out = con.getOutputStream();
+            out.write(bytes);
+            out.flush();
+            // Check the response
+            if (debug != null &&
+                con.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                debug.println("Received HTTP error: " + con.getResponseCode()
+                    + " - " + con.getResponseMessage());
+            }
+            in = con.getInputStream();
+            int contentLength = con.getContentLength();
+            if (contentLength == -1) {
+                contentLength = Integer.MAX_VALUE;
+            }
+            response = new byte[contentLength > 2048 ? 2048 : contentLength];
+            int total = 0;
+            while (total < contentLength) {
+                int count = in.read(response, total, response.length - total);
+                if (count < 0)
+                    break;
+
+                total += count;
+                if (total >= response.length && total < contentLength) {
+                    response = Arrays.copyOf(response, total * 2);
+                }
+            }
+            response = Arrays.copyOf(response, total);
+        } catch (IOException ioe) {
+            throw new CertPathValidatorException(
+                "Unable to determine revocation status due to network error",
+                ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException ioe) {
+                    throw ioe;
+                }
+            }
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException ioe) {
+                    throw ioe;
+                }
+            }
+        }
+
+        OCSPResponse ocspResponse = null;
+        try {
+            ocspResponse = new OCSPResponse(response);
+        } catch (IOException ioe) {
+            // response decoding exception
+            throw new CertPathValidatorException(ioe);
+        }
+
+        // verify the response
+        ocspResponse.verify(certIds, issuerCert, responderCert, date,
+            request.getNonce());
+
+        return ocspResponse;
+    }
+
+    /**
+     * Returns the URI of the OCSP Responder as specified in the
+     * certificate's Authority Information Access extension, or null if
+     * not specified.
+     *
+     * @param cert the certificate
+     * @return the URI of the OCSP Responder, or null if not specified
+     */
+    // Called by com.sun.deploy.security.TrustDecider
+    public static URI getResponderURI(X509Certificate cert) {
+        try {
+            return getResponderURI(X509CertImpl.toImpl(cert));
+        } catch (CertificateException ce) {
+            // treat this case as if the cert had no extension
+            return null;
+        }
+    }
+
+    static URI getResponderURI(X509CertImpl certImpl) {
+
+        // Examine the certificate's AuthorityInfoAccess extension
+        AuthorityInfoAccessExtension aia =
+            certImpl.getAuthorityInfoAccessExtension();
+        if (aia == null) {
+            return null;
+        }
+
+        List<AccessDescription> descriptions = aia.getAccessDescriptions();
+        for (AccessDescription description : descriptions) {
+            if (description.getAccessMethod().equals((Object)
+                AccessDescription.Ad_OCSP_Id)) {
+
+                GeneralName generalName = description.getAccessLocation();
+                if (generalName.getType() == GeneralNameInterface.NAME_URI) {
+                    URIName uri = (URIName) generalName.getName();
+                    return uri.getURI();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The Revocation Status of a certificate.
+     */
+    public static interface RevocationStatus {
+        public enum CertStatus { GOOD, REVOKED, UNKNOWN };
+
+        /**
+         * Returns the revocation status.
+         */
+        CertStatus getCertStatus();
+        /**
+         * Returns the time when the certificate was revoked, or null
+         * if it has not been revoked.
+         */
+        Date getRevocationTime();
+        /**
+         * Returns the reason the certificate was revoked, or null if it
+         * has not been revoked.
+         */
+        CRLReason getRevocationReason();
+
+        /**
+         * Returns a Map of additional extensions.
+         */
+        Map<String, Extension> getSingleExtensions();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/OCSPRequest.java b/ojluni/src/main/java/sun/security/provider/certpath/OCSPRequest.java
new file mode 100644
index 0000000..6bded97
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/OCSPRequest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.cert.Extension;
+import java.util.Collections;
+import java.util.List;
+
+import sun.misc.HexDumpEncoder;
+import sun.security.util.*;
+
+/**
+ * This class can be used to generate an OCSP request and send it over
+ * an outputstream. Currently we do not support signing requests
+ * The OCSP Request is specified in RFC 2560 and
+ * the ASN.1 definition is as follows:
+ * <pre>
+ *
+ * OCSPRequest     ::=     SEQUENCE {
+ *      tbsRequest                  TBSRequest,
+ *      optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
+ *
+ *   TBSRequest      ::=     SEQUENCE {
+ *      version             [0]     EXPLICIT Version DEFAULT v1,
+ *      requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
+ *      requestList                 SEQUENCE OF Request,
+ *      requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
+ *
+ *  Signature       ::=     SEQUENCE {
+ *      signatureAlgorithm      AlgorithmIdentifier,
+ *      signature               BIT STRING,
+ *      certs               [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
+ *   }
+ *
+ *  Version         ::=             INTEGER  {  v1(0) }
+ *
+ *  Request         ::=     SEQUENCE {
+ *      reqCert                     CertID,
+ *      singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL }
+ *
+ *  CertID          ::= SEQUENCE {
+ *       hashAlgorithm  AlgorithmIdentifier,
+ *       issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ *       issuerKeyHash  OCTET STRING, -- Hash of Issuers public key
+ *       serialNumber   CertificateSerialNumber
+ * }
+ *
+ * </pre>
+ *
+ * @author      Ram Marti
+ */
+
+class OCSPRequest {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private static final boolean dump = debug != null && Debug.isOn("ocsp");
+
+    // List of request CertIds
+    private final List<CertId> certIds;
+    private final List<Extension> extensions;
+    private byte[] nonce;
+
+    /*
+     * Constructs an OCSPRequest. This constructor is used
+     * to construct an unsigned OCSP Request for a single user cert.
+     */
+    OCSPRequest(CertId certId) {
+        this(Collections.singletonList(certId));
+    }
+
+    OCSPRequest(List<CertId> certIds) {
+        this.certIds = certIds;
+        this.extensions = Collections.<Extension>emptyList();
+    }
+
+    OCSPRequest(List<CertId> certIds, List<Extension> extensions) {
+        this.certIds = certIds;
+        this.extensions = extensions;
+    }
+
+    byte[] encodeBytes() throws IOException {
+
+        // encode tbsRequest
+        DerOutputStream tmp = new DerOutputStream();
+        DerOutputStream requestsOut = new DerOutputStream();
+        for (CertId certId : certIds) {
+            DerOutputStream certIdOut = new DerOutputStream();
+            certId.encode(certIdOut);
+            requestsOut.write(DerValue.tag_Sequence, certIdOut);
+        }
+
+        tmp.write(DerValue.tag_Sequence, requestsOut);
+        if (!extensions.isEmpty()) {
+            DerOutputStream extOut = new DerOutputStream();
+            for (Extension ext : extensions) {
+                ext.encode(extOut);
+                if (ext.getId().equals(OCSP.NONCE_EXTENSION_OID.toString())) {
+                    nonce = ext.getValue();
+                }
+            }
+            DerOutputStream extsOut = new DerOutputStream();
+            extsOut.write(DerValue.tag_Sequence, extOut);
+            tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                                         true, (byte)2), extsOut);
+        }
+
+        DerOutputStream tbsRequest = new DerOutputStream();
+        tbsRequest.write(DerValue.tag_Sequence, tmp);
+
+        // OCSPRequest without the signature
+        DerOutputStream ocspRequest = new DerOutputStream();
+        ocspRequest.write(DerValue.tag_Sequence, tbsRequest);
+
+        byte[] bytes = ocspRequest.toByteArray();
+
+        if (dump) {
+            HexDumpEncoder hexEnc = new HexDumpEncoder();
+            debug.println("OCSPRequest bytes...\n\n" +
+                hexEnc.encode(bytes) + "\n");
+        }
+
+        return bytes;
+    }
+
+    List<CertId> getCertIds() {
+        return certIds;
+    }
+
+    byte[] getNonce() {
+        return nonce;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java b/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java
new file mode 100644
index 0000000..8075d73
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.CRLReason;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.security.auth.x500.X500Principal;
+
+import sun.misc.HexDumpEncoder;
+import sun.security.action.GetIntegerAction;
+import sun.security.x509.*;
+import sun.security.util.*;
+
+/**
+ * This class is used to process an OCSP response.
+ * The OCSP Response is defined
+ * in RFC 2560 and the ASN.1 encoding is as follows:
+ * <pre>
+ *
+ *  OCSPResponse ::= SEQUENCE {
+ *      responseStatus         OCSPResponseStatus,
+ *      responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+ *
+ *   OCSPResponseStatus ::= ENUMERATED {
+ *       successful            (0),  --Response has valid confirmations
+ *       malformedRequest      (1),  --Illegal confirmation request
+ *       internalError         (2),  --Internal error in issuer
+ *       tryLater              (3),  --Try again later
+ *                                   --(4) is not used
+ *       sigRequired           (5),  --Must sign the request
+ *       unauthorized          (6)   --Request unauthorized
+ *   }
+ *
+ *   ResponseBytes ::=       SEQUENCE {
+ *       responseType   OBJECT IDENTIFIER,
+ *       response       OCTET STRING }
+ *
+ *   BasicOCSPResponse       ::= SEQUENCE {
+ *      tbsResponseData      ResponseData,
+ *      signatureAlgorithm   AlgorithmIdentifier,
+ *      signature            BIT STRING,
+ *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ *
+ *   The value for signature SHALL be computed on the hash of the DER
+ *   encoding ResponseData.
+ *
+ *   ResponseData ::= SEQUENCE {
+ *      version              [0] EXPLICIT Version DEFAULT v1,
+ *      responderID              ResponderID,
+ *      producedAt               GeneralizedTime,
+ *      responses                SEQUENCE OF SingleResponse,
+ *      responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ *
+ *   ResponderID ::= CHOICE {
+ *      byName               [1] Name,
+ *      byKey                [2] KeyHash }
+ *
+ *   KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
+ *   (excluding the tag and length fields)
+ *
+ *   SingleResponse ::= SEQUENCE {
+ *      certID                       CertID,
+ *      certStatus                   CertStatus,
+ *      thisUpdate                   GeneralizedTime,
+ *      nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+ *      singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+ *
+ *   CertStatus ::= CHOICE {
+ *       good        [0]     IMPLICIT NULL,
+ *       revoked     [1]     IMPLICIT RevokedInfo,
+ *       unknown     [2]     IMPLICIT UnknownInfo }
+ *
+ *   RevokedInfo ::= SEQUENCE {
+ *       revocationTime              GeneralizedTime,
+ *       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+ *
+ *   UnknownInfo ::= NULL -- this can be replaced with an enumeration
+ *
+ * </pre>
+ *
+ * @author      Ram Marti
+ */
+
+public final class OCSPResponse {
+
+    public enum ResponseStatus {
+        SUCCESSFUL,            // Response has valid confirmations
+        MALFORMED_REQUEST,     // Illegal request
+        INTERNAL_ERROR,        // Internal error in responder
+        TRY_LATER,             // Try again later
+        UNUSED,                // is not used
+        SIG_REQUIRED,          // Must sign the request
+        UNAUTHORIZED           // Request unauthorized
+    };
+    private static ResponseStatus[] rsvalues = ResponseStatus.values();
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private static final boolean dump = debug != null && Debug.isOn("ocsp");
+    private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
+        ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
+    private static final int CERT_STATUS_GOOD = 0;
+    private static final int CERT_STATUS_REVOKED = 1;
+    private static final int CERT_STATUS_UNKNOWN = 2;
+
+    // ResponderID CHOICE tags
+    private static final int NAME_TAG = 1;
+    private static final int KEY_TAG = 2;
+
+    // Object identifier for the OCSPSigning key purpose
+    private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
+
+    // Default maximum clock skew in milliseconds (15 minutes)
+    // allowed when checking validity of OCSP responses
+    private static final int DEFAULT_MAX_CLOCK_SKEW = 900000;
+
+    /**
+     * Integer value indicating the maximum allowable clock skew, in seconds,
+     * to be used for the OCSP check.
+     */
+    private static final int MAX_CLOCK_SKEW = initializeClockSkew();
+
+    /**
+     * Initialize the maximum allowable clock skew by getting the OCSP
+     * clock skew system property. If the property has not been set, or if its
+     * value is negative, set the skew to the default.
+     */
+    private static int initializeClockSkew() {
+        Integer tmp = java.security.AccessController.doPrivileged(
+                new GetIntegerAction("com.sun.security.ocsp.clockSkew"));
+        if (tmp == null || tmp < 0) {
+            return DEFAULT_MAX_CLOCK_SKEW;
+        }
+        // Convert to milliseconds, as the system property will be
+        // specified in seconds
+        return tmp * 1000;
+    }
+
+    // an array of all of the CRLReasons (used in SingleResponse)
+    private static CRLReason[] values = CRLReason.values();
+
+    private final ResponseStatus responseStatus;
+    private final Map<CertId, SingleResponse> singleResponseMap;
+    private final AlgorithmId sigAlgId;
+    private final byte[] signature;
+    private final byte[] tbsResponseData;
+    private final byte[] responseNonce;
+    private List<X509CertImpl> certs;
+    private X509CertImpl signerCert = null;
+    private X500Principal responderName = null;
+    private KeyIdentifier responderKeyId = null;
+
+    /*
+     * Create an OCSP response from its ASN.1 DER encoding.
+     */
+    OCSPResponse(byte[] bytes) throws IOException {
+        if (dump) {
+            HexDumpEncoder hexEnc = new HexDumpEncoder();
+            debug.println("OCSPResponse bytes...\n\n" +
+                hexEnc.encode(bytes) + "\n");
+        }
+        DerValue der = new DerValue(bytes);
+        if (der.tag != DerValue.tag_Sequence) {
+            throw new IOException("Bad encoding in OCSP response: " +
+                "expected ASN.1 SEQUENCE tag.");
+        }
+        DerInputStream derIn = der.getData();
+
+        // responseStatus
+        int status = derIn.getEnumerated();
+        if (status >= 0 && status < rsvalues.length) {
+            responseStatus = rsvalues[status];
+        } else {
+            // unspecified responseStatus
+            throw new IOException("Unknown OCSPResponse status: " + status);
+        }
+        if (debug != null) {
+            debug.println("OCSP response status: " + responseStatus);
+        }
+        if (responseStatus != ResponseStatus.SUCCESSFUL) {
+            // no need to continue, responseBytes are not set.
+            singleResponseMap = Collections.emptyMap();
+            certs = new ArrayList<X509CertImpl>();
+            sigAlgId = null;
+            signature = null;
+            tbsResponseData = null;
+            responseNonce = null;
+            return;
+        }
+
+        // responseBytes
+        der = derIn.getDerValue();
+        if (!der.isContextSpecific((byte)0)) {
+            throw new IOException("Bad encoding in responseBytes element " +
+                "of OCSP response: expected ASN.1 context specific tag 0.");
+        }
+        DerValue tmp = der.data.getDerValue();
+        if (tmp.tag != DerValue.tag_Sequence) {
+            throw new IOException("Bad encoding in responseBytes element " +
+                "of OCSP response: expected ASN.1 SEQUENCE tag.");
+        }
+
+        // responseType
+        derIn = tmp.data;
+        ObjectIdentifier responseType = derIn.getOID();
+        if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {
+            if (debug != null) {
+                debug.println("OCSP response type: basic");
+            }
+        } else {
+            if (debug != null) {
+                debug.println("OCSP response type: " + responseType);
+            }
+            throw new IOException("Unsupported OCSP response type: " +
+                                  responseType);
+        }
+
+        // BasicOCSPResponse
+        DerInputStream basicOCSPResponse =
+            new DerInputStream(derIn.getOctetString());
+
+        DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
+        if (seqTmp.length < 3) {
+            throw new IOException("Unexpected BasicOCSPResponse value");
+        }
+
+        DerValue responseData = seqTmp[0];
+
+        // Need the DER encoded ResponseData to verify the signature later
+        tbsResponseData = seqTmp[0].toByteArray();
+
+        // tbsResponseData
+        if (responseData.tag != DerValue.tag_Sequence) {
+            throw new IOException("Bad encoding in tbsResponseData " +
+                "element of OCSP response: expected ASN.1 SEQUENCE tag.");
+        }
+        DerInputStream seqDerIn = responseData.data;
+        DerValue seq = seqDerIn.getDerValue();
+
+        // version
+        if (seq.isContextSpecific((byte)0)) {
+            // seq[0] is version
+            if (seq.isConstructed() && seq.isContextSpecific()) {
+                //System.out.println ("version is available");
+                seq = seq.data.getDerValue();
+                int version = seq.getInteger();
+                if (seq.data.available() != 0) {
+                    throw new IOException("Bad encoding in version " +
+                        " element of OCSP response: bad format");
+                }
+                seq = seqDerIn.getDerValue();
+            }
+        }
+
+        // responderID
+        short tag = (byte)(seq.tag & 0x1f);
+        if (tag == NAME_TAG) {
+            responderName = new X500Principal(seq.getData().toByteArray());
+            if (debug != null) {
+                debug.println("Responder's name: " + responderName);
+            }
+        } else if (tag == KEY_TAG) {
+            responderKeyId = new KeyIdentifier(seq.getData().getOctetString());
+            if (debug != null) {
+                debug.println("Responder's key ID: " +
+                    Debug.toString(responderKeyId.getIdentifier()));
+            }
+        } else {
+            throw new IOException("Bad encoding in responderID element of " +
+                "OCSP response: expected ASN.1 context specific tag 0 or 1");
+        }
+
+        // producedAt
+        seq = seqDerIn.getDerValue();
+        if (debug != null) {
+            Date producedAtDate = seq.getGeneralizedTime();
+            debug.println("OCSP response produced at: " + producedAtDate);
+        }
+
+        // responses
+        DerValue[] singleResponseDer = seqDerIn.getSequence(1);
+        singleResponseMap = new HashMap<>(singleResponseDer.length);
+        if (debug != null) {
+            debug.println("OCSP number of SingleResponses: "
+                          + singleResponseDer.length);
+        }
+        for (int i = 0; i < singleResponseDer.length; i++) {
+            SingleResponse singleResponse =
+                new SingleResponse(singleResponseDer[i]);
+            singleResponseMap.put(singleResponse.getCertId(), singleResponse);
+        }
+
+        // responseExtensions
+        byte[] nonce = null;
+        if (seqDerIn.available() > 0) {
+            seq = seqDerIn.getDerValue();
+            if (seq.isContextSpecific((byte)1)) {
+                DerValue[] responseExtDer = seq.data.getSequence(3);
+                for (int i = 0; i < responseExtDer.length; i++) {
+                    Extension ext = new Extension(responseExtDer[i]);
+                    if (debug != null) {
+                        debug.println("OCSP extension: " + ext);
+                    }
+                    // Only the NONCE extension is recognized
+                    if (ext.getExtensionId().equals((Object)
+                        OCSP.NONCE_EXTENSION_OID))
+                    {
+                        nonce = ext.getExtensionValue();
+                    } else if (ext.isCritical())  {
+                        throw new IOException(
+                            "Unsupported OCSP critical extension: " +
+                            ext.getExtensionId());
+                    }
+                }
+            }
+        }
+        responseNonce = nonce;
+
+        // signatureAlgorithmId
+        sigAlgId = AlgorithmId.parse(seqTmp[1]);
+
+        // signature
+        signature = seqTmp[2].getBitString();
+
+        // if seq[3] is available , then it is a sequence of certificates
+        if (seqTmp.length > 3) {
+            // certs are available
+            DerValue seqCert = seqTmp[3];
+            if (!seqCert.isContextSpecific((byte)0)) {
+                throw new IOException("Bad encoding in certs element of " +
+                    "OCSP response: expected ASN.1 context specific tag 0.");
+            }
+            DerValue[] derCerts = seqCert.getData().getSequence(3);
+            certs = new ArrayList<X509CertImpl>(derCerts.length);
+            try {
+                for (int i = 0; i < derCerts.length; i++) {
+                    X509CertImpl cert =
+                        new X509CertImpl(derCerts[i].toByteArray());
+                    certs.add(cert);
+
+                    if (debug != null) {
+                        debug.println("OCSP response cert #" + (i + 1) + ": " +
+                            cert.getSubjectX500Principal());
+                    }
+                }
+            } catch (CertificateException ce) {
+                throw new IOException("Bad encoding in X509 Certificate", ce);
+            }
+        } else {
+            certs = new ArrayList<X509CertImpl>();
+        }
+    }
+
+    void verify(List<CertId> certIds, X509Certificate issuerCert,
+                X509Certificate responderCert, Date date, byte[] nonce)
+        throws CertPathValidatorException
+    {
+        switch (responseStatus) {
+            case SUCCESSFUL:
+                break;
+            case TRY_LATER:
+            case INTERNAL_ERROR:
+                throw new CertPathValidatorException(
+                    "OCSP response error: " + responseStatus, null, null, -1,
+                    BasicReason.UNDETERMINED_REVOCATION_STATUS);
+            case UNAUTHORIZED:
+            default:
+                throw new CertPathValidatorException("OCSP response error: " +
+                                                     responseStatus);
+        }
+
+        // Check that the response includes a response for all of the
+        // certs that were supplied in the request
+        for (CertId certId : certIds) {
+            SingleResponse sr = getSingleResponse(certId);
+            if (sr == null) {
+                if (debug != null) {
+                    debug.println("No response found for CertId: " + certId);
+                }
+                throw new CertPathValidatorException(
+                    "OCSP response does not include a response for a " +
+                    "certificate supplied in the OCSP request");
+            }
+            if (debug != null) {
+                debug.println("Status of certificate (with serial number " +
+                    certId.getSerialNumber() + ") is: " + sr.getCertStatus());
+            }
+        }
+
+        // Locate the signer cert
+        if (signerCert == null) {
+            // Add the Issuing CA cert and/or Trusted Responder cert to the list
+            // of certs from the OCSP response
+            try {
+                certs.add(X509CertImpl.toImpl(issuerCert));
+                if (responderCert != null) {
+                    certs.add(X509CertImpl.toImpl(responderCert));
+                }
+            } catch (CertificateException ce) {
+                throw new CertPathValidatorException(
+                    "Invalid issuer or trusted responder certificate", ce);
+            }
+
+            if (responderName != null) {
+                for (X509CertImpl cert : certs) {
+                    if (cert.getSubjectX500Principal().equals(responderName)) {
+                        signerCert = cert;
+                        break;
+                    }
+                }
+            } else if (responderKeyId != null) {
+                for (X509CertImpl cert : certs) {
+                    // Match responder's key identifier against the cert's SKID
+                    // This will match if the SKID is encoded using the 160-bit
+                    // SHA-1 hash method as defined in RFC 5280.
+                    KeyIdentifier certKeyId = cert.getSubjectKeyId();
+                    if (certKeyId != null && responderKeyId.equals(certKeyId)) {
+                        signerCert = cert;
+                        break;
+                    } else {
+                        // The certificate does not have a SKID or may have
+                        // been using a different algorithm (ex: see RFC 7093).
+                        // Check if the responder's key identifier matches
+                        // against a newly generated key identifier of the
+                        // cert's public key using the 160-bit SHA-1 method.
+                        try {
+                            certKeyId = new KeyIdentifier(cert.getPublicKey());
+                        } catch (IOException e) {
+                            // ignore
+                        }
+                        if (responderKeyId.equals(certKeyId)) {
+                            signerCert = cert;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Check whether the signer cert returned by the responder is trusted
+        if (signerCert != null) {
+            // Check if the response is signed by the issuing CA
+            if (signerCert.equals(issuerCert)) {
+                if (debug != null) {
+                    debug.println("OCSP response is signed by the target's " +
+                        "Issuing CA");
+                }
+                // cert is trusted, now verify the signed response
+
+            // Check if the response is signed by a trusted responder
+            } else if (signerCert.equals(responderCert)) {
+                if (debug != null) {
+                    debug.println("OCSP response is signed by a Trusted " +
+                        "Responder");
+                }
+                // cert is trusted, now verify the signed response
+
+            // Check if the response is signed by an authorized responder
+            } else if (signerCert.getIssuerX500Principal().equals(
+                       issuerCert.getSubjectX500Principal())) {
+
+                // Check for the OCSPSigning key purpose
+                try {
+                    List<String> keyPurposes = signerCert.getExtendedKeyUsage();
+                    if (keyPurposes == null ||
+                        !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
+                        throw new CertPathValidatorException(
+                            "Responder's certificate not valid for signing " +
+                            "OCSP responses");
+                    }
+                } catch (CertificateParsingException cpe) {
+                    // assume cert is not valid for signing
+                    throw new CertPathValidatorException(
+                        "Responder's certificate not valid for signing " +
+                        "OCSP responses", cpe);
+                }
+
+                // Check algorithm constraints specified in security property
+                // "jdk.certpath.disabledAlgorithms".
+                AlgorithmChecker algChecker = new AlgorithmChecker(
+                                    new TrustAnchor(issuerCert, null));
+                algChecker.init(false);
+                algChecker.check(signerCert, Collections.<String>emptySet());
+
+                // check the validity
+                try {
+                    if (date == null) {
+                        signerCert.checkValidity();
+                    } else {
+                        signerCert.checkValidity(date);
+                    }
+                } catch (CertificateException e) {
+                    throw new CertPathValidatorException(
+                        "Responder's certificate not within the " +
+                        "validity period", e);
+                }
+
+                // check for revocation
+                //
+                // A CA may specify that an OCSP client can trust a
+                // responder for the lifetime of the responder's
+                // certificate. The CA does so by including the
+                // extension id-pkix-ocsp-nocheck.
+                //
+                Extension noCheck =
+                    signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
+                if (noCheck != null) {
+                    if (debug != null) {
+                        debug.println("Responder's certificate includes " +
+                            "the extension id-pkix-ocsp-nocheck.");
+                    }
+                } else {
+                    // we should do the revocation checking of the
+                    // authorized responder in a future update.
+                }
+
+                // verify the signature
+                try {
+                    signerCert.verify(issuerCert.getPublicKey());
+                    if (debug != null) {
+                        debug.println("OCSP response is signed by an " +
+                            "Authorized Responder");
+                    }
+                    // cert is trusted, now verify the signed response
+
+                } catch (GeneralSecurityException e) {
+                    signerCert = null;
+                }
+            } else {
+                throw new CertPathValidatorException(
+                    "Responder's certificate is not authorized to sign " +
+                    "OCSP responses");
+            }
+        }
+
+        // Confirm that the signed response was generated using the public
+        // key from the trusted responder cert
+        if (signerCert != null) {
+            // Check algorithm constraints specified in security property
+            // "jdk.certpath.disabledAlgorithms".
+            AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId);
+
+            if (!verifySignature(signerCert)) {
+                throw new CertPathValidatorException(
+                    "Error verifying OCSP Response's signature");
+            }
+        } else {
+            // Need responder's cert in order to verify the signature
+            throw new CertPathValidatorException(
+                "Unable to verify OCSP Response's signature");
+        }
+
+        // Check freshness of OCSPResponse
+        if (nonce != null) {
+            if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) {
+                throw new CertPathValidatorException("Nonces don't match");
+            }
+        }
+
+        long now = (date == null) ? System.currentTimeMillis() : date.getTime();
+        Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
+        Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
+        for (SingleResponse sr : singleResponseMap.values()) {
+            if (debug != null) {
+                String until = "";
+                if (sr.nextUpdate != null) {
+                    until = " until " + sr.nextUpdate;
+                }
+                debug.println("Response's validity interval is from " +
+                              sr.thisUpdate + until);
+            }
+
+            // Check that the test date is within the validity interval
+            if ((sr.thisUpdate != null && nowPlusSkew.before(sr.thisUpdate)) ||
+                (sr.nextUpdate != null && nowMinusSkew.after(sr.nextUpdate)))
+            {
+                throw new CertPathValidatorException(
+                                      "Response is unreliable: its validity " +
+                                      "interval is out-of-date");
+            }
+        }
+    }
+
+    /**
+     * Returns the OCSP ResponseStatus.
+     */
+    ResponseStatus getResponseStatus() {
+        return responseStatus;
+    }
+
+    /*
+     * Verify the signature of the OCSP response.
+     */
+    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);
+        }
+    }
+
+    /**
+     * Returns the SingleResponse of the specified CertId, or null if
+     * there is no response for that CertId.
+     */
+    SingleResponse getSingleResponse(CertId certId) {
+        return singleResponseMap.get(certId);
+    }
+
+    /*
+     * Returns the certificate for the authority that signed the OCSP response.
+     */
+    X509Certificate getSignerCertificate() {
+        return signerCert; // set in verify()
+    }
+
+    /*
+     * A class representing a single OCSP response.
+     */
+    final static class SingleResponse implements OCSP.RevocationStatus {
+        private final CertId certId;
+        private final CertStatus certStatus;
+        private final Date thisUpdate;
+        private final Date nextUpdate;
+        private final Date revocationTime;
+        private final CRLReason revocationReason;
+        private final Map<String, java.security.cert.Extension> singleExtensions;
+
+        private SingleResponse(DerValue der) throws IOException {
+            if (der.tag != DerValue.tag_Sequence) {
+                throw new IOException("Bad ASN.1 encoding in SingleResponse");
+            }
+            DerInputStream tmp = der.data;
+
+            certId = new CertId(tmp.getDerValue().data);
+            DerValue derVal = tmp.getDerValue();
+            short tag = (byte)(derVal.tag & 0x1f);
+            if (tag ==  CERT_STATUS_REVOKED) {
+                certStatus = CertStatus.REVOKED;
+                revocationTime = derVal.data.getGeneralizedTime();
+                if (derVal.data.available() != 0) {
+                    DerValue dv = derVal.data.getDerValue();
+                    tag = (byte)(dv.tag & 0x1f);
+                    if (tag == 0) {
+                        int reason = dv.data.getEnumerated();
+                        // if reason out-of-range just leave as UNSPECIFIED
+                        if (reason >= 0 && reason < values.length) {
+                            revocationReason = values[reason];
+                        } else {
+                            revocationReason = CRLReason.UNSPECIFIED;
+                        }
+                    } else {
+                        revocationReason = CRLReason.UNSPECIFIED;
+                    }
+                } else {
+                    revocationReason = CRLReason.UNSPECIFIED;
+                }
+                // RevokedInfo
+                if (debug != null) {
+                    debug.println("Revocation time: " + revocationTime);
+                    debug.println("Revocation reason: " + revocationReason);
+                }
+            } else {
+                revocationTime = null;
+                revocationReason = CRLReason.UNSPECIFIED;
+                if (tag == CERT_STATUS_GOOD) {
+                    certStatus = CertStatus.GOOD;
+                } else if (tag == CERT_STATUS_UNKNOWN) {
+                    certStatus = CertStatus.UNKNOWN;
+                } else {
+                    throw new IOException("Invalid certificate status");
+                }
+            }
+
+            thisUpdate = tmp.getGeneralizedTime();
+
+            if (tmp.available() == 0)  {
+                // we are done
+                nextUpdate = null;
+            } else {
+                derVal = tmp.getDerValue();
+                tag = (byte)(derVal.tag & 0x1f);
+                if (tag == 0) {
+                    // next update
+                    nextUpdate = derVal.data.getGeneralizedTime();
+
+                    if (tmp.available() == 0)  {
+                        // we are done
+                    } else {
+                        derVal = tmp.getDerValue();
+                        tag = (byte)(derVal.tag & 0x1f);
+                    }
+                } else {
+                    nextUpdate = null;
+                }
+            }
+            // singleExtensions
+            if (tmp.available() > 0) {
+                derVal = tmp.getDerValue();
+                if (derVal.isContextSpecific((byte)1)) {
+                    DerValue[] singleExtDer = derVal.data.getSequence(3);
+                    singleExtensions =
+                        new HashMap<String, java.security.cert.Extension>
+                            (singleExtDer.length);
+                    for (int i = 0; i < singleExtDer.length; i++) {
+                        Extension ext = new Extension(singleExtDer[i]);
+                        if (debug != null) {
+                            debug.println("OCSP single extension: " + ext);
+                        }
+                        // We don't support any extensions yet. Therefore, if it
+                        // is critical we must throw an exception because we
+                        // don't know how to process it.
+                        if (ext.isCritical()) {
+                            throw new IOException(
+                                "Unsupported OCSP critical extension: " +
+                                ext.getExtensionId());
+                        }
+                        singleExtensions.put(ext.getId(), ext);
+                    }
+                } else {
+                    singleExtensions = Collections.emptyMap();
+                }
+            } else {
+                singleExtensions = Collections.emptyMap();
+            }
+        }
+
+        /*
+         * Return the certificate's revocation status code
+         */
+        @Override public CertStatus getCertStatus() {
+            return certStatus;
+        }
+
+        private CertId getCertId() {
+            return certId;
+        }
+
+        @Override public Date getRevocationTime() {
+            return (Date) revocationTime.clone();
+        }
+
+        @Override public CRLReason getRevocationReason() {
+            return revocationReason;
+        }
+
+        @Override
+        public Map<String, java.security.cert.Extension> getSingleExtensions() {
+            return Collections.unmodifiableMap(singleExtensions);
+        }
+
+        /**
+         * Construct a string representation of a single OCSP response.
+         */
+        @Override public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("SingleResponse:  \n");
+            sb.append(certId);
+            sb.append("\nCertStatus: "+ certStatus + "\n");
+            if (certStatus == CertStatus.REVOKED) {
+                sb.append("revocationTime is " + revocationTime + "\n");
+                sb.append("revocationReason is " + revocationReason + "\n");
+            }
+            sb.append("thisUpdate is " + thisUpdate + "\n");
+            if (nextUpdate != null) {
+                sb.append("nextUpdate is " + nextUpdate + "\n");
+            }
+            return sb.toString();
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java b/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java
new file mode 100644
index 0000000..98c8834
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PKIX.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.provider.certpath;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.security.cert.*;
+import java.security.interfaces.DSAPublicKey;
+import java.util.*;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.util.Debug;
+
+/**
+ * Common utility methods and classes used by the PKIX CertPathValidator and
+ * CertPathBuilder implementation.
+ */
+class PKIX {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    private PKIX() { }
+
+    static boolean isDSAPublicKeyWithoutParams(PublicKey publicKey) {
+        return (publicKey instanceof DSAPublicKey &&
+               ((DSAPublicKey)publicKey).getParams() == null);
+    }
+
+    static ValidatorParams checkParams(CertPath cp, CertPathParameters params)
+        throws InvalidAlgorithmParameterException
+    {
+        if (!(params instanceof PKIXParameters)) {
+            throw new InvalidAlgorithmParameterException("inappropriate "
+                + "params, must be an instance of PKIXParameters");
+        }
+        return new ValidatorParams(cp, (PKIXParameters)params);
+    }
+
+    static BuilderParams checkBuilderParams(CertPathParameters params)
+        throws InvalidAlgorithmParameterException
+    {
+        if (!(params instanceof PKIXBuilderParameters)) {
+            throw new InvalidAlgorithmParameterException("inappropriate "
+                + "params, must be an instance of PKIXBuilderParameters");
+        }
+        return new BuilderParams((PKIXBuilderParameters)params);
+    }
+
+    /**
+     * PKIXParameters that are shared by the PKIX CertPathValidator
+     * implementation. Provides additional functionality and avoids
+     * unnecessary cloning.
+     */
+    static class ValidatorParams {
+        private final PKIXParameters params;
+        private CertPath certPath;
+        private List<PKIXCertPathChecker> checkers;
+        private List<CertStore> stores;
+        private boolean gotDate;
+        private Date date;
+        private Set<String> policies;
+        private boolean gotConstraints;
+        private CertSelector constraints;
+        private Set<TrustAnchor> anchors;
+        private List<X509Certificate> certs;
+
+        ValidatorParams(CertPath cp, PKIXParameters params)
+            throws InvalidAlgorithmParameterException
+        {
+            this(params);
+            if (!cp.getType().equals("X.509") && !cp.getType().equals("X509")) {
+                throw new InvalidAlgorithmParameterException("inappropriate "
+                    + "CertPath type specified, must be X.509 or X509");
+            }
+            this.certPath = cp;
+        }
+
+        ValidatorParams(PKIXParameters params)
+            throws InvalidAlgorithmParameterException
+        {
+            this.anchors = params.getTrustAnchors();
+            // Make sure that none of the trust anchors include name constraints
+            // (not supported).
+            for (TrustAnchor anchor : this.anchors) {
+                if (anchor.getNameConstraints() != null) {
+                    throw new InvalidAlgorithmParameterException
+                        ("name constraints in trust anchor not supported");
+                }
+            }
+            this.params = params;
+        }
+
+        CertPath certPath() {
+            return certPath;
+        }
+        // called by CertPathBuilder after path has been built
+        void setCertPath(CertPath cp) {
+            this.certPath = cp;
+        }
+        List<X509Certificate> certificates() {
+            if (certs == null) {
+                if (certPath == null) {
+                    certs = Collections.emptyList();
+                } else {
+                    // Reverse the ordering for validation so that the target
+                    // cert is the last certificate
+                    @SuppressWarnings("unchecked")
+                    List<X509Certificate> xc = new ArrayList<>
+                        ((List<X509Certificate>)certPath.getCertificates());
+                    Collections.reverse(xc);
+                    certs = xc;
+                }
+            }
+            return certs;
+        }
+        List<PKIXCertPathChecker> certPathCheckers() {
+            if (checkers == null)
+                checkers = params.getCertPathCheckers();
+            return checkers;
+        }
+        List<CertStore> certStores() {
+            if (stores == null)
+                stores = params.getCertStores();
+            return stores;
+        }
+        Date date() {
+            if (!gotDate) {
+                date = params.getDate();
+                if (date == null)
+                    date = new Date();
+                gotDate = true;
+            }
+            return date;
+        }
+        Set<String> initialPolicies() {
+            if (policies == null)
+                policies = params.getInitialPolicies();
+            return policies;
+        }
+        CertSelector targetCertConstraints() {
+            if (!gotConstraints) {
+                constraints = params.getTargetCertConstraints();
+                gotConstraints = true;
+            }
+            return constraints;
+        }
+        Set<TrustAnchor> trustAnchors() {
+            return anchors;
+        }
+        boolean revocationEnabled() {
+            return params.isRevocationEnabled();
+        }
+        boolean policyMappingInhibited() {
+            return params.isPolicyMappingInhibited();
+        }
+        boolean explicitPolicyRequired() {
+            return params.isExplicitPolicyRequired();
+        }
+        boolean policyQualifiersRejected() {
+            return params.getPolicyQualifiersRejected();
+        }
+        String sigProvider() { return params.getSigProvider(); }
+        boolean anyPolicyInhibited() { return params.isAnyPolicyInhibited(); }
+
+        // in rare cases we need access to the original params, for example
+        // in order to clone CertPathCheckers before building a new chain
+        PKIXParameters getPKIXParameters() {
+            return params;
+        }
+    }
+
+    static class BuilderParams extends ValidatorParams {
+        private PKIXBuilderParameters params;
+        private boolean buildForward = true;
+        private List<CertStore> stores;
+        private X500Principal targetSubject;
+
+        BuilderParams(PKIXBuilderParameters params)
+            throws InvalidAlgorithmParameterException
+        {
+            super(params);
+            checkParams(params);
+        }
+        private void checkParams(PKIXBuilderParameters params)
+            throws InvalidAlgorithmParameterException
+        {
+            CertSelector sel = targetCertConstraints();
+            if (!(sel instanceof X509CertSelector)) {
+                throw new InvalidAlgorithmParameterException("the "
+                    + "targetCertConstraints parameter must be an "
+                    + "X509CertSelector");
+            }
+            if (params instanceof SunCertPathBuilderParameters) {
+                buildForward =
+                    ((SunCertPathBuilderParameters)params).getBuildForward();
+            }
+            this.params = params;
+            this.targetSubject = getTargetSubject(
+                certStores(), (X509CertSelector)targetCertConstraints());
+        }
+        @Override List<CertStore> certStores() {
+            if (stores == null) {
+                // reorder CertStores so that local CertStores are tried first
+                stores = new ArrayList<>(params.getCertStores());
+                Collections.sort(stores, new CertStoreComparator());
+            }
+            return stores;
+        }
+        int maxPathLength() { return params.getMaxPathLength(); }
+        boolean buildForward() { return buildForward; }
+        PKIXBuilderParameters params() { return params; }
+        X500Principal targetSubject() { return targetSubject; }
+
+        /**
+         * Returns the target subject DN from the first X509Certificate that
+         * is fetched that matches the specified X509CertSelector.
+         */
+        private static X500Principal getTargetSubject(List<CertStore> stores,
+                                                      X509CertSelector sel)
+            throws InvalidAlgorithmParameterException
+        {
+            X500Principal subject = sel.getSubject();
+            if (subject != null) {
+                return subject;
+            }
+            X509Certificate cert = sel.getCertificate();
+            if (cert != null) {
+                subject = cert.getSubjectX500Principal();
+            }
+            if (subject != null) {
+                return subject;
+            }
+            for (CertStore store : stores) {
+                try {
+                    Collection<? extends Certificate> certs =
+                        (Collection<? extends Certificate>)
+                            store.getCertificates(sel);
+                    if (!certs.isEmpty()) {
+                        X509Certificate xc =
+                            (X509Certificate)certs.iterator().next();
+                        return xc.getSubjectX500Principal();
+                    }
+                } catch (CertStoreException e) {
+                    // ignore but log it
+                    if (debug != null) {
+                        debug.println("BuilderParams.getTargetSubjectDN: " +
+                            "non-fatal exception retrieving certs: " + e);
+                        e.printStackTrace();
+                    }
+                }
+            }
+            throw new InvalidAlgorithmParameterException
+                ("Could not determine unique target subject");
+        }
+    }
+
+    /**
+     * A CertStoreException with additional information about the type of
+     * CertStore that generated the exception.
+     */
+    static class CertStoreTypeException extends CertStoreException {
+        private static final long serialVersionUID = 7463352639238322556L;
+
+        private final String type;
+
+        CertStoreTypeException(String type, CertStoreException cse) {
+            super(cse.getMessage(), cse.getCause());
+            this.type = type;
+        }
+        String getType() {
+            return type;
+        }
+    }
+
+    /**
+     * Comparator that orders CertStores so that local CertStores come before
+     * remote CertStores.
+     */
+    private static class CertStoreComparator implements Comparator<CertStore> {
+        @Override
+        public int compare(CertStore store1, CertStore store2) {
+            if (store1.getType().equals("Collection") ||
+                store1.getCertStoreParameters() instanceof
+                CollectionCertStoreParameters) {
+                return -1;
+            } else {
+                return 1;
+            }
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java b/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java
new file mode 100644
index 0000000..728bc19
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.*;
+import java.util.*;
+
+import sun.security.provider.certpath.PKIX.ValidatorParams;
+import sun.security.x509.X509CertImpl;
+import sun.security.util.Debug;
+
+/**
+ * This class implements the PKIX validation algorithm for certification
+ * paths consisting exclusively of <code>X509Certificates</code>. It uses
+ * the specified input parameter set (which must be a
+ * <code>PKIXParameters</code> object).
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+public final class PKIXCertPathValidator extends CertPathValidatorSpi {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    /**
+     * Default constructor.
+     */
+    public PKIXCertPathValidator() {}
+
+    @Override
+    public CertPathChecker engineGetRevocationChecker() {
+        return new RevocationChecker();
+    }
+
+    /**
+     * Validates a certification path consisting exclusively of
+     * <code>X509Certificate</code>s using the PKIX validation algorithm,
+     * which uses the specified input parameter set.
+     * The input parameter set must be a <code>PKIXParameters</code> object.
+     *
+     * @param cp the X509 certification path
+     * @param params the input PKIX parameter set
+     * @return the result
+     * @throws CertPathValidatorException if cert path does not validate.
+     * @throws InvalidAlgorithmParameterException if the specified
+     *         parameters are inappropriate for this CertPathValidator
+     */
+    @Override
+    public CertPathValidatorResult engineValidate(CertPath cp,
+                                                  CertPathParameters params)
+        throws CertPathValidatorException, InvalidAlgorithmParameterException
+    {
+        ValidatorParams valParams = PKIX.checkParams(cp, params);
+        return validate(valParams);
+    }
+
+    private static PKIXCertPathValidatorResult validate(ValidatorParams params)
+        throws CertPathValidatorException
+    {
+        if (debug != null)
+            debug.println("PKIXCertPathValidator.engineValidate()...");
+
+        // Retrieve the first certificate in the certpath
+        // (to be used later in pre-screening)
+        AdaptableX509CertSelector selector = null;
+        List<X509Certificate> certList = params.certificates();
+        if (!certList.isEmpty()) {
+            selector = new AdaptableX509CertSelector();
+            X509Certificate firstCert = certList.get(0);
+            // check trusted certificate's subject
+            selector.setSubject(firstCert.getIssuerX500Principal());
+            // check the validity period
+            selector.setValidityPeriod(firstCert.getNotBefore(),
+                                       firstCert.getNotAfter());
+            /*
+             * Facilitate certification path construction with authority
+             * key identifier and subject key identifier.
+             */
+            try {
+                X509CertImpl firstCertImpl = X509CertImpl.toImpl(firstCert);
+                selector.parseAuthorityKeyIdentifierExtension(
+                            firstCertImpl.getAuthorityKeyIdentifierExtension());
+            } catch (CertificateException | IOException e) {
+                // ignore
+            }
+        }
+
+        CertPathValidatorException lastException = null;
+
+        // We iterate through the set of trust anchors until we find
+        // one that works at which time we stop iterating
+        for (TrustAnchor anchor : params.trustAnchors()) {
+            X509Certificate trustedCert = anchor.getTrustedCert();
+            if (trustedCert != null) {
+                // if this trust anchor is not worth trying,
+                // we move on to the next one
+                if (selector != null && !selector.match(trustedCert)) {
+                    if (debug != null) {
+                        debug.println("NO - don't try this trustedCert");
+                    }
+                    continue;
+                }
+
+                if (debug != null) {
+                    debug.println("YES - try this trustedCert");
+                    debug.println("anchor.getTrustedCert()."
+                        + "getSubjectX500Principal() = "
+                        + trustedCert.getSubjectX500Principal());
+                }
+            } else {
+                if (debug != null) {
+                    debug.println("PKIXCertPathValidator.engineValidate(): "
+                        + "anchor.getTrustedCert() == null");
+                }
+            }
+
+            try {
+                return validate(anchor, params);
+            } catch (CertPathValidatorException cpe) {
+                // remember this exception
+                lastException = cpe;
+            }
+        }
+
+        // could not find a trust anchor that verified
+        // (a) if we did a validation and it failed, use that exception
+        if (lastException != null) {
+            throw lastException;
+        }
+        // (b) otherwise, generate new exception
+        throw new CertPathValidatorException
+            ("Path does not chain with any of the trust anchors",
+             null, null, -1, PKIXReason.NO_TRUST_ANCHOR);
+    }
+
+    private static PKIXCertPathValidatorResult validate(TrustAnchor anchor,
+                                                        ValidatorParams params)
+        throws CertPathValidatorException
+    {
+        int certPathLen = params.certificates().size();
+
+        // create PKIXCertPathCheckers
+        List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
+        // add standard checkers that we will be using
+        certPathCheckers.add(new UntrustedChecker());
+        certPathCheckers.add(new AlgorithmChecker(anchor));
+        certPathCheckers.add(new KeyChecker(certPathLen,
+                                            params.targetCertConstraints()));
+        certPathCheckers.add(new ConstraintsChecker(certPathLen));
+        PolicyNodeImpl rootNode =
+            new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false,
+                               Collections.singleton(PolicyChecker.ANY_POLICY),
+                               false);
+        PolicyChecker pc = new PolicyChecker(params.initialPolicies(),
+                                             certPathLen,
+                                             params.explicitPolicyRequired(),
+                                             params.policyMappingInhibited(),
+                                             params.anyPolicyInhibited(),
+                                             params.policyQualifiersRejected(),
+                                             rootNode);
+        certPathCheckers.add(pc);
+        // default value for date is current time
+        BasicChecker bc = new BasicChecker(anchor, params.date(),
+                                           params.sigProvider(), false);
+        certPathCheckers.add(bc);
+
+        boolean revCheckerAdded = false;
+        List<PKIXCertPathChecker> checkers = params.certPathCheckers();
+        for (PKIXCertPathChecker checker : checkers) {
+            if (checker instanceof PKIXRevocationChecker) {
+                if (revCheckerAdded) {
+                    throw new CertPathValidatorException(
+                        "Only one PKIXRevocationChecker can be specified");
+                }
+                revCheckerAdded = true;
+                // if it's our own, initialize it
+                if (checker instanceof RevocationChecker) {
+                    ((RevocationChecker)checker).init(anchor, params);
+                }
+            }
+        }
+        // only add a RevocationChecker if revocation is enabled and
+        // a PKIXRevocationChecker has not already been added
+        if (params.revocationEnabled() && !revCheckerAdded) {
+            certPathCheckers.add(new RevocationChecker(anchor, params));
+        }
+        // add user-specified checkers
+        certPathCheckers.addAll(checkers);
+
+        PKIXMasterCertPathValidator.validate(params.certPath(),
+                                             params.certificates(),
+                                             certPathCheckers);
+
+        return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(),
+                                               bc.getPublicKey());
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PKIXMasterCertPathValidator.java b/ojluni/src/main/java/sun/security/provider/certpath/PKIXMasterCertPathValidator.java
new file mode 100644
index 0000000..db5edda
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PKIXMasterCertPathValidator.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import sun.security.util.Debug;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXReason;
+import java.security.cert.X509Certificate;
+
+/**
+ * This class is initialized with a list of <code>PKIXCertPathChecker</code>s
+ * and is used to verify the certificates in a <code>CertPath</code> by
+ * feeding each certificate to each <code>PKIXCertPathChecker</code>.
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+class PKIXMasterCertPathValidator {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    /**
+     * Validates a certification path consisting exclusively of
+     * <code>X509Certificate</code>s using the specified
+     * <code>PKIXCertPathChecker</code>s. It is assumed that the
+     * <code>PKIXCertPathChecker</code>s
+     * have been initialized with any input parameters they may need.
+     *
+     * @param cpOriginal the original X509 CertPath passed in by the user
+     * @param reversedCertList the reversed X509 CertPath (as a List)
+     * @param certPathCheckers the PKIXCertPathCheckers
+     * @throws CertPathValidatorException if cert path does not validate
+     */
+    static void validate(CertPath cpOriginal,
+                         List<X509Certificate> reversedCertList,
+                         List<PKIXCertPathChecker> certPathCheckers)
+        throws CertPathValidatorException
+    {
+        // we actually process reversedCertList, but we keep cpOriginal because
+        // we need to return the original certPath when we throw an exception.
+        // we will also need to modify the index appropriately when we
+        // throw an exception.
+
+        int cpSize = reversedCertList.size();
+
+        if (debug != null) {
+            debug.println("--------------------------------------------------"
+                  + "------------");
+            debug.println("Executing PKIX certification path validation "
+                  + "algorithm.");
+        }
+
+        for (int i = 0; i < cpSize; i++) {
+
+            /* The basic loop algorithm is that we get the
+             * current certificate, we verify the current certificate using
+             * information from the previous certificate and from the state,
+             * and we modify the state for the next loop by setting the
+             * current certificate of this loop to be the previous certificate
+             * of the next loop. The state is initialized during first loop.
+             */
+            if (debug != null)
+                debug.println("Checking cert" + (i+1) + " ...");
+
+            X509Certificate currCert = reversedCertList.get(i);
+            Set<String> unresCritExts = currCert.getCriticalExtensionOIDs();
+            if (unresCritExts == null) {
+                unresCritExts = Collections.<String>emptySet();
+            }
+
+            if (debug != null && !unresCritExts.isEmpty()) {
+                debug.println("Set of critical extensions:");
+                for (String oid : unresCritExts) {
+                    debug.println(oid);
+                }
+            }
+
+            for (int j = 0; j < certPathCheckers.size(); j++) {
+
+                PKIXCertPathChecker currChecker = certPathCheckers.get(j);
+                if (debug != null) {
+                    debug.println("-Using checker" + (j + 1) + " ... [" +
+                        currChecker.getClass().getName() + "]");
+                }
+
+                if (i == 0)
+                    currChecker.init(false);
+
+                try {
+                    currChecker.check(currCert, unresCritExts);
+
+                    if (debug != null) {
+                        debug.println("-checker" + (j + 1) +
+                            " validation succeeded");
+                    }
+
+                } catch (CertPathValidatorException cpve) {
+                    throw new CertPathValidatorException(cpve.getMessage(),
+                        cpve.getCause(), cpOriginal, cpSize - (i + 1),
+                        cpve.getReason());
+                }
+            }
+
+            if (!unresCritExts.isEmpty()) {
+                throw new CertPathValidatorException("unrecognized " +
+                    "critical extension(s)", null, cpOriginal, cpSize-(i+1),
+                    PKIXReason.UNRECOGNIZED_CRIT_EXT);
+            }
+
+            if (debug != null)
+                debug.println("\ncert" + (i+1) + " validation succeeded.\n");
+        }
+
+        if (debug != null) {
+            debug.println("Cert path validation succeeded. (PKIX validation "
+                          + "algorithm)");
+            debug.println("-------------------------------------------------"
+                          + "-------------");
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PolicyChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/PolicyChecker.java
new file mode 100644
index 0000000..ab82820
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PolicyChecker.java
@@ -0,0 +1,924 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXReason;
+import java.security.cert.PolicyNode;
+import java.security.cert.PolicyQualifierInfo;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+import sun.security.util.Debug;
+import sun.security.x509.CertificatePoliciesExtension;
+import sun.security.x509.PolicyConstraintsExtension;
+import sun.security.x509.PolicyMappingsExtension;
+import sun.security.x509.CertificatePolicyMap;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.x509.PolicyInformation;
+import sun.security.x509.X509CertImpl;
+import sun.security.x509.InhibitAnyPolicyExtension;
+
+/**
+ * PolicyChecker is a <code>PKIXCertPathChecker</code> that checks policy
+ * information on a PKIX certificate, namely certificate policies, policy
+ * mappings, policy constraints and policy qualifiers.
+ *
+ * @since       1.4
+ * @author      Yassir Elley
+ */
+class PolicyChecker extends PKIXCertPathChecker {
+
+    private final Set<String> initPolicies;
+    private final int certPathLen;
+    private final boolean expPolicyRequired;
+    private final boolean polMappingInhibited;
+    private final boolean anyPolicyInhibited;
+    private final boolean rejectPolicyQualifiers;
+    private PolicyNodeImpl rootNode;
+    private int explicitPolicy;
+    private int policyMapping;
+    private int inhibitAnyPolicy;
+    private int certIndex;
+
+    private Set<String> supportedExts;
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    static final String ANY_POLICY = "2.5.29.32.0";
+
+    /**
+     * Constructs a Policy Checker.
+     *
+     * @param initialPolicies Set of initial policies
+     * @param certPathLen length of the certification path to be checked
+     * @param expPolicyRequired true if explicit policy is required
+     * @param polMappingInhibited true if policy mapping is inhibited
+     * @param anyPolicyInhibited true if the ANY_POLICY OID should be inhibited
+     * @param rejectPolicyQualifiers true if pol qualifiers are to be rejected
+     * @param rootNode the initial root node of the valid policy tree
+     */
+    PolicyChecker(Set<String> initialPolicies, int certPathLen,
+        boolean expPolicyRequired, boolean polMappingInhibited,
+        boolean anyPolicyInhibited, boolean rejectPolicyQualifiers,
+        PolicyNodeImpl rootNode)
+    {
+        if (initialPolicies.isEmpty()) {
+            // if no initialPolicies are specified by user, set
+            // initPolicies to be anyPolicy by default
+            this.initPolicies = new HashSet<String>(1);
+            this.initPolicies.add(ANY_POLICY);
+        } else {
+            this.initPolicies = new HashSet<String>(initialPolicies);
+        }
+        this.certPathLen = certPathLen;
+        this.expPolicyRequired = expPolicyRequired;
+        this.polMappingInhibited = polMappingInhibited;
+        this.anyPolicyInhibited = anyPolicyInhibited;
+        this.rejectPolicyQualifiers = rejectPolicyQualifiers;
+        this.rootNode = rootNode;
+    }
+
+    /**
+     * Initializes the internal state of the checker from parameters
+     * specified in the constructor
+     *
+     * @param forward a boolean indicating whether this checker should be
+     *        initialized capable of building in the forward direction
+     * @throws CertPathValidatorException if user wants to enable forward
+     *         checking and forward checking is not supported.
+     */
+    @Override
+    public void init(boolean forward) throws CertPathValidatorException {
+        if (forward) {
+            throw new CertPathValidatorException
+                                        ("forward checking not supported");
+        }
+
+        certIndex = 1;
+        explicitPolicy = (expPolicyRequired ? 0 : certPathLen + 1);
+        policyMapping = (polMappingInhibited ? 0 : certPathLen + 1);
+        inhibitAnyPolicy = (anyPolicyInhibited ? 0 : certPathLen + 1);
+    }
+
+    /**
+     * Checks if forward checking is supported. Forward checking refers
+     * to the ability of the PKIXCertPathChecker to perform its checks
+     * when presented with certificates in the forward direction (from
+     * target to anchor).
+     *
+     * @return true if forward checking is supported, false otherwise
+     */
+    @Override
+    public boolean isForwardCheckingSupported() {
+        return false;
+    }
+
+    /**
+     * Gets an immutable Set of the OID strings for the extensions that
+     * the PKIXCertPathChecker supports (i.e. recognizes, is able to
+     * process), or null if no extensions are
+     * supported. All OID strings that a PKIXCertPathChecker might
+     * possibly be able to process should be included.
+     *
+     * @return the Set of extensions supported by this PKIXCertPathChecker,
+     * or null if no extensions are supported
+     */
+    @Override
+    public Set<String> getSupportedExtensions() {
+        if (supportedExts == null) {
+            supportedExts = new HashSet<String>(4);
+            supportedExts.add(CertificatePolicies_Id.toString());
+            supportedExts.add(PolicyMappings_Id.toString());
+            supportedExts.add(PolicyConstraints_Id.toString());
+            supportedExts.add(InhibitAnyPolicy_Id.toString());
+            supportedExts = Collections.unmodifiableSet(supportedExts);
+        }
+        return supportedExts;
+    }
+
+    /**
+     * Performs the policy processing checks on the certificate using its
+     * internal state.
+     *
+     * @param cert the Certificate to be processed
+     * @param unresCritExts the unresolved critical extensions
+     * @throws CertPathValidatorException if the certificate does not verify
+     */
+    @Override
+    public void check(Certificate cert, Collection<String> unresCritExts)
+        throws CertPathValidatorException
+    {
+        // now do the policy checks
+        checkPolicy((X509Certificate) cert);
+
+        if (unresCritExts != null && !unresCritExts.isEmpty()) {
+            unresCritExts.remove(CertificatePolicies_Id.toString());
+            unresCritExts.remove(PolicyMappings_Id.toString());
+            unresCritExts.remove(PolicyConstraints_Id.toString());
+            unresCritExts.remove(InhibitAnyPolicy_Id.toString());
+        }
+    }
+
+    /**
+     * Internal method to run through all the checks.
+     *
+     * @param currCert the certificate to be processed
+     * @exception CertPathValidatorException Exception thrown if
+     * the certificate does not verify
+     */
+    private void checkPolicy(X509Certificate currCert)
+        throws CertPathValidatorException
+    {
+        String msg = "certificate policies";
+        if (debug != null) {
+            debug.println("PolicyChecker.checkPolicy() ---checking " + msg
+                + "...");
+            debug.println("PolicyChecker.checkPolicy() certIndex = "
+                + certIndex);
+            debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+                + "explicitPolicy = " + explicitPolicy);
+            debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+                + "policyMapping = " + policyMapping);
+            debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+                + "inhibitAnyPolicy = " + inhibitAnyPolicy);
+            debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+                + "policyTree = " + rootNode);
+        }
+
+        X509CertImpl currCertImpl = null;
+        try {
+            currCertImpl = X509CertImpl.toImpl(currCert);
+        } catch (CertificateException ce) {
+            throw new CertPathValidatorException(ce);
+        }
+
+        boolean finalCert = (certIndex == certPathLen);
+
+        rootNode = processPolicies(certIndex, initPolicies, explicitPolicy,
+            policyMapping, inhibitAnyPolicy, rejectPolicyQualifiers, rootNode,
+            currCertImpl, finalCert);
+
+        if (!finalCert) {
+            explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCertImpl,
+                                                 finalCert);
+            policyMapping = mergePolicyMapping(policyMapping, currCertImpl);
+            inhibitAnyPolicy = mergeInhibitAnyPolicy(inhibitAnyPolicy,
+                                                     currCertImpl);
+        }
+
+        certIndex++;
+
+        if (debug != null) {
+            debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+                + "explicitPolicy = " + explicitPolicy);
+            debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+                + "policyMapping = " + policyMapping);
+            debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+                + "inhibitAnyPolicy = " + inhibitAnyPolicy);
+            debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+                + "policyTree = " + rootNode);
+            debug.println("PolicyChecker.checkPolicy() " + msg + " verified");
+        }
+    }
+
+    /**
+     * Merges the specified explicitPolicy value with the
+     * requireExplicitPolicy field of the <code>PolicyConstraints</code>
+     * extension obtained from the certificate. An explicitPolicy
+     * value of -1 implies no constraint.
+     *
+     * @param explicitPolicy an integer which indicates if a non-null
+     * valid policy tree is required
+     * @param currCert the Certificate to be processed
+     * @param finalCert a boolean indicating whether currCert is
+     * the final cert in the cert path
+     * @return returns the new explicitPolicy value
+     * @exception CertPathValidatorException Exception thrown if an error
+     * occurs
+     */
+    static int mergeExplicitPolicy(int explicitPolicy, X509CertImpl currCert,
+        boolean finalCert) throws CertPathValidatorException
+    {
+        if ((explicitPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
+            explicitPolicy--;
+        }
+
+        try {
+            PolicyConstraintsExtension polConstExt
+                = currCert.getPolicyConstraintsExtension();
+            if (polConstExt == null)
+                return explicitPolicy;
+            int require =
+                polConstExt.get(PolicyConstraintsExtension.REQUIRE).intValue();
+            if (debug != null) {
+                debug.println("PolicyChecker.mergeExplicitPolicy() "
+                   + "require Index from cert = " + require);
+            }
+            if (!finalCert) {
+                if (require != -1) {
+                    if ((explicitPolicy == -1) || (require < explicitPolicy)) {
+                        explicitPolicy = require;
+                    }
+                }
+            } else {
+                if (require == 0)
+                    explicitPolicy = require;
+            }
+        } catch (IOException e) {
+            if (debug != null) {
+                debug.println("PolicyChecker.mergeExplicitPolicy "
+                              + "unexpected exception");
+                e.printStackTrace();
+            }
+            throw new CertPathValidatorException(e);
+        }
+
+        return explicitPolicy;
+    }
+
+    /**
+     * Merges the specified policyMapping value with the
+     * inhibitPolicyMapping field of the <code>PolicyConstraints</code>
+     * extension obtained from the certificate. A policyMapping
+     * value of -1 implies no constraint.
+     *
+     * @param policyMapping an integer which indicates if policy mapping
+     * is inhibited
+     * @param currCert the Certificate to be processed
+     * @return returns the new policyMapping value
+     * @exception CertPathValidatorException Exception thrown if an error
+     * occurs
+     */
+    static int mergePolicyMapping(int policyMapping, X509CertImpl currCert)
+        throws CertPathValidatorException
+    {
+        if ((policyMapping > 0) && !X509CertImpl.isSelfIssued(currCert)) {
+            policyMapping--;
+        }
+
+        try {
+            PolicyConstraintsExtension polConstExt
+                = currCert.getPolicyConstraintsExtension();
+            if (polConstExt == null)
+                return policyMapping;
+
+            int inhibit =
+                polConstExt.get(PolicyConstraintsExtension.INHIBIT).intValue();
+            if (debug != null)
+                debug.println("PolicyChecker.mergePolicyMapping() "
+                    + "inhibit Index from cert = " + inhibit);
+
+            if (inhibit != -1) {
+                if ((policyMapping == -1) || (inhibit < policyMapping)) {
+                    policyMapping = inhibit;
+                }
+            }
+        } catch (IOException e) {
+            if (debug != null) {
+                debug.println("PolicyChecker.mergePolicyMapping "
+                              + "unexpected exception");
+                e.printStackTrace();
+            }
+            throw new CertPathValidatorException(e);
+        }
+
+        return policyMapping;
+    }
+
+    /**
+     * Merges the specified inhibitAnyPolicy value with the
+     * SkipCerts value of the InhibitAnyPolicy
+     * extension obtained from the certificate.
+     *
+     * @param inhibitAnyPolicy an integer which indicates whether
+     * "any-policy" is considered a match
+     * @param currCert the Certificate to be processed
+     * @return returns the new inhibitAnyPolicy value
+     * @exception CertPathValidatorException Exception thrown if an error
+     * occurs
+     */
+    static int mergeInhibitAnyPolicy(int inhibitAnyPolicy,
+        X509CertImpl currCert) throws CertPathValidatorException
+    {
+        if ((inhibitAnyPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
+            inhibitAnyPolicy--;
+        }
+
+        try {
+            InhibitAnyPolicyExtension inhAnyPolExt = (InhibitAnyPolicyExtension)
+                currCert.getExtension(InhibitAnyPolicy_Id);
+            if (inhAnyPolExt == null)
+                return inhibitAnyPolicy;
+
+            int skipCerts =
+                inhAnyPolExt.get(InhibitAnyPolicyExtension.SKIP_CERTS).intValue();
+            if (debug != null)
+                debug.println("PolicyChecker.mergeInhibitAnyPolicy() "
+                    + "skipCerts Index from cert = " + skipCerts);
+
+            if (skipCerts != -1) {
+                if (skipCerts < inhibitAnyPolicy) {
+                    inhibitAnyPolicy = skipCerts;
+                }
+            }
+        } catch (IOException e) {
+            if (debug != null) {
+                debug.println("PolicyChecker.mergeInhibitAnyPolicy "
+                              + "unexpected exception");
+                e.printStackTrace();
+            }
+            throw new CertPathValidatorException(e);
+        }
+
+        return inhibitAnyPolicy;
+    }
+
+    /**
+     * Processes certificate policies in the certificate.
+     *
+     * @param certIndex the index of the certificate
+     * @param initPolicies the initial policies required by the user
+     * @param explicitPolicy an integer which indicates if a non-null
+     * valid policy tree is required
+     * @param policyMapping an integer which indicates if policy
+     * mapping is inhibited
+     * @param inhibitAnyPolicy an integer which indicates whether
+     * "any-policy" is considered a match
+     * @param rejectPolicyQualifiers a boolean indicating whether the
+     * user wants to reject policies that have qualifiers
+     * @param origRootNode the root node of the valid policy tree
+     * @param currCert the Certificate to be processed
+     * @param finalCert a boolean indicating whether currCert is the final
+     * cert in the cert path
+     * @return the root node of the valid policy tree after modification
+     * @exception CertPathValidatorException Exception thrown if an
+     * error occurs while processing policies.
+     */
+    static PolicyNodeImpl processPolicies(int certIndex, Set<String> initPolicies,
+        int explicitPolicy, int policyMapping, int inhibitAnyPolicy,
+        boolean rejectPolicyQualifiers, PolicyNodeImpl origRootNode,
+        X509CertImpl currCert, boolean finalCert)
+        throws CertPathValidatorException
+    {
+        boolean policiesCritical = false;
+        List<PolicyInformation> policyInfo;
+        PolicyNodeImpl rootNode = null;
+        Set<PolicyQualifierInfo> anyQuals = new HashSet<>();
+
+        if (origRootNode == null)
+            rootNode = null;
+        else
+            rootNode = origRootNode.copyTree();
+
+        // retrieve policyOIDs from currCert
+        CertificatePoliciesExtension currCertPolicies
+            = currCert.getCertificatePoliciesExtension();
+
+        // PKIX: Section 6.1.3: Step (d)
+        if ((currCertPolicies != null) && (rootNode != null)) {
+            policiesCritical = currCertPolicies.isCritical();
+            if (debug != null)
+                debug.println("PolicyChecker.processPolicies() "
+                    + "policiesCritical = " + policiesCritical);
+
+            try {
+                policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
+            } catch (IOException ioe) {
+                throw new CertPathValidatorException("Exception while "
+                    + "retrieving policyOIDs", ioe);
+            }
+
+            if (debug != null)
+                debug.println("PolicyChecker.processPolicies() "
+                    + "rejectPolicyQualifiers = " + rejectPolicyQualifiers);
+
+            boolean foundAnyPolicy = false;
+
+            // process each policy in cert
+            for (PolicyInformation curPolInfo : policyInfo) {
+                String curPolicy =
+                    curPolInfo.getPolicyIdentifier().getIdentifier().toString();
+
+                if (curPolicy.equals(ANY_POLICY)) {
+                    foundAnyPolicy = true;
+                    anyQuals = curPolInfo.getPolicyQualifiers();
+                } else {
+                    // PKIX: Section 6.1.3: Step (d)(1)
+                    if (debug != null)
+                        debug.println("PolicyChecker.processPolicies() "
+                                      + "processing policy: " + curPolicy);
+
+                    // retrieve policy qualifiers from cert
+                    Set<PolicyQualifierInfo> pQuals =
+                                        curPolInfo.getPolicyQualifiers();
+
+                    // reject cert if we find critical policy qualifiers and
+                    // the policyQualifiersRejected flag is set in the params
+                    if (!pQuals.isEmpty() && rejectPolicyQualifiers &&
+                        policiesCritical) {
+                        throw new CertPathValidatorException(
+                            "critical policy qualifiers present in certificate",
+                            null, null, -1, PKIXReason.INVALID_POLICY);
+                    }
+
+                    // PKIX: Section 6.1.3: Step (d)(1)(i)
+                    boolean foundMatch = processParents(certIndex,
+                        policiesCritical, rejectPolicyQualifiers, rootNode,
+                        curPolicy, pQuals, false);
+
+                    if (!foundMatch) {
+                        // PKIX: Section 6.1.3: Step (d)(1)(ii)
+                        processParents(certIndex, policiesCritical,
+                            rejectPolicyQualifiers, rootNode, curPolicy,
+                            pQuals, true);
+                    }
+                }
+            }
+
+            // PKIX: Section 6.1.3: Step (d)(2)
+            if (foundAnyPolicy) {
+                if ((inhibitAnyPolicy > 0) ||
+                        (!finalCert && X509CertImpl.isSelfIssued(currCert))) {
+                    if (debug != null) {
+                        debug.println("PolicyChecker.processPolicies() "
+                            + "processing policy: " + ANY_POLICY);
+                    }
+                    processParents(certIndex, policiesCritical,
+                        rejectPolicyQualifiers, rootNode, ANY_POLICY, anyQuals,
+                        true);
+                }
+            }
+
+            // PKIX: Section 6.1.3: Step (d)(3)
+            rootNode.prune(certIndex);
+            if (!rootNode.getChildren().hasNext()) {
+                rootNode = null;
+            }
+        } else if (currCertPolicies == null) {
+            if (debug != null)
+                debug.println("PolicyChecker.processPolicies() "
+                              + "no policies present in cert");
+            // PKIX: Section 6.1.3: Step (e)
+            rootNode = null;
+        }
+
+        // We delay PKIX: Section 6.1.3: Step (f) to the end
+        // because the code that follows may delete some nodes
+        // resulting in a null tree
+        if (rootNode != null) {
+            if (!finalCert) {
+                // PKIX: Section 6.1.4: Steps (a)-(b)
+                rootNode = processPolicyMappings(currCert, certIndex,
+                    policyMapping, rootNode, policiesCritical, anyQuals);
+            }
+        }
+
+        // At this point, we optimize the PKIX algorithm by
+        // removing those nodes which would later have
+        // been removed by PKIX: Section 6.1.5: Step (g)(iii)
+
+        if ((rootNode != null) && (!initPolicies.contains(ANY_POLICY))
+            && (currCertPolicies != null)) {
+            rootNode = removeInvalidNodes(rootNode, certIndex,
+                                          initPolicies, currCertPolicies);
+
+            // PKIX: Section 6.1.5: Step (g)(iii)
+            if ((rootNode != null) && finalCert) {
+                // rewrite anyPolicy leaf nodes (see method comments)
+                rootNode = rewriteLeafNodes(certIndex, initPolicies, rootNode);
+            }
+        }
+
+
+        if (finalCert) {
+            // PKIX: Section 6.1.5: Steps (a) and (b)
+            explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCert,
+                                             finalCert);
+        }
+
+        // PKIX: Section 6.1.3: Step (f)
+        // verify that either explicit policy is greater than 0 or
+        // the valid_policy_tree is not equal to NULL
+
+        if ((explicitPolicy == 0) && (rootNode == null)) {
+            throw new CertPathValidatorException
+                ("non-null policy tree required and policy tree is null",
+                 null, null, -1, PKIXReason.INVALID_POLICY);
+        }
+
+        return rootNode;
+    }
+
+    /**
+     * Rewrite leaf nodes at the end of validation as described in RFC 3280
+     * section 6.1.5: Step (g)(iii). Leaf nodes with anyPolicy are replaced
+     * by nodes explicitly representing initial policies not already
+     * represented by leaf nodes.
+     *
+     * This method should only be called when processing the final cert
+     * and if the policy tree is not null and initial policies is not
+     * anyPolicy.
+     *
+     * @param certIndex the depth of the tree
+     * @param initPolicies Set of user specified initial policies
+     * @param rootNode the root of the policy tree
+     */
+    private static PolicyNodeImpl rewriteLeafNodes(int certIndex,
+            Set<String> initPolicies, PolicyNodeImpl rootNode) {
+        Set<PolicyNodeImpl> anyNodes =
+                        rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
+        if (anyNodes.isEmpty()) {
+            return rootNode;
+        }
+        PolicyNodeImpl anyNode = anyNodes.iterator().next();
+        PolicyNodeImpl parentNode = (PolicyNodeImpl)anyNode.getParent();
+        parentNode.deleteChild(anyNode);
+        // see if there are any initialPolicies not represented by leaf nodes
+        Set<String> initial = new HashSet<>(initPolicies);
+        for (PolicyNodeImpl node : rootNode.getPolicyNodes(certIndex)) {
+            initial.remove(node.getValidPolicy());
+        }
+        if (initial.isEmpty()) {
+            // we deleted the anyPolicy node and have nothing to re-add,
+            // so we need to prune the tree
+            rootNode.prune(certIndex);
+            if (rootNode.getChildren().hasNext() == false) {
+                rootNode = null;
+            }
+        } else {
+            boolean anyCritical = anyNode.isCritical();
+            Set<PolicyQualifierInfo> anyQualifiers =
+                                                anyNode.getPolicyQualifiers();
+            for (String policy : initial) {
+                Set<String> expectedPolicies = Collections.singleton(policy);
+                PolicyNodeImpl node = new PolicyNodeImpl(parentNode, policy,
+                    anyQualifiers, anyCritical, expectedPolicies, false);
+            }
+        }
+        return rootNode;
+    }
+
+    /**
+     * Finds the policy nodes of depth (certIndex-1) where curPolicy
+     * is in the expected policy set and creates a new child node
+     * appropriately. If matchAny is true, then a value of ANY_POLICY
+     * in the expected policy set will match any curPolicy. If matchAny
+     * is false, then the expected policy set must exactly contain the
+     * curPolicy to be considered a match. This method returns a boolean
+     * value indicating whether a match was found.
+     *
+     * @param certIndex the index of the certificate whose policy is
+     * being processed
+     * @param policiesCritical a boolean indicating whether the certificate
+     * policies extension is critical
+     * @param rejectPolicyQualifiers a boolean indicating whether the
+     * user wants to reject policies that have qualifiers
+     * @param rootNode the root node of the valid policy tree
+     * @param curPolicy a String representing the policy being processed
+     * @param pQuals the policy qualifiers of the policy being processed or an
+     * empty Set if there are no qualifiers
+     * @param matchAny a boolean indicating whether a value of ANY_POLICY
+     * in the expected policy set will be considered a match
+     * @return a boolean indicating whether a match was found
+     * @exception CertPathValidatorException Exception thrown if error occurs.
+     */
+    private static boolean processParents(int certIndex,
+        boolean policiesCritical, boolean rejectPolicyQualifiers,
+        PolicyNodeImpl rootNode, String curPolicy,
+        Set<PolicyQualifierInfo> pQuals,
+        boolean matchAny) throws CertPathValidatorException
+    {
+        boolean foundMatch = false;
+
+        if (debug != null)
+            debug.println("PolicyChecker.processParents(): matchAny = "
+                + matchAny);
+
+        // find matching parents
+        Set<PolicyNodeImpl> parentNodes =
+                rootNode.getPolicyNodesExpected(certIndex - 1,
+                                                curPolicy, matchAny);
+
+        // for each matching parent, extend policy tree
+        for (PolicyNodeImpl curParent : parentNodes) {
+            if (debug != null)
+                debug.println("PolicyChecker.processParents() "
+                              + "found parent:\n" + curParent.asString());
+
+            foundMatch = true;
+            String curParPolicy = curParent.getValidPolicy();
+
+            PolicyNodeImpl curNode = null;
+            Set<String> curExpPols = null;
+
+            if (curPolicy.equals(ANY_POLICY)) {
+                // do step 2
+                Set<String> parExpPols = curParent.getExpectedPolicies();
+            parentExplicitPolicies:
+                for (String curParExpPol : parExpPols) {
+
+                    Iterator<PolicyNodeImpl> childIter =
+                                        curParent.getChildren();
+                    while (childIter.hasNext()) {
+                        PolicyNodeImpl childNode = childIter.next();
+                        String childPolicy = childNode.getValidPolicy();
+                        if (curParExpPol.equals(childPolicy)) {
+                            if (debug != null)
+                                debug.println(childPolicy + " in parent's "
+                                    + "expected policy set already appears in "
+                                    + "child node");
+                            continue parentExplicitPolicies;
+                        }
+                    }
+
+                    Set<String> expPols = new HashSet<>();
+                    expPols.add(curParExpPol);
+
+                    curNode = new PolicyNodeImpl
+                        (curParent, curParExpPol, pQuals,
+                         policiesCritical, expPols, false);
+                }
+            } else {
+                curExpPols = new HashSet<String>();
+                curExpPols.add(curPolicy);
+
+                curNode = new PolicyNodeImpl
+                    (curParent, curPolicy, pQuals,
+                     policiesCritical, curExpPols, false);
+            }
+        }
+
+        return foundMatch;
+    }
+
+    /**
+     * Processes policy mappings in the certificate.
+     *
+     * @param currCert the Certificate to be processed
+     * @param certIndex the index of the current certificate
+     * @param policyMapping an integer which indicates if policy
+     * mapping is inhibited
+     * @param rootNode the root node of the valid policy tree
+     * @param policiesCritical a boolean indicating if the certificate policies
+     * extension is critical
+     * @param anyQuals the qualifiers associated with ANY-POLICY, or an empty
+     * Set if there are no qualifiers associated with ANY-POLICY
+     * @return the root node of the valid policy tree after modification
+     * @exception CertPathValidatorException exception thrown if an error
+     * occurs while processing policy mappings
+     */
+    private static PolicyNodeImpl processPolicyMappings(X509CertImpl currCert,
+        int certIndex, int policyMapping, PolicyNodeImpl rootNode,
+        boolean policiesCritical, Set<PolicyQualifierInfo> anyQuals)
+        throws CertPathValidatorException
+    {
+        PolicyMappingsExtension polMappingsExt
+            = currCert.getPolicyMappingsExtension();
+
+        if (polMappingsExt == null)
+            return rootNode;
+
+        if (debug != null)
+            debug.println("PolicyChecker.processPolicyMappings() "
+                + "inside policyMapping check");
+
+        List<CertificatePolicyMap> maps = null;
+        try {
+            maps = polMappingsExt.get(PolicyMappingsExtension.MAP);
+        } catch (IOException e) {
+            if (debug != null) {
+                debug.println("PolicyChecker.processPolicyMappings() "
+                    + "mapping exception");
+                e.printStackTrace();
+            }
+            throw new CertPathValidatorException("Exception while checking "
+                                                 + "mapping", e);
+        }
+
+        boolean childDeleted = false;
+        for (CertificatePolicyMap polMap : maps) {
+            String issuerDomain
+                = polMap.getIssuerIdentifier().getIdentifier().toString();
+            String subjectDomain
+                = polMap.getSubjectIdentifier().getIdentifier().toString();
+            if (debug != null) {
+                debug.println("PolicyChecker.processPolicyMappings() "
+                              + "issuerDomain = " + issuerDomain);
+                debug.println("PolicyChecker.processPolicyMappings() "
+                              + "subjectDomain = " + subjectDomain);
+            }
+
+            if (issuerDomain.equals(ANY_POLICY)) {
+                throw new CertPathValidatorException
+                    ("encountered an issuerDomainPolicy of ANY_POLICY",
+                     null, null, -1, PKIXReason.INVALID_POLICY);
+            }
+
+            if (subjectDomain.equals(ANY_POLICY)) {
+                throw new CertPathValidatorException
+                    ("encountered a subjectDomainPolicy of ANY_POLICY",
+                     null, null, -1, PKIXReason.INVALID_POLICY);
+            }
+
+            Set<PolicyNodeImpl> validNodes =
+                rootNode.getPolicyNodesValid(certIndex, issuerDomain);
+            if (!validNodes.isEmpty()) {
+                for (PolicyNodeImpl curNode : validNodes) {
+                    if ((policyMapping > 0) || (policyMapping == -1)) {
+                        curNode.addExpectedPolicy(subjectDomain);
+                    } else if (policyMapping == 0) {
+                        PolicyNodeImpl parentNode =
+                            (PolicyNodeImpl) curNode.getParent();
+                        if (debug != null)
+                            debug.println("PolicyChecker.processPolicyMappings"
+                                + "() before deleting: policy tree = "
+                                + rootNode);
+                        parentNode.deleteChild(curNode);
+                        childDeleted = true;
+                        if (debug != null)
+                            debug.println("PolicyChecker.processPolicyMappings"
+                                + "() after deleting: policy tree = "
+                                + rootNode);
+                    }
+                }
+            } else { // no node of depth i has a valid policy
+                if ((policyMapping > 0) || (policyMapping == -1)) {
+                    Set<PolicyNodeImpl> validAnyNodes =
+                        rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
+                    for (PolicyNodeImpl curAnyNode : validAnyNodes) {
+                        PolicyNodeImpl curAnyNodeParent =
+                            (PolicyNodeImpl) curAnyNode.getParent();
+
+                        Set<String> expPols = new HashSet<>();
+                        expPols.add(subjectDomain);
+
+                        PolicyNodeImpl curNode = new PolicyNodeImpl
+                            (curAnyNodeParent, issuerDomain, anyQuals,
+                             policiesCritical, expPols, true);
+                    }
+                }
+            }
+        }
+
+        if (childDeleted) {
+            rootNode.prune(certIndex);
+            if (!rootNode.getChildren().hasNext()) {
+                if (debug != null)
+                    debug.println("setting rootNode to null");
+                rootNode = null;
+            }
+        }
+
+        return rootNode;
+    }
+
+    /**
+     * Removes those nodes which do not intersect with the initial policies
+     * specified by the user.
+     *
+     * @param rootNode the root node of the valid policy tree
+     * @param certIndex the index of the certificate being processed
+     * @param initPolicies the Set of policies required by the user
+     * @param currCertPolicies the CertificatePoliciesExtension of the
+     * certificate being processed
+     * @returns the root node of the valid policy tree after modification
+     * @exception CertPathValidatorException Exception thrown if error occurs.
+     */
+    private static PolicyNodeImpl removeInvalidNodes(PolicyNodeImpl rootNode,
+        int certIndex, Set<String> initPolicies,
+        CertificatePoliciesExtension currCertPolicies)
+        throws CertPathValidatorException
+    {
+        List<PolicyInformation> policyInfo = null;
+        try {
+            policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
+        } catch (IOException ioe) {
+            throw new CertPathValidatorException("Exception while "
+                + "retrieving policyOIDs", ioe);
+        }
+
+        boolean childDeleted = false;
+        for (PolicyInformation curPolInfo : policyInfo) {
+            String curPolicy =
+                curPolInfo.getPolicyIdentifier().getIdentifier().toString();
+
+            if (debug != null)
+                debug.println("PolicyChecker.processPolicies() "
+                              + "processing policy second time: " + curPolicy);
+
+            Set<PolicyNodeImpl> validNodes =
+                        rootNode.getPolicyNodesValid(certIndex, curPolicy);
+            for (PolicyNodeImpl curNode : validNodes) {
+                PolicyNodeImpl parentNode = (PolicyNodeImpl)curNode.getParent();
+                if (parentNode.getValidPolicy().equals(ANY_POLICY)) {
+                    if ((!initPolicies.contains(curPolicy)) &&
+                        (!curPolicy.equals(ANY_POLICY))) {
+                        if (debug != null)
+                            debug.println("PolicyChecker.processPolicies() "
+                                + "before deleting: policy tree = " + rootNode);
+                        parentNode.deleteChild(curNode);
+                        childDeleted = true;
+                        if (debug != null)
+                            debug.println("PolicyChecker.processPolicies() "
+                                + "after deleting: policy tree = " + rootNode);
+                    }
+                }
+            }
+        }
+
+        if (childDeleted) {
+            rootNode.prune(certIndex);
+            if (!rootNode.getChildren().hasNext()) {
+                rootNode = null;
+            }
+        }
+
+        return rootNode;
+    }
+
+    /**
+     * Gets the root node of the valid policy tree, or null if the
+     * valid policy tree is null. Marks each node of the returned tree
+     * immutable and thread-safe.
+     *
+     * @returns the root node of the valid policy tree, or null if
+     * the valid policy tree is null
+     */
+    PolicyNode getPolicyTree() {
+        if (rootNode == null)
+            return null;
+        else {
+            PolicyNodeImpl policyTree = rootNode.copyTree();
+            policyTree.setImmutable();
+            return policyTree;
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/PolicyNodeImpl.java b/ojluni/src/main/java/sun/security/provider/certpath/PolicyNodeImpl.java
new file mode 100644
index 0000000..02109d4
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/PolicyNodeImpl.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import java.security.cert.*;
+
+/**
+ * Implements the <code>PolicyNode</code> interface.
+ * <p>
+ * This class provides an implementation of the <code>PolicyNode</code>
+ * interface, and is used internally to build and search Policy Trees.
+ * While the implementation is mutable during construction, it is immutable
+ * before returning to a client and no mutable public or protected methods
+ * are exposed by this implementation, as per the contract of PolicyNode.
+ *
+ * @since       1.4
+ * @author      Seth Proctor
+ * @author      Sean Mullan
+ */
+final class PolicyNodeImpl implements PolicyNode {
+
+    /**
+     * Use to specify the special policy "Any Policy"
+     */
+    private static final String ANY_POLICY = "2.5.29.32.0";
+
+    // every node has one parent, and zero or more children
+    private PolicyNodeImpl mParent;
+    private HashSet<PolicyNodeImpl> mChildren;
+
+    // the 4 fields specified by RFC 3280
+    private String mValidPolicy;
+    private HashSet<PolicyQualifierInfo> mQualifierSet;
+    private boolean mCriticalityIndicator;
+    private HashSet<String> mExpectedPolicySet;
+    private boolean mOriginalExpectedPolicySet;
+
+    // the tree depth
+    private int mDepth;
+    // immutability flag
+    private boolean isImmutable = false;
+
+    /**
+     * Constructor which takes a <code>PolicyNodeImpl</code> representing the
+     * parent in the Policy Tree to this node. If null, this is the
+     * root of the tree. The constructor also takes the associated data
+     * for this node, as found in the certificate. It also takes a boolean
+     * argument specifying whether this node is being created as a result
+     * of policy mapping.
+     *
+     * @param parent the PolicyNode above this in the tree, or null if this
+     *               node is the tree's root node
+     * @param validPolicy a String representing this node's valid policy OID
+     * @param qualifierSet the Set of qualifiers for this policy
+     * @param criticalityIndicator a boolean representing whether or not the
+     *                             extension is critical
+     * @param expectedPolicySet a Set of expected policies
+     * @param generatedByPolicyMapping a boolean indicating whether this
+     * node was generated by a policy mapping
+     */
+    PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy,
+                Set<PolicyQualifierInfo> qualifierSet,
+                boolean criticalityIndicator, Set<String> expectedPolicySet,
+                boolean generatedByPolicyMapping) {
+        mParent = parent;
+        mChildren = new HashSet<PolicyNodeImpl>();
+
+        if (validPolicy != null)
+            mValidPolicy = validPolicy;
+        else
+            mValidPolicy = "";
+
+        if (qualifierSet != null)
+            mQualifierSet = new HashSet<PolicyQualifierInfo>(qualifierSet);
+        else
+            mQualifierSet = new HashSet<PolicyQualifierInfo>();
+
+        mCriticalityIndicator = criticalityIndicator;
+
+        if (expectedPolicySet != null)
+            mExpectedPolicySet = new HashSet<String>(expectedPolicySet);
+        else
+            mExpectedPolicySet = new HashSet<String>();
+
+        mOriginalExpectedPolicySet = !generatedByPolicyMapping;
+
+        // see if we're the root, and act appropriately
+        if (mParent != null) {
+            mDepth = mParent.getDepth() + 1;
+            mParent.addChild(this);
+        } else {
+            mDepth = 0;
+        }
+    }
+
+    /**
+     * Alternate constructor which makes a new node with the policy data
+     * in an existing <code>PolicyNodeImpl</code>.
+     *
+     * @param parent a PolicyNode that's the new parent of the node, or
+     *               null if this is the root node
+     * @param node a PolicyNode containing the policy data to copy
+     */
+    PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) {
+        this(parent, node.mValidPolicy, node.mQualifierSet,
+             node.mCriticalityIndicator, node.mExpectedPolicySet, false);
+    }
+
+    @Override
+    public PolicyNode getParent() {
+        return mParent;
+    }
+
+    @Override
+    public Iterator<PolicyNodeImpl> getChildren() {
+        return Collections.unmodifiableSet(mChildren).iterator();
+    }
+
+    @Override
+    public int getDepth() {
+        return mDepth;
+    }
+
+    @Override
+    public String getValidPolicy() {
+        return mValidPolicy;
+    }
+
+    @Override
+    public Set<PolicyQualifierInfo> getPolicyQualifiers() {
+        return Collections.unmodifiableSet(mQualifierSet);
+    }
+
+    @Override
+    public Set<String> getExpectedPolicies() {
+        return Collections.unmodifiableSet(mExpectedPolicySet);
+    }
+
+    @Override
+    public boolean isCritical() {
+        return mCriticalityIndicator;
+    }
+
+    /**
+     * Return a printable representation of the PolicyNode.
+     * Starting at the node on which this method is called,
+     * it recurses through the tree and prints out each node.
+     *
+     * @return a String describing the contents of the Policy Node
+     */
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder(this.asString());
+
+        for (PolicyNodeImpl node : mChildren) {
+            buffer.append(node);
+        }
+        return buffer.toString();
+    }
+
+    // private methods and package private operations
+
+    boolean isImmutable() {
+        return isImmutable;
+    }
+
+    /**
+     * Sets the immutability flag of this node and all of its children
+     * to true.
+     */
+    void setImmutable() {
+        if (isImmutable)
+            return;
+        for (PolicyNodeImpl node : mChildren) {
+            node.setImmutable();
+        }
+        isImmutable = true;
+    }
+
+    /**
+     * Private method sets a child node. This is called from the child's
+     * constructor.
+     *
+     * @param child new <code>PolicyNodeImpl</code> child node
+     */
+    private void addChild(PolicyNodeImpl child) {
+        if (isImmutable) {
+            throw new IllegalStateException("PolicyNode is immutable");
+        }
+        mChildren.add(child);
+    }
+
+    /**
+     * Adds an expectedPolicy to the expected policy set.
+     * If this is the original expected policy set initialized
+     * by the constructor, then the expected policy set is cleared
+     * before the expected policy is added.
+     *
+     * @param expectedPolicy a String representing an expected policy.
+     */
+    void addExpectedPolicy(String expectedPolicy) {
+        if (isImmutable) {
+            throw new IllegalStateException("PolicyNode is immutable");
+        }
+        if (mOriginalExpectedPolicySet) {
+            mExpectedPolicySet.clear();
+            mOriginalExpectedPolicySet = false;
+        }
+        mExpectedPolicySet.add(expectedPolicy);
+    }
+
+    /**
+     * Removes all paths which don't reach the specified depth.
+     *
+     * @param depth an int representing the desired minimum depth of all paths
+     */
+    void prune(int depth) {
+        if (isImmutable)
+            throw new IllegalStateException("PolicyNode is immutable");
+
+        // if we have no children, we can't prune below us...
+        if (mChildren.size() == 0)
+            return;
+
+        Iterator<PolicyNodeImpl> it = mChildren.iterator();
+        while (it.hasNext()) {
+            PolicyNodeImpl node = it.next();
+            node.prune(depth);
+            // now that we've called prune on the child, see if we should
+            // remove it from the tree
+            if ((node.mChildren.size() == 0) && (depth > mDepth + 1))
+                it.remove();
+        }
+    }
+
+    /**
+     * Deletes the specified child node of this node, if it exists.
+     *
+     * @param childNode the child node to be deleted
+     */
+    void deleteChild(PolicyNode childNode) {
+        if (isImmutable) {
+            throw new IllegalStateException("PolicyNode is immutable");
+        }
+        mChildren.remove(childNode);
+    }
+
+    /**
+     * Returns a copy of the tree, without copying the policy-related data,
+     * rooted at the node on which this was called.
+     *
+     * @return a copy of the tree
+     */
+    PolicyNodeImpl copyTree() {
+        return copyTree(null);
+    }
+
+    private PolicyNodeImpl copyTree(PolicyNodeImpl parent) {
+        PolicyNodeImpl newNode = new PolicyNodeImpl(parent, this);
+
+        for (PolicyNodeImpl node : mChildren) {
+            node.copyTree(newNode);
+        }
+
+        return newNode;
+    }
+
+    /**
+     * Returns all nodes at the specified depth in the tree.
+     *
+     * @param depth an int representing the depth of the desired nodes
+     * @return a <code>Set</code> of all nodes at the specified depth
+     */
+    Set<PolicyNodeImpl> getPolicyNodes(int depth) {
+        Set<PolicyNodeImpl> set = new HashSet<>();
+        getPolicyNodes(depth, set);
+        return set;
+    }
+
+    /**
+     * Add all nodes at depth depth to set and return the Set.
+     * Internal recursion helper.
+     */
+    private void getPolicyNodes(int depth, Set<PolicyNodeImpl> set) {
+        // if we've reached the desired depth, then return ourself
+        if (mDepth == depth) {
+            set.add(this);
+        } else {
+            for (PolicyNodeImpl node : mChildren) {
+                node.getPolicyNodes(depth, set);
+            }
+        }
+    }
+
+    /**
+     * Finds all nodes at the specified depth whose expected_policy_set
+     * contains the specified expected OID (if matchAny is false)
+     * or the special OID "any value" (if matchAny is true).
+     *
+     * @param depth an int representing the desired depth
+     * @param expectedOID a String encoding the valid OID to match
+     * @param matchAny a boolean indicating whether an expected_policy_set
+     * containing ANY_POLICY should be considered a match
+     * @return a Set of matched <code>PolicyNode</code>s
+     */
+    Set<PolicyNodeImpl> getPolicyNodesExpected(int depth,
+        String expectedOID, boolean matchAny) {
+
+        if (expectedOID.equals(ANY_POLICY)) {
+            return getPolicyNodes(depth);
+        } else {
+            return getPolicyNodesExpectedHelper(depth, expectedOID, matchAny);
+        }
+    }
+
+    private Set<PolicyNodeImpl> getPolicyNodesExpectedHelper(int depth,
+        String expectedOID, boolean matchAny) {
+
+        HashSet<PolicyNodeImpl> set = new HashSet<>();
+
+        if (mDepth < depth) {
+            for (PolicyNodeImpl node : mChildren) {
+                set.addAll(node.getPolicyNodesExpectedHelper(depth,
+                                                             expectedOID,
+                                                             matchAny));
+            }
+        } else {
+            if (matchAny) {
+                if (mExpectedPolicySet.contains(ANY_POLICY))
+                    set.add(this);
+            } else {
+                if (mExpectedPolicySet.contains(expectedOID))
+                    set.add(this);
+            }
+        }
+
+        return set;
+    }
+
+    /**
+     * Finds all nodes at the specified depth that contains the
+     * specified valid OID
+     *
+     * @param depth an int representing the desired depth
+     * @param validOID a String encoding the valid OID to match
+     * @return a Set of matched <code>PolicyNode</code>s
+     */
+    Set<PolicyNodeImpl> getPolicyNodesValid(int depth, String validOID) {
+        HashSet<PolicyNodeImpl> set = new HashSet<>();
+
+        if (mDepth < depth) {
+            for (PolicyNodeImpl node : mChildren) {
+                set.addAll(node.getPolicyNodesValid(depth, validOID));
+            }
+        } else {
+            if (mValidPolicy.equals(validOID))
+                set.add(this);
+        }
+
+        return set;
+    }
+
+    private static String policyToString(String oid) {
+        if (oid.equals(ANY_POLICY)) {
+            return "anyPolicy";
+        } else {
+            return oid;
+        }
+    }
+
+    /**
+     * Prints out some data on this node.
+     */
+    String asString() {
+        if (mParent == null) {
+            return "anyPolicy  ROOT\n";
+        } else {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0, n = getDepth(); i < n; i++) {
+                sb.append("  ");
+            }
+            sb.append(policyToString(getValidPolicy()));
+            sb.append("  CRIT: ");
+            sb.append(isCritical());
+            sb.append("  EP: ");
+            for (String policy : getExpectedPolicies()) {
+                sb.append(policyToString(policy));
+                sb.append(" ");
+            }
+            sb.append(" (");
+            sb.append(getDepth());
+            sb.append(")\n");
+            return sb.toString();
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java b/ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java
new file mode 100644
index 0000000..a70945d
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXParameters;
+import java.security.cert.PKIXReason;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509CertSelector;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.provider.certpath.PKIX.BuilderParams;
+import sun.security.util.Debug;
+import sun.security.x509.Extension;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509CertImpl;
+import sun.security.x509.PolicyMappingsExtension;
+
+/**
+ * This class represents a reverse builder, which is able to retrieve
+ * matching certificates from CertStores and verify a particular certificate
+ * against a ReverseState.
+ *
+ * @since       1.4
+ * @author      Sean Mullan
+ * @author      Yassir Elley
+ */
+
+class ReverseBuilder extends Builder {
+
+    private Debug debug = Debug.getInstance("certpath");
+
+    private final Set<String> initPolicies;
+
+    /**
+     * Initialize the builder with the input parameters.
+     *
+     * @param params the parameter set used to build a certification path
+     */
+    ReverseBuilder(BuilderParams buildParams) {
+        super(buildParams);
+
+        Set<String> initialPolicies = buildParams.initialPolicies();
+        initPolicies = new HashSet<String>();
+        if (initialPolicies.isEmpty()) {
+            // if no initialPolicies are specified by user, set
+            // initPolicies to be anyPolicy by default
+            initPolicies.add(PolicyChecker.ANY_POLICY);
+        } else {
+            initPolicies.addAll(initialPolicies);
+        }
+    }
+
+    /**
+     * Retrieves all certs from the specified CertStores that 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>
+     * @param certStores list of CertStores
+     */
+    @Override
+    Collection<X509Certificate> getMatchingCerts
+        (State currState, List<CertStore> certStores)
+        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<X509Certificate> certs =
+            getMatchingEECerts(currentState, certStores);
+        certs.addAll(getMatchingCACerts(currentState, certStores));
+
+        return certs;
+    }
+
+    /*
+     * Retrieves all end-entity certificates which satisfy constraints
+     * and requirements specified in the parameters and PKIX state.
+     */
+    private Collection<X509Certificate> getMatchingEECerts
+        (ReverseState currentState, List<CertStore> certStores)
+        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) targetCertConstraints.clone();
+
+        /*
+         * Match on issuer (subject of previous cert)
+         */
+        sel.setIssuer(currentState.subjectDN);
+
+        /*
+         * Match on certificate validity date.
+         */
+        sel.setCertificateValid(buildParams.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<X509Certificate> eeCerts = new HashSet<>();
+        addMatchingCerts(sel, certStores, eeCerts, true);
+
+        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<X509Certificate> getMatchingCACerts
+        (ReverseState currentState, List<CertStore> certStores)
+        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)
+         */
+        sel.setIssuer(currentState.subjectDN);
+
+        /*
+         * Match on certificate validity date.
+         */
+        sel.setCertificateValid(buildParams.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).
+         */
+        byte[] subject = targetCertConstraints.getSubjectAsBytes();
+        if (subject != null) {
+            sel.addPathToName(4, subject);
+        } else {
+            X509Certificate cert = targetCertConstraints.getCertificate();
+            if (cert != null) {
+                sel.addPathToName(4,
+                                  cert.getSubjectX500Principal().getEncoded());
+            }
+        }
+
+        /*
+         * 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<X509Certificate> reverseCerts = new ArrayList<>();
+        addMatchingCerts(sel, certStores, reverseCerts, true);
+
+        /* 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<X509Certificate> {
+
+        private Debug debug = Debug.getInstance("certpath");
+
+        @Override
+        public int compare(X509Certificate cert1, X509Certificate cert2) {
+
+            /*
+             * if either cert certifies the target, always
+             * put at head of list.
+             */
+            X500Principal targetSubject = buildParams.targetSubject();
+            if (cert1.getSubjectX500Principal().equals(targetSubject)) {
+                return -1;
+            }
+            if (cert2.getSubjectX500Principal().equals(targetSubject)) {
+                return 1;
+            }
+
+            int targetDist1;
+            int targetDist2;
+            try {
+                X500Name targetSubjectName = X500Name.asX500Name(targetSubject);
+                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.
+     *
+     * 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.
+     *
+     * 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
+     */
+    @Override
+    void verifyCert(X509Certificate cert, State currState,
+        List<X509Certificate> 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;
+        }
+
+        // Don't bother to verify untrusted certificate more.
+        currentState.untrustedChecker.check(cert,
+                                    Collections.<String>emptySet());
+
+        /*
+         * 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 occurrences of the same
+         * certificate)))
+         * in order to facilitate the check to see if there are
+         * any policy mapping extensions found between the occurrences
+         * of the same certificate, we reverse the certpathlist first
+         */
+        if ((certPathList != null) && (!certPathList.isEmpty())) {
+            List<X509Certificate> reverseCertList = new ArrayList<>();
+            for (X509Certificate c : certPathList) {
+                reverseCertList.add(0, c);
+            }
+
+            boolean policyMappingFound = false;
+            for (X509Certificate cpListCert : reverseCertList) {
+                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.policyMappingInhibited()) ||
+                        (!policyMappingFound)){
+                        if (debug != null)
+                            debug.println("loop detected!!");
+                        throw new CertPathValidatorException("loop detected");
+                    }
+                }
+            }
+        }
+
+        /* check if target cert */
+        boolean finalCert = cert.getSubjectX500Principal().equals(buildParams.targetSubject());
+
+        /* 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", null,
+                         null, -1, PKIXReason.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 (targetCertConstraints.match(cert) == false) {
+                throw new CertPathValidatorException("target certificate " +
+                    "constraints check failed");
+            }
+        }
+
+        /*
+         * Check revocation.
+         */
+        if (buildParams.revocationEnabled() && currentState.revChecker != null) {
+            currentState.revChecker.check(cert, Collections.<String>emptySet());
+        }
+
+        /* 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", null, null, -1,
+                             PKIXReason.INVALID_NAME);
+                    }
+                } 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.policyQualifiersRejected(), currentState.rootNode,
+            certImpl, finalCert);
+
+        /*
+         * Check CRITICAL private extensions
+         */
+        Set<String> unresolvedCritExts = cert.getCriticalExtensionOIDs();
+        if (unresolvedCritExts == null) {
+            unresolvedCritExts = Collections.<String>emptySet();
+        }
+
+        /*
+         * Check that the signature algorithm is not disabled.
+         */
+        currentState.algorithmChecker.check(cert, unresolvedCritExts);
+
+        for (PKIXCertPathChecker checker : currentState.userCheckers) {
+            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(BasicConstraints_Id.toString());
+            unresolvedCritExts.remove(NameConstraints_Id.toString());
+            unresolvedCritExts.remove(CertificatePolicies_Id.toString());
+            unresolvedCritExts.remove(PolicyMappings_Id.toString());
+            unresolvedCritExts.remove(PolicyConstraints_Id.toString());
+            unresolvedCritExts.remove(InhibitAnyPolicy_Id.toString());
+            unresolvedCritExts.remove(SubjectAlternativeName_Id.toString());
+            unresolvedCritExts.remove(KeyUsage_Id.toString());
+            unresolvedCritExts.remove(ExtendedKeyUsage_Id.toString());
+
+            if (!unresolvedCritExts.isEmpty())
+                throw new CertPathValidatorException
+                    ("Unrecognized critical extension(s)", null, null, -1,
+                     PKIXReason.UNRECOGNIZED_CRIT_EXT);
+        }
+
+        /*
+         * Check signature.
+         */
+        if (buildParams.sigProvider() != null) {
+            cert.verify(currentState.pubKey, buildParams.sigProvider());
+        } 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.
+     */
+    @Override
+    boolean isPathCompleted(X509Certificate cert) {
+        return cert.getSubjectX500Principal().equals(buildParams.targetSubject());
+    }
+
+    /** Adds the certificate to the certPathList
+     *
+     * @param cert the certificate to be added
+     * @param certPathList the certification path list
+     */
+    @Override
+    void addCertToPath(X509Certificate cert,
+        LinkedList<X509Certificate> certPathList) {
+        certPathList.addLast(cert);
+    }
+
+    /** Removes final certificate from the certPathList
+     *
+     * @param certPathList the certification path list
+     */
+    @Override
+    void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
+        certPathList.removeLast();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java b/ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java
new file mode 100644
index 0000000..c515003
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.provider.certpath.PKIX.BuilderParams;
+import sun.security.util.Debug;
+import sun.security.x509.NameConstraintsExtension;
+import sun.security.x509.SubjectKeyIdentifierExtension;
+import sun.security.x509.X509CertImpl;
+
+/**
+ * A specification of a reverse PKIX validation state
+ * which is initialized by each build and updated each time a
+ * certificate is added to the current path.
+ * @since       1.4
+ * @author      Sean Mullan
+ * @author      Yassir Elley
+ */
+
+class ReverseState implements State {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    /* The subject DN of the last cert in the path */
+    X500Principal subjectDN;
+
+    /* The subject public key of the last cert */
+    PublicKey pubKey;
+
+    /* The subject key identifier extension (if any) of the last cert */
+    SubjectKeyIdentifierExtension subjKeyId;
+
+    /* The PKIX constrained/excluded subtrees state variable */
+    NameConstraintsExtension nc;
+
+    /* The PKIX explicit policy, policy mapping, and inhibit_any-policy
+       state variables */
+    int explicitPolicy;
+    int policyMapping;
+    int inhibitAnyPolicy;
+    int certIndex;
+    PolicyNodeImpl rootNode;
+
+    /* The number of remaining CA certs which may follow in the path.
+     * -1: previous cert was an EE cert
+     * 0: only EE certs may follow.
+     * >0 and <Integer.MAX_VALUE:no more than this number of CA certs may follow
+     * Integer.MAX_VALUE: unlimited
+     */
+    int remainingCACerts;
+
+    /* The list of user-defined checkers retrieved from the PKIXParameters
+     * instance */
+    ArrayList<PKIXCertPathChecker> userCheckers;
+
+    /* Flag indicating if state is initial (path is just starting) */
+    private boolean init = true;
+
+    /* the checker used for revocation status */
+    RevocationChecker revChecker;
+
+    /* the algorithm checker */
+    AlgorithmChecker algorithmChecker;
+
+    /* the untrusted certificates checker */
+    UntrustedChecker untrustedChecker;
+
+    /* the trust anchor used to validate the path */
+    TrustAnchor trustAnchor;
+
+    /* Flag indicating if current cert can vouch for the CRL for
+     * the next cert
+     */
+    boolean crlSign = true;
+
+    /**
+     * Returns a boolean flag indicating if the state is initial
+     * (just starting)
+     *
+     * @return boolean flag indicating if the state is initial (just starting)
+     */
+    @Override
+    public boolean isInitial() {
+        return init;
+    }
+
+    /**
+     * Display state for debugging purposes
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("State [");
+        sb.append("\n  subjectDN of last cert: ").append(subjectDN);
+        sb.append("\n  subjectKeyIdentifier: ").append
+                 (String.valueOf(subjKeyId));
+        sb.append("\n  nameConstraints: ").append(String.valueOf(nc));
+        sb.append("\n  certIndex: ").append(certIndex);
+        sb.append("\n  explicitPolicy: ").append(explicitPolicy);
+        sb.append("\n  policyMapping:  ").append(policyMapping);
+        sb.append("\n  inhibitAnyPolicy:  ").append(inhibitAnyPolicy);
+        sb.append("\n  rootNode: ").append(rootNode);
+        sb.append("\n  remainingCACerts: ").append(remainingCACerts);
+        sb.append("\n  crlSign: ").append(crlSign);
+        sb.append("\n  init: ").append(init);
+        sb.append("\n]\n");
+        return sb.toString();
+    }
+
+    /**
+     * Initialize the state.
+     *
+     * @param buildParams builder parameters
+     */
+    public void initState(BuilderParams buildParams)
+        throws CertPathValidatorException
+    {
+        /*
+         * Initialize number of remainingCACerts.
+         * Note that -1 maxPathLen implies unlimited.
+         * 0 implies only an EE cert is acceptable.
+         */
+        int maxPathLen = buildParams.maxPathLength();
+        remainingCACerts = (maxPathLen == -1) ? Integer.MAX_VALUE
+                                              : maxPathLen;
+
+        /* Initialize explicit policy state variable */
+        if (buildParams.explicitPolicyRequired()) {
+            explicitPolicy = 0;
+        } else {
+            // unconstrained if maxPathLen is -1,
+            // otherwise, we want to initialize this to the value of the
+            // longest possible path + 1 (i.e. maxpathlen + finalcert + 1)
+            explicitPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
+        }
+
+        /* Initialize policy mapping state variable */
+        if (buildParams.policyMappingInhibited()) {
+            policyMapping = 0;
+        } else {
+            policyMapping = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
+        }
+
+        /* Initialize inhibit any policy state variable */
+        if (buildParams.anyPolicyInhibited()) {
+            inhibitAnyPolicy = 0;
+        } else {
+            inhibitAnyPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
+        }
+
+        /* Initialize certIndex */
+        certIndex = 1;
+
+        /* Initialize policy tree */
+        Set<String> initExpPolSet = new HashSet<>(1);
+        initExpPolSet.add(PolicyChecker.ANY_POLICY);
+
+        rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null,
+                                      false, initExpPolSet, false);
+
+        /*
+         * Initialize each user-defined checker
+         * Shallow copy the checkers
+         */
+        userCheckers = new ArrayList<>(buildParams.certPathCheckers());
+        /* initialize each checker (just in case) */
+        for (PKIXCertPathChecker checker : userCheckers) {
+            checker.init(false);
+        }
+
+        /* Start by trusting the cert to sign CRLs */
+        crlSign = true;
+
+        init = true;
+    }
+
+    /**
+     * Update the state with the specified trust anchor.
+     *
+     * @param anchor the most-trusted CA
+     * @param buildParams builder parameters
+     */
+    public void updateState(TrustAnchor anchor, BuilderParams buildParams)
+        throws CertificateException, IOException, CertPathValidatorException
+    {
+        trustAnchor = anchor;
+        X509Certificate trustedCert = anchor.getTrustedCert();
+        if (trustedCert != null) {
+            updateState(trustedCert);
+        } else {
+            X500Principal caName = anchor.getCA();
+            updateState(anchor.getCAPublicKey(), caName);
+        }
+
+        // The user specified AlgorithmChecker and RevocationChecker may not be
+        // able to set the trust anchor until now.
+        boolean revCheckerAdded = false;
+        for (PKIXCertPathChecker checker : userCheckers) {
+            if (checker instanceof AlgorithmChecker) {
+                ((AlgorithmChecker)checker).trySetTrustAnchor(anchor);
+            } else if (checker instanceof PKIXRevocationChecker) {
+                if (revCheckerAdded) {
+                    throw new CertPathValidatorException(
+                        "Only one PKIXRevocationChecker can be specified");
+                }
+                // if it's our own, initialize it
+                if (checker instanceof RevocationChecker) {
+                    ((RevocationChecker)checker).init(anchor, buildParams);
+                }
+                ((PKIXRevocationChecker)checker).init(false);
+                revCheckerAdded = true;
+            }
+        }
+
+        // only create a RevocationChecker if revocation is enabled and
+        // a PKIXRevocationChecker has not already been added
+        if (buildParams.revocationEnabled() && !revCheckerAdded) {
+            revChecker = new RevocationChecker(anchor, buildParams);
+            revChecker.init(false);
+        }
+
+        init = false;
+    }
+
+    /**
+     * Update the state. This method is used when the most-trusted CA is
+     * a trusted public-key and caName, instead of a trusted cert.
+     *
+     * @param pubKey the public key of the trusted CA
+     * @param subjectDN the subject distinguished name of the trusted CA
+     */
+    private void updateState(PublicKey pubKey, X500Principal subjectDN) {
+
+        /* update subject DN */
+        this.subjectDN = subjectDN;
+
+        /* update subject public key */
+        this.pubKey = pubKey;
+    }
+
+    /**
+     * Update the state with the next certificate added to the path.
+     *
+     * @param cert the certificate which is used to update the state
+     */
+    public void updateState(X509Certificate cert)
+        throws CertificateException, IOException, CertPathValidatorException {
+
+        if (cert == null) {
+            return;
+        }
+
+        /* update subject DN */
+        subjectDN = cert.getSubjectX500Principal();
+
+        /* check for key needing to inherit alg parameters */
+        X509CertImpl icert = X509CertImpl.toImpl(cert);
+        PublicKey newKey = cert.getPublicKey();
+        if (PKIX.isDSAPublicKeyWithoutParams(newKey)) {
+            newKey = BasicChecker.makeInheritedParamsKey(newKey, pubKey);
+        }
+
+        /* update subject public key */
+        pubKey = newKey;
+
+        /*
+         * if this is a trusted cert (init == true), then we
+         * don't update any of the remaining fields
+         */
+        if (init) {
+            init = false;
+            return;
+        }
+
+        /* update subject key identifier */
+        subjKeyId = icert.getSubjectKeyIdentifierExtension();
+
+        /* update crlSign */
+        crlSign = RevocationChecker.certCanSignCrl(cert);
+
+        /* update current name constraints */
+        if (nc != null) {
+            nc.merge(icert.getNameConstraintsExtension());
+        } else {
+            nc = icert.getNameConstraintsExtension();
+            if (nc != null) {
+                // Make sure we do a clone here, because we're probably
+                // going to modify this object later and we don't want to
+                // be sharing it with a Certificate object!
+                nc = (NameConstraintsExtension) nc.clone();
+            }
+        }
+
+        /* update policy state variables */
+        explicitPolicy =
+            PolicyChecker.mergeExplicitPolicy(explicitPolicy, icert, false);
+        policyMapping =
+            PolicyChecker.mergePolicyMapping(policyMapping, icert);
+        inhibitAnyPolicy =
+            PolicyChecker.mergeInhibitAnyPolicy(inhibitAnyPolicy, icert);
+        certIndex++;
+
+        /*
+         * Update remaining CA certs
+         */
+        remainingCACerts =
+            ConstraintsChecker.mergeBasicConstraints(cert, remainingCACerts);
+
+        init = false;
+    }
+
+    /**
+     * Returns a boolean flag indicating if a key lacking necessary key
+     * algorithm parameters has been encountered.
+     *
+     * @return boolean flag indicating if key lacking parameters encountered.
+     */
+    @Override
+    public boolean keyParamsNeeded() {
+        /* when building in reverse, we immediately get parameters needed
+         * or else throw an exception
+         */
+        return false;
+    }
+
+    /*
+     * Clone current state. The state is cloned as each cert is
+     * added to the path. This is necessary if backtracking occurs,
+     * and a prior state needs to be restored.
+     *
+     * Note that this is a SMART clone. Not all fields are fully copied,
+     * because some of them (e.g., subjKeyId) will
+     * not have their contents modified by subsequent calls to updateState.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
+    public Object clone() {
+        try {
+            ReverseState clonedState = (ReverseState) super.clone();
+
+            /* clone checkers, if cloneable */
+            clonedState.userCheckers =
+                        (ArrayList<PKIXCertPathChecker>)userCheckers.clone();
+            ListIterator<PKIXCertPathChecker> li =
+                        clonedState.userCheckers.listIterator();
+            while (li.hasNext()) {
+                PKIXCertPathChecker checker = li.next();
+                if (checker instanceof Cloneable) {
+                    li.set((PKIXCertPathChecker)checker.clone());
+                }
+            }
+
+            /* make copy of name constraints */
+            if (nc != null) {
+                clonedState.nc = (NameConstraintsExtension) nc.clone();
+            }
+
+            /* make copy of policy tree */
+            if (rootNode != null) {
+                clonedState.rootNode = rootNode.copyTree();
+            }
+
+            return clonedState;
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError(e.toString(), e);
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java
new file mode 100644
index 0000000..19b41f6
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedAction;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.Extension;
+import java.security.cert.*;
+import java.util.*;
+import javax.security.auth.x500.X500Principal;
+
+import static sun.security.provider.certpath.OCSP.*;
+import static sun.security.provider.certpath.PKIX.*;
+import sun.security.action.GetPropertyAction;
+import sun.security.x509.*;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.util.Debug;
+
+class RevocationChecker extends PKIXRevocationChecker {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    private TrustAnchor anchor;
+    private ValidatorParams params;
+    private boolean onlyEE;
+    private boolean softFail;
+    private boolean crlDP;
+    private URI responderURI;
+    private X509Certificate responderCert;
+    private List<CertStore> certStores;
+    private Map<X509Certificate, byte[]> ocspResponses;
+    private List<Extension> ocspExtensions;
+    private boolean legacy;
+    private LinkedList<CertPathValidatorException> softFailExceptions =
+        new LinkedList<>();
+
+    // state variables
+    private X509Certificate issuerCert;
+    private PublicKey prevPubKey;
+    private boolean crlSignFlag;
+    private int certIndex;
+
+    private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP };
+    private Mode mode = Mode.PREFER_OCSP;
+
+    private static class RevocationProperties {
+        boolean onlyEE;
+        boolean ocspEnabled;
+        boolean crlDPEnabled;
+        String ocspUrl;
+        String ocspSubject;
+        String ocspIssuer;
+        String ocspSerial;
+    }
+
+    RevocationChecker() {
+        legacy = false;
+    }
+
+    RevocationChecker(TrustAnchor anchor, ValidatorParams params)
+        throws CertPathValidatorException
+    {
+        legacy = true;
+        init(anchor, params);
+    }
+
+    void init(TrustAnchor anchor, ValidatorParams params)
+        throws CertPathValidatorException
+    {
+        RevocationProperties rp = getRevocationProperties();
+        URI uri = getOcspResponder();
+        responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri;
+        X509Certificate cert = getOcspResponderCert();
+        responderCert = (cert == null)
+                        ? getResponderCert(rp, params.trustAnchors(),
+                                           params.certStores())
+                        : cert;
+        Set<Option> options = getOptions();
+        for (Option option : options) {
+            switch (option) {
+            case ONLY_END_ENTITY:
+            case PREFER_CRLS:
+            case SOFT_FAIL:
+            case NO_FALLBACK:
+                break;
+            default:
+                throw new CertPathValidatorException(
+                    "Unrecognized revocation parameter option: " + option);
+            }
+        }
+        softFail = options.contains(Option.SOFT_FAIL);
+
+        // set mode, only end entity flag
+        if (legacy) {
+            mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS;
+            onlyEE = rp.onlyEE;
+        } else {
+            if (options.contains(Option.NO_FALLBACK)) {
+                if (options.contains(Option.PREFER_CRLS)) {
+                    mode = Mode.ONLY_CRLS;
+                } else {
+                    mode = Mode.ONLY_OCSP;
+                }
+            } else if (options.contains(Option.PREFER_CRLS)) {
+                mode = Mode.PREFER_CRLS;
+            }
+            onlyEE = options.contains(Option.ONLY_END_ENTITY);
+        }
+        if (legacy) {
+            crlDP = rp.crlDPEnabled;
+        } else {
+            crlDP = true;
+        }
+        ocspResponses = getOcspResponses();
+        ocspExtensions = getOcspExtensions();
+
+        this.anchor = anchor;
+        this.params = params;
+        this.certStores = new ArrayList<>(params.certStores());
+        try {
+            this.certStores.add(CertStore.getInstance("Collection",
+                new CollectionCertStoreParameters(params.certificates())));
+        } catch (InvalidAlgorithmParameterException |
+                 NoSuchAlgorithmException e) {
+            // should never occur but not necessarily fatal, so log it,
+            // ignore and continue
+            if (debug != null) {
+                debug.println("RevocationChecker: " +
+                              "error creating Collection CertStore: " + e);
+            }
+        }
+    }
+
+    private static URI toURI(String uriString)
+        throws CertPathValidatorException
+    {
+        try {
+            if (uriString != null) {
+                return new URI(uriString);
+            }
+            return null;
+        } catch (URISyntaxException e) {
+            throw new CertPathValidatorException(
+                "cannot parse ocsp.responderURL property", e);
+        }
+    }
+
+    private static RevocationProperties getRevocationProperties() {
+        return AccessController.doPrivileged(
+            new PrivilegedAction<RevocationProperties>() {
+                public RevocationProperties run() {
+                    RevocationProperties rp = new RevocationProperties();
+                    String onlyEE = Security.getProperty(
+                        "com.sun.security.onlyCheckRevocationOfEECert");
+                    rp.onlyEE = onlyEE != null
+                                && onlyEE.equalsIgnoreCase("true");
+                    String ocspEnabled = Security.getProperty("ocsp.enable");
+                    rp.ocspEnabled = ocspEnabled != null
+                                     && ocspEnabled.equalsIgnoreCase("true");
+                    rp.ocspUrl = Security.getProperty("ocsp.responderURL");
+                    rp.ocspSubject
+                        = Security.getProperty("ocsp.responderCertSubjectName");
+                    rp.ocspIssuer
+                        = Security.getProperty("ocsp.responderCertIssuerName");
+                    rp.ocspSerial
+                        = Security.getProperty("ocsp.responderCertSerialNumber");
+                    rp.crlDPEnabled
+                        = Boolean.getBoolean("com.sun.security.enableCRLDP");
+                    return rp;
+                }
+            }
+        );
+    }
+
+    private static X509Certificate getResponderCert(RevocationProperties rp,
+                                                    Set<TrustAnchor> anchors,
+                                                    List<CertStore> stores)
+        throws CertPathValidatorException
+    {
+        if (rp.ocspSubject != null) {
+            return getResponderCert(rp.ocspSubject, anchors, stores);
+        } else if (rp.ocspIssuer != null && rp.ocspSerial != null) {
+            return getResponderCert(rp.ocspIssuer, rp.ocspSerial,
+                                    anchors, stores);
+        } else if (rp.ocspIssuer != null || rp.ocspSerial != null) {
+            throw new CertPathValidatorException(
+                "Must specify both ocsp.responderCertIssuerName and " +
+                "ocsp.responderCertSerialNumber properties");
+        }
+        return null;
+    }
+
+    private static X509Certificate getResponderCert(String subject,
+                                                    Set<TrustAnchor> anchors,
+                                                    List<CertStore> stores)
+        throws CertPathValidatorException
+    {
+        X509CertSelector sel = new X509CertSelector();
+        try {
+            sel.setSubject(new X500Principal(subject));
+        } catch (IllegalArgumentException e) {
+            throw new CertPathValidatorException(
+                "cannot parse ocsp.responderCertSubjectName property", e);
+        }
+        return getResponderCert(sel, anchors, stores);
+    }
+
+    private static X509Certificate getResponderCert(String issuer,
+                                                    String serial,
+                                                    Set<TrustAnchor> anchors,
+                                                    List<CertStore> stores)
+        throws CertPathValidatorException
+    {
+        X509CertSelector sel = new X509CertSelector();
+        try {
+            sel.setIssuer(new X500Principal(issuer));
+        } catch (IllegalArgumentException e) {
+            throw new CertPathValidatorException(
+                "cannot parse ocsp.responderCertIssuerName property", e);
+        }
+        try {
+            sel.setSerialNumber(new BigInteger(stripOutSeparators(serial), 16));
+        } catch (NumberFormatException e) {
+            throw new CertPathValidatorException(
+                "cannot parse ocsp.responderCertSerialNumber property", e);
+        }
+        return getResponderCert(sel, anchors, stores);
+    }
+
+    private static X509Certificate getResponderCert(X509CertSelector sel,
+                                                    Set<TrustAnchor> anchors,
+                                                    List<CertStore> stores)
+        throws CertPathValidatorException
+    {
+        // first check TrustAnchors
+        for (TrustAnchor anchor : anchors) {
+            X509Certificate cert = anchor.getTrustedCert();
+            if (cert == null) {
+                continue;
+            }
+            if (sel.match(cert)) {
+                return cert;
+            }
+        }
+        // now check CertStores
+        for (CertStore store : stores) {
+            try {
+                Collection<? extends Certificate> certs =
+                    store.getCertificates(sel);
+                if (!certs.isEmpty()) {
+                    return (X509Certificate)certs.iterator().next();
+                }
+            } catch (CertStoreException e) {
+                // ignore and try next CertStore
+                if (debug != null) {
+                    debug.println("CertStore exception:" + e);
+                }
+                continue;
+            }
+        }
+        throw new CertPathValidatorException(
+            "Cannot find the responder's certificate " +
+            "(set using the OCSP security properties).");
+    }
+
+    @Override
+    public void init(boolean forward) throws CertPathValidatorException {
+        if (forward) {
+            throw new
+                CertPathValidatorException("forward checking not supported");
+        }
+        if (anchor != null) {
+            issuerCert = anchor.getTrustedCert();
+            prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey()
+                                              : anchor.getCAPublicKey();
+        }
+        crlSignFlag = true;
+        if (params != null && params.certPath() != null) {
+            certIndex = params.certPath().getCertificates().size() - 1;
+        } else {
+            certIndex = -1;
+        }
+        softFailExceptions.clear();
+    }
+
+    @Override
+    public boolean isForwardCheckingSupported() {
+        return false;
+    }
+
+    @Override
+    public Set<String> getSupportedExtensions() {
+        return null;
+    }
+
+    @Override
+    public List<CertPathValidatorException> getSoftFailExceptions() {
+        return Collections.unmodifiableList(softFailExceptions);
+    }
+
+    @Override
+    public void check(Certificate cert, Collection<String> unresolvedCritExts)
+        throws CertPathValidatorException
+    {
+        check((X509Certificate)cert, unresolvedCritExts,
+              prevPubKey, crlSignFlag);
+    }
+
+    private void check(X509Certificate xcert,
+                       Collection<String> unresolvedCritExts,
+                       PublicKey pubKey, boolean crlSignFlag)
+        throws CertPathValidatorException
+    {
+        try {
+            if (onlyEE && xcert.getBasicConstraints() != -1) {
+                if (debug != null) {
+                    debug.println("Skipping revocation check, not end " +
+                                  "entity cert");
+                }
+                return;
+            }
+            switch (mode) {
+                case PREFER_OCSP:
+                case ONLY_OCSP:
+                    checkOCSP(xcert, unresolvedCritExts);
+                    break;
+                case PREFER_CRLS:
+                case ONLY_CRLS:
+                    checkCRLs(xcert, unresolvedCritExts, null,
+                              pubKey, crlSignFlag);
+                    break;
+            }
+        } catch (CertPathValidatorException e) {
+            if (e.getReason() == BasicReason.REVOKED) {
+                throw e;
+            }
+            boolean eSoftFail = isSoftFailException(e);
+            if (eSoftFail) {
+                if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {
+                    return;
+                }
+            } else {
+                if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {
+                    throw e;
+                }
+            }
+            CertPathValidatorException cause = e;
+            // Otherwise, failover
+            if (debug != null) {
+                debug.println("RevocationChecker.check() " + e.getMessage());
+                debug.println("RevocationChecker.check() preparing to failover");
+            }
+            try {
+                switch (mode) {
+                    case PREFER_OCSP:
+                        checkCRLs(xcert, unresolvedCritExts, null,
+                                  pubKey, crlSignFlag);
+                        break;
+                    case PREFER_CRLS:
+                        checkOCSP(xcert, unresolvedCritExts);
+                        break;
+                }
+            } catch (CertPathValidatorException x) {
+                if (debug != null) {
+                    debug.println("RevocationChecker.check() failover failed");
+                    debug.println("RevocationChecker.check() " + x.getMessage());
+                }
+                if (x.getReason() == BasicReason.REVOKED) {
+                    throw x;
+                }
+                if (!isSoftFailException(x)) {
+                    cause.addSuppressed(x);
+                    throw cause;
+                } else {
+                    // only pass if both exceptions were soft failures
+                    if (!eSoftFail) {
+                        throw cause;
+                    }
+                }
+            }
+        } finally {
+            updateState(xcert);
+        }
+    }
+
+    private boolean isSoftFailException(CertPathValidatorException e) {
+        if (softFail &&
+            e.getReason() == BasicReason.UNDETERMINED_REVOCATION_STATUS)
+        {
+            // recreate exception with correct index
+            CertPathValidatorException e2 = new CertPathValidatorException(
+                e.getMessage(), e.getCause(), params.certPath(), certIndex,
+                e.getReason());
+            softFailExceptions.addFirst(e2);
+            return true;
+        }
+        return false;
+    }
+
+    private void updateState(X509Certificate cert)
+        throws CertPathValidatorException
+    {
+        issuerCert = cert;
+
+        // Make new public key if parameters are missing
+        PublicKey pubKey = cert.getPublicKey();
+        if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) {
+            // pubKey needs to inherit DSA parameters from prev key
+            pubKey = BasicChecker.makeInheritedParamsKey(pubKey, prevPubKey);
+        }
+        prevPubKey = pubKey;
+        crlSignFlag = certCanSignCrl(cert);
+        if (certIndex > 0) {
+            certIndex--;
+        }
+    }
+
+    // Maximum clock skew in milliseconds (15 minutes) allowed when checking
+    // validity of CRLs
+    private static final long MAX_CLOCK_SKEW = 900000;
+    private void checkCRLs(X509Certificate cert,
+                           Collection<String> unresolvedCritExts,
+                           Set<X509Certificate> stackedCerts,
+                           PublicKey pubKey, boolean signFlag)
+        throws CertPathValidatorException
+    {
+        checkCRLs(cert, pubKey, null, signFlag, true,
+                  stackedCerts, params.trustAnchors());
+    }
+
+    private void checkCRLs(X509Certificate cert, PublicKey prevKey,
+                           X509Certificate prevCert, boolean signFlag,
+                           boolean allowSeparateKey,
+                           Set<X509Certificate> stackedCerts,
+                           Set<TrustAnchor> anchors)
+        throws CertPathValidatorException
+    {
+        if (debug != null) {
+            debug.println("RevocationChecker.checkCRLs()" +
+                          " ---checking revocation status ...");
+        }
+
+        // reject circular dependencies - RFC 3280 is not explicit on how
+        // to handle this, so we feel it is safest to reject them until
+        // the issue is resolved in the PKIX WG.
+        if (stackedCerts != null && stackedCerts.contains(cert)) {
+            if (debug != null) {
+                debug.println("RevocationChecker.checkCRLs()" +
+                              " circular dependency");
+            }
+            throw new CertPathValidatorException
+                 ("Could not determine revocation status", null, null, -1,
+                  BasicReason.UNDETERMINED_REVOCATION_STATUS);
+        }
+
+        Set<X509CRL> possibleCRLs = new HashSet<>();
+        Set<X509CRL> approvedCRLs = new HashSet<>();
+        X509CRLSelector sel = new X509CRLSelector();
+        sel.setCertificateChecking(cert);
+        CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW);
+
+        // First, check user-specified CertStores
+        CertPathValidatorException networkFailureException = null;
+        for (CertStore store : certStores) {
+            try {
+                for (CRL crl : store.getCRLs(sel)) {
+                    possibleCRLs.add((X509CRL)crl);
+                }
+            } catch (CertStoreException e) {
+                if (debug != null) {
+                    debug.println("RevocationChecker.checkCRLs() " +
+                                  "CertStoreException: " + e.getMessage());
+                }
+                if (networkFailureException == null &&
+                    CertStoreHelper.isCausedByNetworkIssue(store.getType(),e)) {
+                    // save this exception, we may need to throw it later
+                    networkFailureException = new CertPathValidatorException(
+                        "Unable to determine revocation status due to " +
+                        "network error", e, null, -1,
+                        BasicReason.UNDETERMINED_REVOCATION_STATUS);
+                }
+            }
+        }
+
+        if (debug != null) {
+            debug.println("RevocationChecker.checkCRLs() " +
+                          "possible crls.size() = " + possibleCRLs.size());
+        }
+        boolean[] reasonsMask = new boolean[9];
+        if (!possibleCRLs.isEmpty()) {
+            // Now that we have a list of possible CRLs, see which ones can
+            // be approved
+            approvedCRLs.addAll(verifyPossibleCRLs(possibleCRLs, cert, prevKey,
+                                                   signFlag, reasonsMask,
+                                                   anchors));
+        }
+
+        if (debug != null) {
+            debug.println("RevocationChecker.checkCRLs() " +
+                          "approved crls.size() = " + approvedCRLs.size());
+        }
+
+        // make sure that we have at least one CRL that _could_ cover
+        // the certificate in question and all reasons are covered
+        if (!approvedCRLs.isEmpty() &&
+            Arrays.equals(reasonsMask, ALL_REASONS))
+        {
+            checkApprovedCRLs(cert, approvedCRLs);
+        } else {
+            // Check Distribution Points
+            // all CRLs returned by the DP Fetcher have also been verified
+            try {
+                if (crlDP) {
+                    approvedCRLs.addAll(DistributionPointFetcher.getCRLs(
+                                        sel, signFlag, prevKey, prevCert,
+                                        params.sigProvider(), certStores,
+                                        reasonsMask, anchors, null));
+                }
+            } catch (CertStoreException e) {
+                if (e instanceof CertStoreTypeException) {
+                    CertStoreTypeException cste = (CertStoreTypeException)e;
+                    if (CertStoreHelper.isCausedByNetworkIssue(cste.getType(),
+                                                               e)) {
+                        throw new CertPathValidatorException(
+                            "Unable to determine revocation status due to " +
+                            "network error", e, null, -1,
+                            BasicReason.UNDETERMINED_REVOCATION_STATUS);
+                    }
+                }
+                throw new CertPathValidatorException(e);
+            }
+            if (!approvedCRLs.isEmpty() &&
+                Arrays.equals(reasonsMask, ALL_REASONS))
+            {
+                checkApprovedCRLs(cert, approvedCRLs);
+            } else {
+                if (allowSeparateKey) {
+                    try {
+                        verifyWithSeparateSigningKey(cert, prevKey, signFlag,
+                                                     stackedCerts);
+                        return;
+                    } catch (CertPathValidatorException cpve) {
+                        if (networkFailureException != null) {
+                            // if a network issue previously prevented us from
+                            // retrieving a CRL from one of the user-specified
+                            // CertStores, throw it now so it can be handled
+                            // appropriately
+                            throw networkFailureException;
+                        }
+                        throw cpve;
+                    }
+                } else {
+                    if (networkFailureException != null) {
+                        // if a network issue previously prevented us from
+                        // retrieving a CRL from one of the user-specified
+                        // CertStores, throw it now so it can be handled
+                        // appropriately
+                        throw networkFailureException;
+                    }
+                    throw new CertPathValidatorException(
+                        "Could not determine revocation status", null, null, -1,
+                        BasicReason.UNDETERMINED_REVOCATION_STATUS);
+                }
+            }
+        }
+    }
+
+    private void checkApprovedCRLs(X509Certificate cert,
+                                   Set<X509CRL> approvedCRLs)
+        throws CertPathValidatorException
+    {
+        // See if the cert is in the set of approved crls.
+        if (debug != null) {
+            BigInteger sn = cert.getSerialNumber();
+            debug.println("RevocationChecker.checkApprovedCRLs() " +
+                          "starting the final sweep...");
+            debug.println("RevocationChecker.checkApprovedCRLs()" +
+                          " cert SN: " + sn.toString());
+        }
+
+        CRLReason reasonCode = CRLReason.UNSPECIFIED;
+        X509CRLEntryImpl entry = null;
+        for (X509CRL crl : approvedCRLs) {
+            X509CRLEntry e = crl.getRevokedCertificate(cert);
+            if (e != null) {
+                try {
+                    entry = X509CRLEntryImpl.toImpl(e);
+                } catch (CRLException ce) {
+                    throw new CertPathValidatorException(ce);
+                }
+                if (debug != null) {
+                    debug.println("RevocationChecker.checkApprovedCRLs()"
+                        + " CRL entry: " + entry.toString());
+                }
+
+                /*
+                 * Abort CRL validation and throw exception if there are any
+                 * unrecognized critical CRL entry extensions (see section
+                 * 5.3 of RFC 3280).
+                 */
+                Set<String> unresCritExts = entry.getCriticalExtensionOIDs();
+                if (unresCritExts != null && !unresCritExts.isEmpty()) {
+                    /* remove any that we will process */
+                    unresCritExts.remove(ReasonCode_Id.toString());
+                    unresCritExts.remove(CertificateIssuer_Id.toString());
+                    if (!unresCritExts.isEmpty()) {
+                        throw new CertPathValidatorException(
+                            "Unrecognized critical extension(s) in revoked " +
+                            "CRL entry");
+                    }
+                }
+
+                reasonCode = entry.getRevocationReason();
+                if (reasonCode == null) {
+                    reasonCode = CRLReason.UNSPECIFIED;
+                }
+                Date revocationDate = entry.getRevocationDate();
+                if (revocationDate.before(params.date())) {
+                    Throwable t = new CertificateRevokedException(
+                        revocationDate, reasonCode,
+                        crl.getIssuerX500Principal(), entry.getExtensions());
+                    throw new CertPathValidatorException(
+                        t.getMessage(), t, null, -1, BasicReason.REVOKED);
+                }
+            }
+        }
+    }
+
+    private void checkOCSP(X509Certificate cert,
+                           Collection<String> unresolvedCritExts)
+        throws CertPathValidatorException
+    {
+        X509CertImpl currCert = null;
+        try {
+            currCert = X509CertImpl.toImpl(cert);
+        } catch (CertificateException ce) {
+            throw new CertPathValidatorException(ce);
+        }
+
+        // The algorithm constraints of the OCSP trusted responder certificate
+        // does not need to be checked in this code. The constraints will be
+        // checked when the responder's certificate is validated.
+
+        OCSPResponse response = null;
+        CertId certId = null;
+        try {
+            if (issuerCert != null) {
+                certId = new CertId(issuerCert,
+                                    currCert.getSerialNumberObject());
+            } else {
+                // must be an anchor name and key
+                certId = new CertId(anchor.getCA(), anchor.getCAPublicKey(),
+                                    currCert.getSerialNumberObject());
+            }
+
+            // check if there is a cached OCSP response available
+            byte[] responseBytes = ocspResponses.get(cert);
+            if (responseBytes != null) {
+                if (debug != null) {
+                    debug.println("Found cached OCSP response");
+                }
+                response = new OCSPResponse(responseBytes);
+
+                // verify the response
+                byte[] nonce = null;
+                for (Extension ext : ocspExtensions) {
+                    if (ext.getId().equals("1.3.6.1.5.5.7.48.1.2")) {
+                        nonce = ext.getValue();
+                    }
+                }
+                response.verify(Collections.singletonList(certId), issuerCert,
+                                responderCert, params.date(), nonce);
+
+            } else {
+                URI responderURI = (this.responderURI != null)
+                                   ? this.responderURI
+                                   : OCSP.getResponderURI(currCert);
+                if (responderURI == null) {
+                    throw new CertPathValidatorException(
+                        "Certificate does not specify OCSP responder", null,
+                        null, -1);
+                }
+
+                response = OCSP.check(Collections.singletonList(certId),
+                                      responderURI, issuerCert, responderCert,
+                                      null, ocspExtensions);
+            }
+        } catch (IOException e) {
+            throw new CertPathValidatorException(
+                "Unable to determine revocation status due to network error",
+                e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
+        }
+
+        RevocationStatus rs =
+            (RevocationStatus)response.getSingleResponse(certId);
+        RevocationStatus.CertStatus certStatus = rs.getCertStatus();
+        if (certStatus == RevocationStatus.CertStatus.REVOKED) {
+            Date revocationTime = rs.getRevocationTime();
+            if (revocationTime.before(params.date())) {
+                Throwable t = new CertificateRevokedException(
+                    revocationTime, rs.getRevocationReason(),
+                    response.getSignerCertificate().getSubjectX500Principal(),
+                    rs.getSingleExtensions());
+                throw new CertPathValidatorException(t.getMessage(), t, null,
+                                                     -1, BasicReason.REVOKED);
+            }
+        } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {
+            throw new CertPathValidatorException(
+                "Certificate's revocation status is unknown", null,
+                params.certPath(), -1,
+                BasicReason.UNDETERMINED_REVOCATION_STATUS);
+        }
+    }
+
+    /*
+     * Removes any non-hexadecimal characters from a string.
+     */
+    private static final String HEX_DIGITS = "0123456789ABCDEFabcdef";
+    private static String stripOutSeparators(String value) {
+        char[] chars = value.toCharArray();
+        StringBuilder hexNumber = new StringBuilder();
+        for (int i = 0; i < chars.length; i++) {
+            if (HEX_DIGITS.indexOf(chars[i]) != -1) {
+                hexNumber.append(chars[i]);
+            }
+        }
+        return hexNumber.toString();
+    }
+
+    /**
+     * Checks that a cert can be used to verify a CRL.
+     *
+     * @param cert an X509Certificate to check
+     * @return a boolean specifying if the cert is allowed to vouch for the
+     *         validity of a CRL
+     */
+    static boolean certCanSignCrl(X509Certificate cert) {
+        // if the cert doesn't include the key usage ext, or
+        // the key usage ext asserts cRLSigning, return true,
+        // otherwise return false.
+        boolean[] keyUsage = cert.getKeyUsage();
+        if (keyUsage != null) {
+            return keyUsage[6];
+        }
+        return false;
+    }
+
+    /**
+     * Internal method that verifies a set of possible_crls,
+     * and sees if each is approved, based on the cert.
+     *
+     * @param crls a set of possible CRLs to test for acceptability
+     * @param cert the certificate whose revocation status is being checked
+     * @param signFlag <code>true</code> if prevKey was trusted to sign CRLs
+     * @param prevKey the public key of the issuer of cert
+     * @param reasonsMask the reason code mask
+     * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s>
+     * @return a collection of approved crls (or an empty collection)
+     */
+    private static final boolean[] ALL_REASONS =
+        {true, true, true, true, true, true, true, true, true};
+    private Collection<X509CRL> verifyPossibleCRLs(Set<X509CRL> crls,
+                                                   X509Certificate cert,
+                                                   PublicKey prevKey,
+                                                   boolean signFlag,
+                                                   boolean[] reasonsMask,
+                                                   Set<TrustAnchor> anchors)
+        throws CertPathValidatorException
+    {
+        try {
+            X509CertImpl certImpl = X509CertImpl.toImpl(cert);
+            if (debug != null) {
+                debug.println("RevocationChecker.verifyPossibleCRLs: " +
+                              "Checking CRLDPs for "
+                              + certImpl.getSubjectX500Principal());
+            }
+            CRLDistributionPointsExtension ext =
+                certImpl.getCRLDistributionPointsExtension();
+            List<DistributionPoint> points = null;
+            if (ext == null) {
+                // assume a DP with reasons and CRLIssuer fields omitted
+                // and a DP name of the cert issuer.
+                // TODO add issuerAltName too
+                X500Name certIssuer = (X500Name)certImpl.getIssuerDN();
+                DistributionPoint point = new DistributionPoint(
+                     new GeneralNames().add(new GeneralName(certIssuer)),
+                     null, null);
+                points = Collections.singletonList(point);
+            } else {
+                points = ext.get(CRLDistributionPointsExtension.POINTS);
+            }
+            Set<X509CRL> results = new HashSet<>();
+            for (DistributionPoint point : points) {
+                for (X509CRL crl : crls) {
+                    if (DistributionPointFetcher.verifyCRL(
+                            certImpl, point, crl, reasonsMask, signFlag,
+                            prevKey, null, params.sigProvider(), anchors,
+                            certStores, params.date()))
+                    {
+                        results.add(crl);
+                    }
+                }
+                if (Arrays.equals(reasonsMask, ALL_REASONS))
+                    break;
+            }
+            return results;
+        } catch (CertificateException | CRLException | IOException e) {
+            if (debug != null) {
+                debug.println("Exception while verifying CRL: "+e.getMessage());
+                e.printStackTrace();
+            }
+            return Collections.emptySet();
+        }
+    }
+
+    /**
+     * We have a cert whose revocation status couldn't be verified by
+     * a CRL issued by the cert that issued the CRL. See if we can
+     * find a valid CRL issued by a separate key that can verify the
+     * revocation status of this certificate.
+     * <p>
+     * Note that this does not provide support for indirect CRLs,
+     * only CRLs signed with a different key (but the same issuer
+     * name) as the certificate being checked.
+     *
+     * @param currCert the <code>X509Certificate</code> to be checked
+     * @param prevKey the <code>PublicKey</code> that failed
+     * @param signFlag <code>true</code> if that key was trusted to sign CRLs
+     * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>
+     *                     whose revocation status depends on the
+     *                     non-revoked status of this cert. To avoid
+     *                     circular dependencies, we assume they're
+     *                     revoked while checking the revocation
+     *                     status of this cert.
+     * @throws CertPathValidatorException if the cert's revocation status
+     *         cannot be verified successfully with another key
+     */
+    private void verifyWithSeparateSigningKey(X509Certificate cert,
+                                              PublicKey prevKey,
+                                              boolean signFlag,
+                                              Set<X509Certificate> stackedCerts)
+        throws CertPathValidatorException
+    {
+        String msg = "revocation status";
+        if (debug != null) {
+            debug.println(
+                "RevocationChecker.verifyWithSeparateSigningKey()" +
+                " ---checking " + msg + "...");
+        }
+
+        // reject circular dependencies - RFC 3280 is not explicit on how
+        // to handle this, so we feel it is safest to reject them until
+        // the issue is resolved in the PKIX WG.
+        if ((stackedCerts != null) && stackedCerts.contains(cert)) {
+            if (debug != null) {
+                debug.println(
+                    "RevocationChecker.verifyWithSeparateSigningKey()" +
+                    " circular dependency");
+            }
+            throw new CertPathValidatorException
+                ("Could not determine revocation status", null, null, -1,
+                 BasicReason.UNDETERMINED_REVOCATION_STATUS);
+        }
+
+        // Try to find another key that might be able to sign
+        // CRLs vouching for this cert.
+        // If prevKey wasn't trusted, maybe we just didn't have the right
+        // path to it. Don't rule that key out.
+        if (!signFlag) {
+            buildToNewKey(cert, null, stackedCerts);
+        } else {
+            buildToNewKey(cert, prevKey, stackedCerts);
+        }
+    }
+
+    /**
+     * Tries to find a CertPath that establishes a key that can be
+     * used to verify the revocation status of a given certificate.
+     * Ignores keys that have previously been tried. Throws a
+     * CertPathValidatorException if no such key could be found.
+     *
+     * @param currCert the <code>X509Certificate</code> to be checked
+     * @param prevKey the <code>PublicKey</code> of the certificate whose key
+     *    cannot be used to vouch for the CRL and should be ignored
+     * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>
+     *                     whose revocation status depends on the
+     *                     establishment of this path.
+     * @throws CertPathValidatorException on failure
+     */
+    private static final boolean [] CRL_SIGN_USAGE =
+        { false, false, false, false, false, false, true };
+    private void buildToNewKey(X509Certificate currCert,
+                               PublicKey prevKey,
+                               Set<X509Certificate> stackedCerts)
+        throws CertPathValidatorException
+    {
+
+        if (debug != null) {
+            debug.println("RevocationChecker.buildToNewKey()" +
+                          " starting work");
+        }
+        Set<PublicKey> badKeys = new HashSet<>();
+        if (prevKey != null) {
+            badKeys.add(prevKey);
+        }
+        X509CertSelector certSel = new RejectKeySelector(badKeys);
+        certSel.setSubject(currCert.getIssuerX500Principal());
+        certSel.setKeyUsage(CRL_SIGN_USAGE);
+
+        Set<TrustAnchor> newAnchors = anchor == null ?
+                                      params.trustAnchors() :
+                                      Collections.singleton(anchor);
+
+        PKIXBuilderParameters builderParams;
+        try {
+            builderParams = new PKIXBuilderParameters(newAnchors, certSel);
+        } catch (InvalidAlgorithmParameterException iape) {
+            throw new RuntimeException(iape); // should never occur
+        }
+        builderParams.setInitialPolicies(params.initialPolicies());
+        builderParams.setCertStores(certStores);
+        builderParams.setExplicitPolicyRequired
+            (params.explicitPolicyRequired());
+        builderParams.setPolicyMappingInhibited
+            (params.policyMappingInhibited());
+        builderParams.setAnyPolicyInhibited(params.anyPolicyInhibited());
+        // Policy qualifiers must be rejected, since we don't have
+        // any way to convey them back to the application.
+        // That's the default, so no need to write code.
+        builderParams.setDate(params.date());
+        // CertPathCheckers need to be cloned to start from fresh state
+        builderParams.setCertPathCheckers(
+            params.getPKIXParameters().getCertPathCheckers());
+        builderParams.setSigProvider(params.sigProvider());
+
+        // Skip revocation during this build to detect circular
+        // references. But check revocation afterwards, using the
+        // key (or any other that works).
+        builderParams.setRevocationEnabled(false);
+
+        // check for AuthorityInformationAccess extension
+        if (Builder.USE_AIA == true) {
+            X509CertImpl currCertImpl = null;
+            try {
+                currCertImpl = X509CertImpl.toImpl(currCert);
+            } catch (CertificateException ce) {
+                // ignore but log it
+                if (debug != null) {
+                    debug.println("RevocationChecker.buildToNewKey: " +
+                                  "error decoding cert: " + ce);
+                }
+            }
+            AuthorityInfoAccessExtension aiaExt = null;
+            if (currCertImpl != null) {
+                aiaExt = currCertImpl.getAuthorityInfoAccessExtension();
+            }
+            if (aiaExt != null) {
+                List<AccessDescription> adList = aiaExt.getAccessDescriptions();
+                if (adList != null) {
+                    for (AccessDescription ad : adList) {
+                        CertStore cs = URICertStore.getInstance(ad);
+                        if (cs != null) {
+                            if (debug != null) {
+                                debug.println("adding AIAext CertStore");
+                            }
+                            builderParams.addCertStore(cs);
+                        }
+                    }
+                }
+            }
+        }
+
+        CertPathBuilder builder = null;
+        try {
+            builder = CertPathBuilder.getInstance("PKIX");
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new CertPathValidatorException(nsae);
+        }
+        while (true) {
+            try {
+                if (debug != null) {
+                    debug.println("RevocationChecker.buildToNewKey()" +
+                                  " about to try build ...");
+                }
+                PKIXCertPathBuilderResult cpbr =
+                    (PKIXCertPathBuilderResult)builder.build(builderParams);
+
+                if (debug != null) {
+                    debug.println("RevocationChecker.buildToNewKey()" +
+                                  " about to check revocation ...");
+                }
+                // Now check revocation of all certs in path, assuming that
+                // the stackedCerts are revoked.
+                if (stackedCerts == null) {
+                    stackedCerts = new HashSet<X509Certificate>();
+                }
+                stackedCerts.add(currCert);
+                TrustAnchor ta = cpbr.getTrustAnchor();
+                PublicKey prevKey2 = ta.getCAPublicKey();
+                if (prevKey2 == null) {
+                    prevKey2 = ta.getTrustedCert().getPublicKey();
+                }
+                boolean signFlag = true;
+                List<? extends Certificate> cpList =
+                    cpbr.getCertPath().getCertificates();
+                try {
+                    for (int i = cpList.size()-1; i >= 0; i-- ) {
+                        X509Certificate cert = (X509Certificate)cpList.get(i);
+
+                        if (debug != null) {
+                            debug.println("RevocationChecker.buildToNewKey()"
+                                          + " index " + i + " checking "
+                                          + cert);
+                        }
+                        checkCRLs(cert, prevKey2, null, signFlag, true,
+                                  stackedCerts, newAnchors);
+                        signFlag = certCanSignCrl(cert);
+                        prevKey2 = cert.getPublicKey();
+                    }
+                } catch (CertPathValidatorException cpve) {
+                    // ignore it and try to get another key
+                    badKeys.add(cpbr.getPublicKey());
+                    continue;
+                }
+
+                if (debug != null) {
+                    debug.println("RevocationChecker.buildToNewKey()" +
+                                  " got key " + cpbr.getPublicKey());
+                }
+                // Now check revocation on the current cert using that key and
+                // the corresponding certificate.
+                // If it doesn't check out, try to find a different key.
+                // And if we can't find a key, then return false.
+                PublicKey newKey = cpbr.getPublicKey();
+                try {
+                    checkCRLs(currCert, newKey, (X509Certificate) cpList.get(0),
+                              true, false, null, params.trustAnchors());
+                    // If that passed, the cert is OK!
+                    return;
+                } catch (CertPathValidatorException cpve) {
+                    // If it is revoked, rethrow exception
+                    if (cpve.getReason() == BasicReason.REVOKED) {
+                        throw cpve;
+                    }
+                    // Otherwise, ignore the exception and
+                    // try to get another key.
+                }
+                badKeys.add(newKey);
+            } catch (InvalidAlgorithmParameterException iape) {
+                throw new CertPathValidatorException(iape);
+            } catch (CertPathBuilderException cpbe) {
+                throw new CertPathValidatorException
+                    ("Could not determine revocation status", null, null,
+                     -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
+            }
+        }
+    }
+
+    @Override
+    public RevocationChecker clone() {
+        RevocationChecker copy = (RevocationChecker)super.clone();
+        // we don't deep-copy the exceptions, but that is ok because they
+        // are never modified after they are instantiated
+        copy.softFailExceptions = new LinkedList<>(softFailExceptions);
+        return copy;
+    }
+
+    /*
+     * This inner class extends the X509CertSelector to add an additional
+     * check to make sure the subject public key isn't on a particular list.
+     * This class is used by buildToNewKey() to make sure the builder doesn't
+     * end up with a CertPath to a public key that has already been rejected.
+     */
+    private static class RejectKeySelector extends X509CertSelector {
+        private final Set<PublicKey> badKeySet;
+
+        /**
+         * Creates a new <code>RejectKeySelector</code>.
+         *
+         * @param badPublicKeys a <code>Set</code> of
+         *                      <code>PublicKey</code>s that
+         *                      should be rejected (or <code>null</code>
+         *                      if no such check should be done)
+         */
+        RejectKeySelector(Set<PublicKey> badPublicKeys) {
+            this.badKeySet = badPublicKeys;
+        }
+
+        /**
+         * Decides whether a <code>Certificate</code> should be selected.
+         *
+         * @param cert the <code>Certificate</code> to be checked
+         * @return <code>true</code> if the <code>Certificate</code> should be
+         *         selected, <code>false</code> otherwise
+         */
+        @Override
+        public boolean match(Certificate cert) {
+            if (!super.match(cert))
+                return(false);
+
+            if (badKeySet.contains(cert.getPublicKey())) {
+                if (debug != null)
+                    debug.println("RejectKeySelector.match: bad key");
+                return false;
+            }
+
+            if (debug != null)
+                debug.println("RejectKeySelector.match: returning true");
+            return true;
+        }
+
+        /**
+         * Return a printable representation of the <code>CertSelector</code>.
+         *
+         * @return a <code>String</code> describing the contents of the
+         *         <code>CertSelector</code>
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("RejectKeySelector: [\n");
+            sb.append(super.toString());
+            sb.append(badKeySet);
+            sb.append("]");
+            return sb.toString();
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/State.java b/ojluni/src/main/java/sun/security/provider/certpath/State.java
new file mode 100644
index 0000000..93a153f
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/State.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertPathValidatorException;
+
+/**
+ * A specification of a PKIX validation state
+ * which is initialized by each build and updated each time a
+ * certificate is added to the current path.
+ *
+ * @since       1.4
+ * @author      Sean Mullan
+ * @author      Yassir Elley
+ */
+
+interface State extends Cloneable {
+
+    /**
+     * Update the state with the next certificate added to the path.
+     *
+     * @param cert the certificate which is used to update the state
+     */
+    public void updateState(X509Certificate cert)
+        throws CertificateException, IOException, CertPathValidatorException;
+
+    /**
+     * Creates and returns a copy of this object
+     */
+    public Object clone();
+
+    /**
+     * Returns a boolean flag indicating if the state is initial
+     * (just starting)
+     *
+     * @return boolean flag indicating if the state is initial (just starting)
+     */
+    public boolean isInitial();
+
+    /**
+     * Returns a boolean flag indicating if a key lacking necessary key
+     * algorithm parameters has been encountered.
+     *
+     * @return boolean flag indicating if key lacking parameters encountered.
+     */
+    public boolean keyParamsNeeded();
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java
new file mode 100644
index 0000000..3950760
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.PublicKey;
+import java.security.cert.*;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.PKIXReason;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.provider.certpath.PKIX.BuilderParams;
+import static sun.security.x509.PKIXExtensions.*;
+import sun.security.util.Debug;
+
+/**
+ * This class is able to build certification paths in either the forward
+ * or reverse directions.
+ *
+ * <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. By default, this method will
+     * attempt to build in the forward direction. In order to build in the
+     * reverse direction, the caller needs to pass in an instance of
+     * SunCertPathBuilderParameters with the buildForward flag set to false.
+     *
+     * <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 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 {
+            if (buildParams.buildForward()) {
+                buildForward(adjList, certPathList, searchAllCertStores);
+            } else {
+                buildReverse(adjList, certPathList);
+            }
+        } 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 reverse method.
+     */
+    private void buildReverse(List<List<Vertex>> adjacencyList,
+                              LinkedList<X509Certificate> certPathList)
+        throws GeneralSecurityException, IOException
+    {
+        if (debug != null) {
+            debug.println("SunCertPathBuilder.buildReverse()...");
+            debug.println("SunCertPathBuilder.buildReverse() InitialPolicies: "
+                + buildParams.initialPolicies());
+        }
+
+        ReverseState currentState = new ReverseState();
+        /* Initialize adjacency list */
+        adjacencyList.clear();
+        adjacencyList.add(new LinkedList<Vertex>());
+
+        /*
+         * Perform a search using each trust anchor, until a valid
+         * path is found
+         */
+        Iterator<TrustAnchor> iter = buildParams.trustAnchors().iterator();
+        while (iter.hasNext()) {
+            TrustAnchor anchor = iter.next();
+
+            /* check if anchor satisfies target constraints */
+            if (anchorIsTarget(anchor, buildParams.targetCertConstraints())) {
+                this.trustAnchor = anchor;
+                this.pathCompleted = true;
+                this.finalPublicKey = anchor.getTrustedCert().getPublicKey();
+                break;
+            }
+
+            // skip anchor if it contains a DSA key with no DSA params
+            X509Certificate trustedCert = anchor.getTrustedCert();
+            PublicKey pubKey = trustedCert != null ? trustedCert.getPublicKey()
+                                                   : anchor.getCAPublicKey();
+
+            if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) {
+                continue;
+            }
+
+            /* Initialize current state */
+            currentState.initState(buildParams);
+            currentState.updateState(anchor, buildParams);
+
+            currentState.algorithmChecker = new AlgorithmChecker(anchor);
+            currentState.untrustedChecker = new UntrustedChecker();
+            try {
+                depthFirstSearchReverse(null, currentState,
+                                        new ReverseBuilder(buildParams),
+                                        adjacencyList, certPathList);
+            } catch (GeneralSecurityException | IOException e) {
+                // continue on error if more anchors to try
+                if (iter.hasNext())
+                    continue;
+                else
+                    throw e;
+            }
+
+            // break out of loop if search is successful
+            if (pathCompleted) {
+                break;
+            }
+        }
+
+        if (debug != null) {
+            debug.println("SunCertPathBuilder.buildReverse() returned from "
+                + "depthFirstSearchReverse()");
+            debug.println("SunCertPathBuilder.buildReverse() "
+                + "certPathList.size: " + certPathList.size());
+        }
+    }
+
+    /*
+     * 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);
+            }
+        }
+    }
+
+    /*
+     * This method performs a depth first search for a certification
+     * path while building reverse 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 depthFirstSearchReverse(X500Principal dN,
+                                         ReverseState currentState,
+                                         ReverseBuilder builder,
+                                         List<List<Vertex>> adjList,
+                                         LinkedList<X509Certificate> cpList)
+        throws GeneralSecurityException, IOException
+    {
+        if (debug != null)
+            debug.println("SunCertPathBuilder.depthFirstSearchReverse(" + dN
+                + ", " + currentState.toString() + ")");
+
+        /*
+         * Find all the certificates issued by 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.depthFirstSearchReverse(): "
+                + "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 depthFirstSearchReverse()
+         * recursively for each good cert.
+         */
+        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.
+             */
+            ReverseState nextState = (ReverseState) currentState.clone();
+            X509Certificate cert = vertex.getCertificate();
+            try {
+                builder.verifyCert(cert, nextState, cpList);
+            } catch (GeneralSecurityException gse) {
+                if (debug != null)
+                    debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
+                        + ": validation failed: " + gse);
+                vertex.setThrowable(gse);
+                continue;
+            }
+
+            /*
+             * Certificate is good, add it to the path (if it isn't a
+             * self-signed cert) and update state
+             */
+            if (!currentState.isInitial())
+                builder.addCertToPath(cert, cpList);
+            // save trust anchor
+            this.trustAnchor = currentState.trustAnchor;
+
+            /*
+             * Check if path is completed, return ASAP if so.
+             */
+            if (builder.isPathCompleted(cert)) {
+                if (debug != null)
+                    debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
+                        + ": path completed!");
+                pathCompleted = true;
+
+                PolicyNodeImpl rootNode = nextState.rootNode;
+
+                if (rootNode == null)
+                    policyTreeResult = null;
+                else {
+                    policyTreeResult = rootNode.copyTree();
+                    ((PolicyNodeImpl)policyTreeResult).setImmutable();
+                }
+
+                /*
+                 * Extract and save the final target public key
+                 */
+                finalPublicKey = cert.getPublicKey();
+                if (PKIX.isDSAPublicKeyWithoutParams(finalPublicKey)) {
+                    finalPublicKey =
+                        BasicChecker.makeInheritedParamsKey
+                            (finalPublicKey, currentState.pubKey);
+                }
+
+                return;
+            }
+
+            /* 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 */
+            depthFirstSearchReverse(cert.getSubjectX500Principal(), 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.depthFirstSearchReverse()"
+                        + ": backtracking");
+                if (!currentState.isInitial())
+                    builder.removeFinalCertFromPath(cpList);
+            }
+        }
+        if (debug != null)
+            debug.println("SunCertPathBuilder.depthFirstSearchReverse() all "
+                + "certs in this adjacency list checked");
+    }
+
+    /*
+     * 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;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderException.java b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderException.java
new file mode 100644
index 0000000..8ba440d
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderException.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.util.List;
+import java.security.cert.CertPathBuilderException;
+
+/**
+ * This is a subclass of the generic <code>CertPathBuilderException</code>.
+ * It contains an adjacency list with information regarding the unsuccessful
+ * paths that the SunCertPathBuilder tried.
+ *
+ * @since       1.4
+ * @author      Sean Mullan
+ * @see         CertPathBuilderException
+ */
+public class SunCertPathBuilderException extends CertPathBuilderException {
+
+    private static final long serialVersionUID = -7814288414129264709L;
+
+    /**
+     * @serial
+     */
+    private transient AdjacencyList adjList;
+
+    /**
+     * Constructs a <code>SunCertPathBuilderException</code> with
+     * <code>null</code> as its detail message.
+     */
+    public SunCertPathBuilderException() {
+        super();
+    }
+
+    /**
+     * Constructs a <code>SunCertPathBuilderException</code> with the specified
+     * detail message. A detail message is a <code>String</code> that
+     * describes this particular exception.
+     *
+     * @param msg the detail message
+     */
+    public SunCertPathBuilderException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>SunCertPathBuilderException</code> that wraps the
+     * specified throwable. This allows any exception to be converted into a
+     * <code>SunCertPathBuilderException</code>, while retaining information
+     * about the cause, which may be useful for debugging. The detail message is
+     * set to (<code>cause==null ? null : cause.toString()</code>) (which
+     * typically contains the class and detail message of cause).
+     *
+     * @param cause the cause (which is saved for later retrieval by the
+     * {@link #getCause getCause()} method). (A <code>null</code> value is
+     * permitted, and indicates that the cause is nonexistent or unknown.)
+     * root cause.
+     */
+    public SunCertPathBuilderException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates a <code>SunCertPathBuilderException</code> with the specified
+     * detail message and cause.
+     *
+     * @param msg the detail message
+     * @param cause the cause
+     */
+    public SunCertPathBuilderException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+    /**
+     * Creates a <code>SunCertPathBuilderException</code> withe the specified
+     * detail message and adjacency list.
+     *
+     * @param msg the detail message
+     * @param adjList the adjacency list
+     */
+    SunCertPathBuilderException(String msg, AdjacencyList adjList) {
+        this(msg);
+        this.adjList = adjList;
+    }
+
+    /**
+     * Creates a <code>SunCertPathBuilderException</code> with the specified
+     * detail message, cause, and adjacency list.
+     *
+     * @param msg the detail message
+     * @param cause the throwable that occurred
+     * @param adjList Adjacency list
+     */
+    SunCertPathBuilderException(String msg, Throwable cause,
+        AdjacencyList adjList)
+    {
+        this(msg, cause);
+        this.adjList = adjList;
+    }
+
+    /**
+     * Returns the adjacency list containing information about the build.
+     *
+     * @return the adjacency list containing information about the build
+     */
+    public AdjacencyList getAdjacencyList() {
+        return adjList;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java
new file mode 100644
index 0000000..186e252
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.*;
+import java.util.Set;
+
+/**
+ * This class specifies the set of parameters used as input for the Sun
+ * certification path build algorithm. It is identical to PKIXBuilderParameters
+ * with the addition of a <code>buildForward</code> parameter which allows
+ * the caller to specify whether or not the path should be constructed in
+ * the forward direction.
+ *
+ * The default for the <code>buildForward</code> parameter is
+ * true, which means that the build algorithm should construct paths
+ * from the target subject back to the trusted anchor.
+ *
+ * @since       1.4
+ * @author      Sean Mullan
+ * @author      Yassir Elley
+ */
+public class SunCertPathBuilderParameters extends PKIXBuilderParameters {
+
+    private boolean buildForward = true;
+
+    /**
+     * Creates an instance of <code>SunCertPathBuilderParameters</code> with the
+     * specified parameter values.
+     *
+     * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s
+     * @param targetConstraints a <code>CertSelector</code> specifying the
+     * constraints on the target certificate
+     * @throws InvalidAlgorithmParameterException if the specified
+     * <code>Set</code> is empty <code>(trustAnchors.isEmpty() == true)</code>
+     * @throws NullPointerException if the specified <code>Set</code> is
+     * <code>null</code>
+     * @throws ClassCastException if any of the elements in the <code>Set</code>
+     * are not of type <code>java.security.cert.TrustAnchor</code>
+     */
+    public SunCertPathBuilderParameters(Set<TrustAnchor> trustAnchors,
+        CertSelector targetConstraints) throws InvalidAlgorithmParameterException
+    {
+        super(trustAnchors, targetConstraints);
+        setBuildForward(true);
+    }
+
+    /**
+     * Creates an instance of <code>SunCertPathBuilderParameters</code> that
+     * uses the specified <code>KeyStore</code> to populate the set
+     * of most-trusted CA certificates.
+     *
+     * @param keystore A keystore from which the set of most-trusted
+     * CA certificates will be populated.
+     * @param targetConstraints a <code>CertSelector</code> specifying the
+     * constraints on the target certificate
+     * @throws KeyStoreException if the keystore has not been initialized.
+     * @throws InvalidAlgorithmParameterException if the keystore does
+     * not contain at least one trusted certificate entry
+     * @throws NullPointerException if the keystore is <code>null</code>
+     */
+    public SunCertPathBuilderParameters(KeyStore keystore,
+        CertSelector targetConstraints)
+        throws KeyStoreException, InvalidAlgorithmParameterException
+    {
+        super(keystore, targetConstraints);
+        setBuildForward(true);
+    }
+
+    /**
+     * Returns the value of the buildForward flag.
+     *
+     * @return the value of the buildForward flag
+     */
+    public boolean getBuildForward() {
+        return this.buildForward;
+    }
+
+    /**
+     * Sets the value of the buildForward flag. If true, paths
+     * are built from the target subject to the trusted anchor.
+     * If false, paths are built from the trusted anchor to the
+     * target subject. The default value if not specified is true.
+     *
+     * @param buildForward the value of the buildForward flag
+     */
+    public void setBuildForward(boolean buildForward) {
+        this.buildForward = buildForward;
+    }
+
+    /**
+     * Returns a formatted string describing the parameters.
+     *
+     * @return a formatted string describing the parameters.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[\n");
+        sb.append(super.toString());
+        sb.append("  Build Forward Flag: " + String.valueOf(buildForward) + "\n");
+        sb.append("]\n");
+        return sb.toString();
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderResult.java b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderResult.java
new file mode 100644
index 0000000..5924190
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderResult.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import sun.security.util.Debug;
+import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.PKIXCertPathBuilderResult;
+import java.security.cert.PolicyNode;
+import java.security.cert.TrustAnchor;
+
+/**
+ * This class represents the result of a SunCertPathBuilder build.
+ * Since all paths returned by the SunCertPathProvider are PKIX validated
+ * the result contains the valid policy tree and subject public key returned
+ * by the algorithm. It also contains the trust anchor and debug information
+ * represented in the form of an adjacency list.
+ *
+ * @see PKIXCertPathBuilderResult
+ *
+ * @since       1.4
+ * @author      Sean Mullan
+ */
+//@@@ Note: this class is not in public API and access to adjacency list is
+//@@@ intended for debugging/replay of Sun PKIX CertPathBuilder implementation.
+
+public class SunCertPathBuilderResult extends PKIXCertPathBuilderResult {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    private AdjacencyList adjList;
+
+    /**
+     * Creates a SunCertPathBuilderResult instance.
+     *
+     * @param certPath the validated <code>CertPath</code>
+     * @param trustAnchor a <code>TrustAnchor</code> describing the CA that
+     * served as a trust anchor for the certification path
+     * @param policyTree the valid policy tree, or <code>null</code>
+     * if there are no valid policies
+     * @param subjectPublicKey the public key of the subject
+     * @param adjList an Adjacency list containing debug information
+     */
+    SunCertPathBuilderResult(CertPath certPath,
+        TrustAnchor trustAnchor, PolicyNode policyTree,
+        PublicKey subjectPublicKey, AdjacencyList adjList)
+    {
+        super(certPath, trustAnchor, policyTree, subjectPublicKey);
+        this.adjList = adjList;
+    }
+
+    /**
+     * Returns the adjacency list containing information about the build.
+     *
+     * @return The adjacency list containing information about the build.
+     */
+    public AdjacencyList getAdjacencyList() {
+        return adjList;
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/URICertStore.java b/ojluni/src/main/java/sun/security/provider/certpath/URICertStore.java
new file mode 100644
index 0000000..a5ec5d3
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/URICertStore.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URLConnection;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertSelector;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CertStoreSpi;
+import java.security.cert.CRLException;
+import java.security.cert.CRLSelector;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLSelector;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import sun.security.action.GetIntegerAction;
+import sun.security.x509.AccessDescription;
+import sun.security.x509.GeneralNameInterface;
+import sun.security.x509.URIName;
+import sun.security.util.Cache;
+import sun.security.util.Debug;
+
+/**
+ * A <code>CertStore</code> that retrieves <code>Certificates</code> or
+ * <code>CRL</code>s from a URI, for example, as specified in an X.509
+ * AuthorityInformationAccess or CRLDistributionPoint extension.
+ * <p>
+ * For CRLs, this implementation retrieves a single DER encoded CRL per URI.
+ * For Certificates, this implementation retrieves a single DER encoded CRL or
+ * a collection of Certificates encoded as a PKCS#7 "certs-only" CMS message.
+ * <p>
+ * This <code>CertStore</code> also implements Certificate/CRL caching.
+ * Currently, the cache is shared between all applications in the VM and uses a
+ * hardcoded policy. The cache has a maximum size of 185 entries, which are held
+ * by SoftReferences. A request will be satisfied from the cache if we last
+ * checked for an update within CHECK_INTERVAL (last 30 seconds). Otherwise,
+ * we open an URLConnection to download the Certificate(s)/CRL using an
+ * If-Modified-Since request (HTTP) if possible. Note that both positive and
+ * negative responses are cached, i.e. if we are unable to open the connection
+ * or the Certificate(s)/CRL cannot be parsed, we remember this result and
+ * additional calls during the CHECK_INTERVAL period do not try to open another
+ * connection.
+ * <p>
+ * The URICertStore is not currently a standard CertStore type. We should
+ * consider adding a standard "URI" CertStore type.
+ *
+ * @author Andreas Sterbenz
+ * @author Sean Mullan
+ * @since 7.0
+ */
+class URICertStore extends CertStoreSpi {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    // interval between checks for update of cached Certificates/CRLs
+    // (30 seconds)
+    private final static int CHECK_INTERVAL = 30 * 1000;
+
+    // size of the cache (see Cache class for sizing recommendations)
+    private final static int CACHE_SIZE = 185;
+
+    // X.509 certificate factory instance
+    private final CertificateFactory factory;
+
+    // cached Collection of X509Certificates (may be empty, never null)
+    private Collection<X509Certificate> certs = Collections.emptySet();
+
+    // cached X509CRL (may be null)
+    private X509CRL crl;
+
+    // time we last checked for an update
+    private long lastChecked;
+
+    // time server returned as last modified time stamp
+    // or 0 if not available
+    private long lastModified;
+
+    // the URI of this CertStore
+    private URI uri;
+
+    // true if URI is ldap
+    private boolean ldap = false;
+    private CertStoreHelper ldapHelper;
+    private CertStore ldapCertStore;
+    private String ldapPath;
+
+    // Default maximum connect timeout in milliseconds (15 seconds)
+    // allowed when downloading CRLs
+    private static final int DEFAULT_CRL_CONNECT_TIMEOUT = 15000;
+
+    /**
+     * Integer value indicating the connect timeout, in seconds, to be
+     * used for the CRL download. A timeout of zero is interpreted as
+     * an infinite timeout.
+     */
+    private static final int CRL_CONNECT_TIMEOUT = initializeTimeout();
+
+    /**
+     * Initialize the timeout length by getting the CRL timeout
+     * system property. If the property has not been set, or if its
+     * value is negative, set the timeout length to the default.
+     */
+    private static int initializeTimeout() {
+        Integer tmp = java.security.AccessController.doPrivileged(
+                new GetIntegerAction("com.sun.security.crl.timeout"));
+        if (tmp == null || tmp < 0) {
+            return DEFAULT_CRL_CONNECT_TIMEOUT;
+        }
+        // Convert to milliseconds, as the system property will be
+        // specified in seconds
+        return tmp * 1000;
+    }
+
+    /**
+     * Creates a URICertStore.
+     *
+     * @param parameters specifying the URI
+     */
+    URICertStore(CertStoreParameters params)
+        throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
+        super(params);
+        if (!(params instanceof URICertStoreParameters)) {
+            throw new InvalidAlgorithmParameterException
+                ("params must be instanceof URICertStoreParameters");
+        }
+        this.uri = ((URICertStoreParameters) params).uri;
+        // if ldap URI, use an LDAPCertStore to fetch certs and CRLs
+        if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals("ldap")) {
+            ldap = true;
+            ldapHelper = CertStoreHelper.getInstance("LDAP");
+            ldapCertStore = ldapHelper.getCertStore(uri);
+            ldapPath = uri.getPath();
+            // strip off leading '/'
+            if (ldapPath.charAt(0) == '/') {
+                ldapPath = ldapPath.substring(1);
+            }
+        }
+        try {
+            factory = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            throw new RuntimeException();
+        }
+    }
+
+    /**
+     * Returns a URI CertStore. This method consults a cache of
+     * CertStores (shared per JVM) using the URI as a key.
+     */
+    private static final Cache<URICertStoreParameters, CertStore>
+        certStoreCache = Cache.newSoftMemoryCache(CACHE_SIZE);
+    static synchronized CertStore getInstance(URICertStoreParameters params)
+        throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+        if (debug != null) {
+            debug.println("CertStore URI:" + params.uri);
+        }
+        CertStore ucs = certStoreCache.get(params);
+        if (ucs == null) {
+            ucs = new UCS(new URICertStore(params), null, "URI", params);
+            certStoreCache.put(params, ucs);
+        } else {
+            if (debug != null) {
+                debug.println("URICertStore.getInstance: cache hit");
+            }
+        }
+        return ucs;
+    }
+
+    /**
+     * Creates a CertStore from information included in the AccessDescription
+     * object of a certificate's Authority Information Access Extension.
+     */
+    static CertStore getInstance(AccessDescription ad) {
+        if (!ad.getAccessMethod().equals((Object)
+                AccessDescription.Ad_CAISSUERS_Id)) {
+            return null;
+        }
+        GeneralNameInterface gn = ad.getAccessLocation().getName();
+        if (!(gn instanceof URIName)) {
+            return null;
+        }
+        URI uri = ((URIName) gn).getURI();
+        try {
+            return URICertStore.getInstance
+                (new URICertStore.URICertStoreParameters(uri));
+        } catch (Exception ex) {
+            if (debug != null) {
+                debug.println("exception creating CertStore: " + ex);
+                ex.printStackTrace();
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>X509Certificate</code>s that
+     * match the specified selector. If no <code>X509Certificate</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     *
+     * @param selector a <code>CertSelector</code> used to select which
+     *  <code>X509Certificate</code>s should be returned. Specify
+     *  <code>null</code> to return all <code>X509Certificate</code>s.
+     * @return a <code>Collection</code> of <code>X509Certificate</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public synchronized Collection<X509Certificate> engineGetCertificates
+        (CertSelector selector) throws CertStoreException {
+
+        // if ldap URI we wrap the CertSelector in an LDAPCertSelector to
+        // avoid LDAP DN matching issues (see LDAPCertSelector for more info)
+        if (ldap) {
+            X509CertSelector xsel = (X509CertSelector) selector;
+            try {
+                xsel = ldapHelper.wrap(xsel, xsel.getSubject(), ldapPath);
+            } catch (IOException ioe) {
+                throw new CertStoreException(ioe);
+            }
+            // Fetch the certificates via LDAP. LDAPCertStore has its own
+            // caching mechanism, see the class description for more info.
+            // Safe cast since xsel is an X509 certificate selector.
+            return (Collection<X509Certificate>)
+                ldapCertStore.getCertificates(xsel);
+        }
+
+        // Return the Certificates for this entry. It returns the cached value
+        // if it is still current and fetches the Certificates otherwise.
+        // For the caching details, see the top of this class.
+        long time = System.currentTimeMillis();
+        if (time - lastChecked < CHECK_INTERVAL) {
+            if (debug != null) {
+                debug.println("Returning certificates from cache");
+            }
+            return getMatchingCerts(certs, selector);
+        }
+        lastChecked = time;
+        try {
+            URLConnection connection = uri.toURL().openConnection();
+            if (lastModified != 0) {
+                connection.setIfModifiedSince(lastModified);
+            }
+            long oldLastModified = lastModified;
+            try (InputStream in = connection.getInputStream()) {
+                lastModified = connection.getLastModified();
+                if (oldLastModified != 0) {
+                    if (oldLastModified == lastModified) {
+                        if (debug != null) {
+                            debug.println("Not modified, using cached copy");
+                        }
+                        return getMatchingCerts(certs, selector);
+                    } else if (connection instanceof HttpURLConnection) {
+                        // some proxy servers omit last modified
+                        HttpURLConnection hconn = (HttpURLConnection)connection;
+                        if (hconn.getResponseCode()
+                                    == HttpURLConnection.HTTP_NOT_MODIFIED) {
+                            if (debug != null) {
+                                debug.println("Not modified, using cached copy");
+                            }
+                            return getMatchingCerts(certs, selector);
+                        }
+                    }
+                }
+                if (debug != null) {
+                    debug.println("Downloading new certificates...");
+                }
+                // Safe cast since factory is an X.509 certificate factory
+                certs = (Collection<X509Certificate>)
+                    factory.generateCertificates(in);
+            }
+            return getMatchingCerts(certs, selector);
+        } catch (IOException | CertificateException e) {
+            if (debug != null) {
+                debug.println("Exception fetching certificates:");
+                e.printStackTrace();
+            }
+        }
+        // exception, forget previous values
+        lastModified = 0;
+        certs = Collections.emptySet();
+        return certs;
+    }
+
+    /**
+     * Iterates over the specified Collection of X509Certificates and
+     * returns only those that match the criteria specified in the
+     * CertSelector.
+     */
+    private static Collection<X509Certificate> getMatchingCerts
+        (Collection<X509Certificate> certs, CertSelector selector) {
+        // if selector not specified, all certs match
+        if (selector == null) {
+            return certs;
+        }
+        List<X509Certificate> matchedCerts = new ArrayList<>(certs.size());
+        for (X509Certificate cert : certs) {
+            if (selector.match(cert)) {
+                matchedCerts.add(cert);
+            }
+        }
+        return matchedCerts;
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>X509CRL</code>s that
+     * match the specified selector. If no <code>X509CRL</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     *
+     * @param selector A <code>CRLSelector</code> used to select which
+     *  <code>X509CRL</code>s should be returned. Specify <code>null</code>
+     *  to return all <code>X509CRL</code>s.
+     * @return A <code>Collection</code> of <code>X509CRL</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector)
+        throws CertStoreException {
+
+        // if ldap URI we wrap the CRLSelector in an LDAPCRLSelector to
+        // avoid LDAP DN matching issues (see LDAPCRLSelector for more info)
+        if (ldap) {
+            X509CRLSelector xsel = (X509CRLSelector) selector;
+            try {
+                xsel = ldapHelper.wrap(xsel, null, ldapPath);
+            } catch (IOException ioe) {
+                throw new CertStoreException(ioe);
+            }
+            // Fetch the CRLs via LDAP. LDAPCertStore has its own
+            // caching mechanism, see the class description for more info.
+            // Safe cast since xsel is an X509 certificate selector.
+            try {
+                return (Collection<X509CRL>) ldapCertStore.getCRLs(xsel);
+            } catch (CertStoreException cse) {
+                throw new PKIX.CertStoreTypeException("LDAP", cse);
+            }
+        }
+
+        // Return the CRLs for this entry. It returns the cached value
+        // if it is still current and fetches the CRLs otherwise.
+        // For the caching details, see the top of this class.
+        long time = System.currentTimeMillis();
+        if (time - lastChecked < CHECK_INTERVAL) {
+            if (debug != null) {
+                debug.println("Returning CRL from cache");
+            }
+            return getMatchingCRLs(crl, selector);
+        }
+        lastChecked = time;
+        try {
+            URLConnection connection = uri.toURL().openConnection();
+            if (lastModified != 0) {
+                connection.setIfModifiedSince(lastModified);
+            }
+            long oldLastModified = lastModified;
+            connection.setConnectTimeout(CRL_CONNECT_TIMEOUT);
+            try (InputStream in = connection.getInputStream()) {
+                lastModified = connection.getLastModified();
+                if (oldLastModified != 0) {
+                    if (oldLastModified == lastModified) {
+                        if (debug != null) {
+                            debug.println("Not modified, using cached copy");
+                        }
+                        return getMatchingCRLs(crl, selector);
+                    } else if (connection instanceof HttpURLConnection) {
+                        // some proxy servers omit last modified
+                        HttpURLConnection hconn = (HttpURLConnection)connection;
+                        if (hconn.getResponseCode()
+                                    == HttpURLConnection.HTTP_NOT_MODIFIED) {
+                            if (debug != null) {
+                                debug.println("Not modified, using cached copy");
+                            }
+                            return getMatchingCRLs(crl, selector);
+                        }
+                    }
+                }
+                if (debug != null) {
+                    debug.println("Downloading new CRL...");
+                }
+                crl = (X509CRL) factory.generateCRL(in);
+            }
+            return getMatchingCRLs(crl, selector);
+        } catch (IOException | CRLException e) {
+            if (debug != null) {
+                debug.println("Exception fetching CRL:");
+                e.printStackTrace();
+            }
+            // exception, forget previous values
+            lastModified = 0;
+            crl = null;
+            throw new PKIX.CertStoreTypeException("URI",
+                                                  new CertStoreException(e));
+        }
+    }
+
+    /**
+     * Checks if the specified X509CRL matches the criteria specified in the
+     * CRLSelector.
+     */
+    private static Collection<X509CRL> getMatchingCRLs
+        (X509CRL crl, CRLSelector selector) {
+        if (selector == null || (crl != null && selector.match(crl))) {
+            return Collections.singletonList(crl);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * CertStoreParameters for the URICertStore.
+     */
+    static class URICertStoreParameters implements CertStoreParameters {
+        private final URI uri;
+        private volatile int hashCode = 0;
+        URICertStoreParameters(URI uri) {
+            this.uri = uri;
+        }
+        @Override public boolean equals(Object obj) {
+            if (!(obj instanceof URICertStoreParameters)) {
+                return false;
+            }
+            URICertStoreParameters params = (URICertStoreParameters) obj;
+            return uri.equals(params.uri);
+        }
+        @Override public int hashCode() {
+            if (hashCode == 0) {
+                int result = 17;
+                result = 37*result + uri.hashCode();
+                hashCode = result;
+            }
+            return hashCode;
+        }
+        @Override public Object clone() {
+            try {
+                return super.clone();
+            } catch (CloneNotSupportedException e) {
+                /* Cannot happen */
+                throw new InternalError(e.toString(), e);
+            }
+        }
+    }
+
+    /**
+     * This class allows the URICertStore to be accessed as a CertStore.
+     */
+    private static class UCS extends CertStore {
+        protected UCS(CertStoreSpi spi, Provider p, String type,
+            CertStoreParameters params) {
+            super(spi, p, type, params);
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/UntrustedChecker.java b/ojluni/src/main/java/sun/security/provider/certpath/UntrustedChecker.java
old mode 100755
new mode 100644
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/Vertex.java b/ojluni/src/main/java/sun/security/provider/certpath/Vertex.java
new file mode 100644
index 0000000..e2503f0
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/Vertex.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import sun.security.util.Debug;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.SubjectKeyIdentifierExtension;
+import sun.security.x509.X509CertImpl;
+
+/*
+ * This class represents a vertex in the adjacency list. A
+ * vertex in the builder's view is just a distinguished name
+ * in the directory.  The Vertex contains a certificate
+ * along an attempted certification path, along with a pointer
+ * to a list of certificates that followed this one in various
+ * attempted certification paths.
+ *
+ * @author      Sean Mullan
+ * @since       1.4
+ */
+public class Vertex {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private X509Certificate cert;
+    private int index;
+    private Throwable throwable;
+
+    /**
+     * Constructor; creates vertex with index of -1
+     * Use setIndex method to set another index.
+     *
+     * @param cert X509Certificate associated with vertex
+     */
+    Vertex(X509Certificate cert) {
+        this.cert = cert;
+        this.index = -1;
+    }
+
+    /**
+     * return the certificate for this vertex
+     *
+     * @returns X509Certificate
+     */
+    public X509Certificate getCertificate() {
+        return cert;
+    }
+
+    /**
+     * get the index for this vertex, where the index is the row of the
+     * adjacency list that contains certificates that could follow this
+     * certificate.
+     *
+     * @returns int index for this vertex, or -1 if no following certificates.
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * set the index for this vertex, where the index is the row of the
+     * adjacency list that contains certificates that could follow this
+     * certificate.
+     *
+     * @param ndx int index for vertex, or -1 if no following certificates.
+     */
+    void setIndex(int ndx) {
+        index = ndx;
+    }
+
+    /**
+     * return the throwable associated with this vertex;
+     * returns null if none.
+     *
+     * @returns Throwable
+     */
+    public Throwable getThrowable() {
+        return throwable;
+    }
+
+    /**
+     * set throwable associated with this vertex; default value is null.
+     *
+     * @param throwable Throwable associated with this vertex
+     *                  (or null)
+     */
+    void setThrowable(Throwable throwable) {
+        this.throwable = throwable;
+    }
+
+    /**
+     * Return full string representation of vertex
+     *
+     * @returns String representation of vertex
+     */
+    @Override
+    public String toString() {
+        return certToString() + throwableToString() + indexToString();
+    }
+
+    /**
+     * Return string representation of this vertex's
+     * certificate information.
+     *
+     * @returns String representation of certificate info
+     */
+    public String certToString() {
+        StringBuilder sb = new StringBuilder();
+
+        X509CertImpl x509Cert = null;
+        try {
+            x509Cert = X509CertImpl.toImpl(cert);
+        } catch (CertificateException ce) {
+            if (debug != null) {
+                debug.println("Vertex.certToString() unexpected exception");
+                ce.printStackTrace();
+            }
+            return sb.toString();
+        }
+
+        sb.append("Issuer:     ").append
+                 (x509Cert.getIssuerX500Principal()).append("\n");
+        sb.append("Subject:    ").append
+                 (x509Cert.getSubjectX500Principal()).append("\n");
+        sb.append("SerialNum:  ").append
+                 (x509Cert.getSerialNumber().toString(16)).append("\n");
+        sb.append("Expires:    ").append
+                 (x509Cert.getNotAfter().toString()).append("\n");
+        boolean[] iUID = x509Cert.getIssuerUniqueID();
+        if (iUID != null) {
+            sb.append("IssuerUID:  ");
+            for (boolean b : iUID) {
+                sb.append(b ? 1 : 0);
+            }
+            sb.append("\n");
+        }
+        boolean[] sUID = x509Cert.getSubjectUniqueID();
+        if (sUID != null) {
+            sb.append("SubjectUID: ");
+            for (boolean b : sUID) {
+                sb.append(b ? 1 : 0);
+            }
+            sb.append("\n");
+        }
+        try {
+            SubjectKeyIdentifierExtension sKeyID =
+                x509Cert.getSubjectKeyIdentifierExtension();
+            if (sKeyID != null) {
+                KeyIdentifier keyID = sKeyID.get(
+                        SubjectKeyIdentifierExtension.KEY_ID);
+                sb.append("SubjKeyID:  ").append(keyID.toString());
+            }
+            AuthorityKeyIdentifierExtension aKeyID =
+                x509Cert.getAuthorityKeyIdentifierExtension();
+            if (aKeyID != null) {
+                KeyIdentifier keyID = (KeyIdentifier)aKeyID.get(
+                        AuthorityKeyIdentifierExtension.KEY_ID);
+                sb.append("AuthKeyID:  ").append(keyID.toString());
+            }
+        } catch (IOException e) {
+            if (debug != null) {
+                debug.println("Vertex.certToString() unexpected exception");
+                e.printStackTrace();
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * return Vertex throwable as String compatible with
+     * the way toString returns other information
+     *
+     * @returns String form of exception (or "none")
+     */
+    public String throwableToString() {
+        StringBuilder sb = new StringBuilder("Exception:  ");
+        if (throwable != null)
+            sb.append(throwable.toString());
+        else
+            sb.append("null");
+        sb.append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * return Vertex index as String compatible with
+     * the way other Vertex.xToString() methods display
+     * information.
+     *
+     * @returns String form of index as "Last cert?  [Yes/No]
+     */
+    public String moreToString() {
+        StringBuilder sb = new StringBuilder("Last cert?  ");
+        sb.append((index == -1) ? "Yes" : "No");
+        sb.append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * return Vertex index as String compatible with
+     * the way other Vertex.xToString() methods displays other information.
+     *
+     * @returns String form of index as "Index:     [numeric index]"
+     */
+    public String indexToString() {
+        return "Index:      " + index + "\n";
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/X509CertPath.java b/ojluni/src/main/java/sun/security/provider/certpath/X509CertPath.java
old mode 100755
new mode 100644
index 27465b5..f738b5f
--- a/ojluni/src/main/java/sun/security/provider/certpath/X509CertPath.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/X509CertPath.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
 
 package sun.security.provider.certpath;
 
-import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -34,9 +33,10 @@
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
 import java.security.cert.X509Certificate;
 import java.util.*;
-import java.security.cert.CertPath;
+
 import sun.security.pkcs.ContentInfo;
 import sun.security.pkcs.PKCS7;
 import sun.security.pkcs.SignerInfo;
@@ -45,7 +45,6 @@
 import sun.security.util.DerOutputStream;
 import sun.security.util.DerInputStream;
 
-
 /**
  * A {@link java.security.cert.CertPath CertPath} (certification path)
  * consisting exclusively of
@@ -84,7 +83,7 @@
     private static final Collection<String> encodingList;
 
     static {
-        List<String> list = new ArrayList<String>(2);
+        List<String> list = new ArrayList<>(2);
         list.add(PKIPATH_ENCODING);
         list.add(PKCS7_ENCODING);
         encodingList = Collections.unmodifiableCollection(list);
@@ -101,11 +100,18 @@
      * @exception CertificateException if <code>certs</code> contains an element
      *                      that is not an <code>X509Certificate</code>
      */
+    @SuppressWarnings("unchecked")
     public X509CertPath(List<? extends Certificate> certs) throws CertificateException {
         super("X.509");
 
         // Ensure that the List contains only X509Certificates
-        for (Object obj : (List<?>)certs) {
+        //
+        // Note; The certs parameter is not necessarily to be of Certificate
+        // for some old code. For compatibility, to make sure the exception
+        // is CertificateException, rather than ClassCastException, please
+        // don't use
+        //     for (Certificate obj : certs)
+        for (Object obj : certs) {
             if (obj instanceof X509Certificate == false) {
                 throw new CertificateException
                     ("List is not all X509Certificates: "
@@ -147,12 +153,15 @@
             throws CertificateException {
         super("X.509");
 
-        if (PKIPATH_ENCODING.equals(encoding)) {
-            certs = parsePKIPATH(is);
-        } else if (PKCS7_ENCODING.equals(encoding)) {
-            certs = parsePKCS7(is);
-        } else {
-            throw new CertificateException("unsupported encoding");
+        switch (encoding) {
+            case PKIPATH_ENCODING:
+                certs = parsePKIPATH(is);
+                break;
+            case PKCS7_ENCODING:
+                certs = parsePKCS7(is);
+                break;
+            default:
+                throw new CertificateException("unsupported encoding");
         }
     }
 
@@ -192,10 +201,8 @@
             return Collections.unmodifiableList(certList);
 
         } catch (IOException ioe) {
-            CertificateException ce = new CertificateException("IOException" +
-                " parsing PkiPath data: " + ioe);
-            ce.initCause(ioe);
-            throw ce;
+            throw new CertificateException("IOException parsing PkiPath data: "
+                    + ioe, ioe);
         }
     }
 
@@ -220,7 +227,7 @@
                 // Copy the entire input stream into an InputStream that does
                 // support mark
                 is = new ByteArrayInputStream(readAllBytes(is));
-            };
+            }
             PKCS7 pkcs7 = new PKCS7(is);
 
             X509Certificate[] certArray = pkcs7.getCertificates();
@@ -265,6 +272,7 @@
      * @return the encoded bytes
      * @exception CertificateEncodingException if an encoding error occurs
      */
+    @Override
     public byte[] getEncoded() throws CertificateEncodingException {
         // @@@ Should cache the encoded form
         return encodePKIPATH();
@@ -301,10 +309,8 @@
             return derout.toByteArray();
 
         } catch (IOException ioe) {
-           CertificateEncodingException ce = new CertificateEncodingException
-                ("IOException encoding PkiPath data: " + ioe);
-           ce.initCause(ioe);
-           throw ce;
+           throw new CertificateEncodingException("IOException encoding " +
+                   "PkiPath data: " + ioe, ioe);
         }
     }
 
@@ -337,14 +343,16 @@
      * @exception CertificateEncodingException if an encoding error occurs or
      *   the encoding requested is not supported
      */
+    @Override
     public byte[] getEncoded(String encoding)
             throws CertificateEncodingException {
-        if (PKIPATH_ENCODING.equals(encoding)) {
-            return encodePKIPATH();
-        } else if (PKCS7_ENCODING.equals(encoding)) {
-            return encodePKCS7();
-        } else {
-            throw new CertificateEncodingException("unsupported encoding");
+        switch (encoding) {
+            case PKIPATH_ENCODING:
+                return encodePKIPATH();
+            case PKCS7_ENCODING:
+                return encodePKCS7();
+            default:
+                throw new CertificateEncodingException("unsupported encoding");
         }
     }
 
@@ -370,6 +378,7 @@
      * @return an <code>Iterator</code> over the names of the supported
      *         encodings (as Strings)
      */
+    @Override
     public Iterator<String> getEncodings() {
         return getEncodingsStatic();
     }
@@ -381,6 +390,7 @@
      * @return an immutable <code>List</code> of <code>X509Certificate</code>s
      *         (may be empty, but not null)
      */
+    @Override
     public List<X509Certificate> getCertificates() {
         return certs;
     }
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/X509CertificatePair.java b/ojluni/src/main/java/sun/security/provider/certpath/X509CertificatePair.java
old mode 100755
new mode 100644
index 20d05f7..ea139dd
--- a/ojluni/src/main/java/sun/security/provider/certpath/X509CertificatePair.java
+++ b/ojluni/src/main/java/sun/security/provider/certpath/X509CertificatePair.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -79,7 +79,8 @@
     private X509Certificate reverse;
     private byte[] encoded;
 
-    private static final Cache cache = Cache.newSoftMemoryCache(750);
+    private static final Cache<Object, X509CertificatePair> cache
+        = Cache.newSoftMemoryCache(750);
 
     /**
      * Creates an empty instance of X509CertificatePair.
@@ -114,7 +115,7 @@
      *
      * For internal use only, external code should use generateCertificatePair.
      */
-    private X509CertificatePair(byte[] encoded)throws CertificateException {
+    private X509CertificatePair(byte[] encoded) throws CertificateException {
         try {
             parse(new DerValue(encoded));
             this.encoded = encoded;
@@ -138,7 +139,7 @@
     public static synchronized X509CertificatePair generateCertificatePair
             (byte[] encoded) throws CertificateException {
         Object key = new Cache.EqualByteArray(encoded);
-        X509CertificatePair pair = (X509CertificatePair)cache.get(key);
+        X509CertificatePair pair = cache.get(key);
         if (pair != null) {
             return pair;
         }
@@ -206,13 +207,14 @@
      *
      * @return A String describing the contents of the pair.
      */
+    @Override
     public String toString() {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         sb.append("X.509 Certificate Pair: [\n");
         if (forward != null)
-            sb.append("  Forward: " + forward + "\n");
+            sb.append("  Forward: ").append(forward).append("\n");
         if (reverse != null)
-            sb.append("  Reverse: " + reverse + "\n");
+            sb.append("  Reverse: ").append(reverse).append("\n");
         sb.append("]");
         return sb.toString();
     }
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ldap/LDAPCertStore.java b/ojluni/src/main/java/sun/security/provider/certpath/ldap/LDAPCertStore.java
new file mode 100644
index 0000000..5a24a69
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ldap/LDAPCertStore.java
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath.ldap;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URI;
+import java.util.*;
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NameNotFoundException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import java.security.*;
+import java.security.cert.Certificate;
+import java.security.cert.*;
+import javax.security.auth.x500.X500Principal;
+
+import sun.misc.HexDumpEncoder;
+import sun.security.provider.certpath.X509CertificatePair;
+import sun.security.util.Cache;
+import sun.security.util.Debug;
+import sun.security.x509.X500Name;
+import sun.security.action.GetBooleanAction;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * A <code>CertStore</code> that retrieves <code>Certificates</code> and
+ * <code>CRL</code>s from an LDAP directory, using the PKIX LDAP V2 Schema
+ * (RFC 2587):
+ * <a href="http://www.ietf.org/rfc/rfc2587.txt">
+ * http://www.ietf.org/rfc/rfc2587.txt</a>.
+ * <p>
+ * Before calling the {@link #engineGetCertificates engineGetCertificates} or
+ * {@link #engineGetCRLs engineGetCRLs} methods, the
+ * {@link #LDAPCertStore(CertStoreParameters)
+ * LDAPCertStore(CertStoreParameters)} constructor is called to create the
+ * <code>CertStore</code> and establish the DNS name and port of the LDAP
+ * server from which <code>Certificate</code>s and <code>CRL</code>s will be
+ * retrieved.
+ * <p>
+ * <b>Concurrent Access</b>
+ * <p>
+ * As described in the javadoc for <code>CertStoreSpi</code>, the
+ * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods
+ * must be thread-safe. That is, multiple threads may concurrently
+ * invoke these methods on a single <code>LDAPCertStore</code> object
+ * (or more than one) with no ill effects. This allows a
+ * <code>CertPathBuilder</code> to search for a CRL while simultaneously
+ * searching for further certificates, for instance.
+ * <p>
+ * This is achieved by adding the <code>synchronized</code> keyword to the
+ * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods.
+ * <p>
+ * This classes uses caching and requests multiple attributes at once to
+ * minimize LDAP round trips. The cache is associated with the CertStore
+ * instance. It uses soft references to hold the values to minimize impact
+ * on footprint and currently has a maximum size of 750 attributes and a
+ * 30 second default lifetime.
+ * <p>
+ * We always request CA certificates, cross certificate pairs, and ARLs in
+ * a single LDAP request when any one of them is needed. The reason is that
+ * we typically need all of them anyway and requesting them in one go can
+ * reduce the number of requests to a third. Even if we don't need them,
+ * these attributes are typically small enough not to cause a noticeable
+ * overhead. In addition, when the prefetchCRLs flag is true, we also request
+ * the full CRLs. It is currently false initially but set to true once any
+ * request for an ARL to the server returns an null value. The reason is
+ * that CRLs could be rather large but are rarely used. This implementation
+ * should improve performance in most cases.
+ *
+ * @see java.security.cert.CertStore
+ *
+ * @since       1.4
+ * @author      Steve Hanna
+ * @author      Andreas Sterbenz
+ */
+public final class LDAPCertStore extends CertStoreSpi {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+
+    private final static boolean DEBUG = false;
+
+    /**
+     * LDAP attribute identifiers.
+     */
+    private static final String USER_CERT = "userCertificate;binary";
+    private static final String CA_CERT = "cACertificate;binary";
+    private static final String CROSS_CERT = "crossCertificatePair;binary";
+    private static final String CRL = "certificateRevocationList;binary";
+    private static final String ARL = "authorityRevocationList;binary";
+    private static final String DELTA_CRL = "deltaRevocationList;binary";
+
+    // Constants for various empty values
+    private final static String[] STRING0 = new String[0];
+
+    private final static byte[][] BB0 = new byte[0][];
+
+    private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes();
+
+    // cache related constants
+    private final static int DEFAULT_CACHE_SIZE = 750;
+    private final static int DEFAULT_CACHE_LIFETIME = 30;
+
+    private final static int LIFETIME;
+
+    private final static String PROP_LIFETIME =
+                            "sun.security.certpath.ldap.cache.lifetime";
+
+    /*
+     * Internal system property, that when set to "true", disables the
+     * JNDI application resource files lookup to prevent recursion issues
+     * when validating signed JARs with LDAP URLs in certificates.
+     */
+    private final static String PROP_DISABLE_APP_RESOURCE_FILES =
+        "sun.security.certpath.ldap.disable.app.resource.files";
+
+    static {
+        String s = AccessController.doPrivileged(
+                                new GetPropertyAction(PROP_LIFETIME));
+        if (s != null) {
+            LIFETIME = Integer.parseInt(s); // throws NumberFormatException
+        } else {
+            LIFETIME = DEFAULT_CACHE_LIFETIME;
+        }
+    }
+
+    /**
+     * The CertificateFactory used to decode certificates from
+     * their binary stored form.
+     */
+    private CertificateFactory cf;
+    /**
+     * The JNDI directory context.
+     */
+    private DirContext ctx;
+
+    /**
+     * Flag indicating whether we should prefetch CRLs.
+     */
+    private boolean prefetchCRLs = false;
+
+    private final Cache<String, byte[][]> valueCache;
+
+    private int cacheHits = 0;
+    private int cacheMisses = 0;
+    private int requests = 0;
+
+    /**
+     * Creates a <code>CertStore</code> with the specified parameters.
+     * For this class, the parameters object must be an instance of
+     * <code>LDAPCertStoreParameters</code>.
+     *
+     * @param params the algorithm parameters
+     * @exception InvalidAlgorithmParameterException if params is not an
+     *   instance of <code>LDAPCertStoreParameters</code>
+     */
+    public LDAPCertStore(CertStoreParameters params)
+            throws InvalidAlgorithmParameterException {
+        super(params);
+        if (!(params instanceof LDAPCertStoreParameters))
+          throw new InvalidAlgorithmParameterException(
+            "parameters must be LDAPCertStoreParameters");
+
+        LDAPCertStoreParameters lparams = (LDAPCertStoreParameters) params;
+
+        // Create InitialDirContext needed to communicate with the server
+        createInitialDirContext(lparams.getServerName(), lparams.getPort());
+
+        // Create CertificateFactory for use later on
+        try {
+            cf = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            throw new InvalidAlgorithmParameterException(
+                "unable to create CertificateFactory for X.509");
+        }
+        if (LIFETIME == 0) {
+            valueCache = Cache.newNullCache();
+        } else if (LIFETIME < 0) {
+            valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE);
+        } else {
+            valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME);
+        }
+    }
+
+    /**
+     * Returns an LDAP CertStore. This method consults a cache of
+     * CertStores (shared per JVM) using the LDAP server/port as a key.
+     */
+    private static final Cache<LDAPCertStoreParameters, CertStore>
+        certStoreCache = Cache.newSoftMemoryCache(185);
+    static synchronized CertStore getInstance(LDAPCertStoreParameters params)
+        throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+        CertStore lcs = certStoreCache.get(params);
+        if (lcs == null) {
+            lcs = CertStore.getInstance("LDAP", params);
+            certStoreCache.put(params, lcs);
+        } else {
+            if (debug != null) {
+                debug.println("LDAPCertStore.getInstance: cache hit");
+            }
+        }
+        return lcs;
+    }
+
+    /**
+     * Create InitialDirContext.
+     *
+     * @param server Server DNS name hosting LDAP service
+     * @param port   Port at which server listens for requests
+     * @throws InvalidAlgorithmParameterException if creation fails
+     */
+    private void createInitialDirContext(String server, int port)
+            throws InvalidAlgorithmParameterException {
+        String url = "ldap://" + server + ":" + port;
+        Hashtable<String,Object> env = new Hashtable<>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY,
+                "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, url);
+
+        // If property is set to true, disable application resource file lookup.
+        boolean disableAppResourceFiles = AccessController.doPrivileged(
+            new GetBooleanAction(PROP_DISABLE_APP_RESOURCE_FILES));
+        if (disableAppResourceFiles) {
+            if (debug != null) {
+                debug.println("LDAPCertStore disabling app resource files");
+            }
+            env.put("com.sun.naming.disable.app.resource.files", "true");
+        }
+
+        try {
+            ctx = new InitialDirContext(env);
+            /*
+             * By default, follow referrals unless application has
+             * overridden property in an application resource file.
+             */
+            Hashtable<?,?> currentEnv = ctx.getEnvironment();
+            if (currentEnv.get(Context.REFERRAL) == null) {
+                ctx.addToEnvironment(Context.REFERRAL, "follow");
+            }
+        } catch (NamingException e) {
+            if (debug != null) {
+                debug.println("LDAPCertStore.engineInit about to throw "
+                    + "InvalidAlgorithmParameterException");
+                e.printStackTrace();
+            }
+            Exception ee = new InvalidAlgorithmParameterException
+                ("unable to create InitialDirContext using supplied parameters");
+            ee.initCause(e);
+            throw (InvalidAlgorithmParameterException)ee;
+        }
+    }
+
+    /**
+     * Private class encapsulating the actual LDAP operations and cache
+     * handling. Use:
+     *
+     *   LDAPRequest request = new LDAPRequest(dn);
+     *   request.addRequestedAttribute(CROSS_CERT);
+     *   request.addRequestedAttribute(CA_CERT);
+     *   byte[][] crossValues = request.getValues(CROSS_CERT);
+     *   byte[][] caValues = request.getValues(CA_CERT);
+     *
+     * At most one LDAP request is sent for each instance created. If all
+     * getValues() calls can be satisfied from the cache, no request
+     * is sent at all. If a request is sent, all requested attributes
+     * are always added to the cache irrespective of whether the getValues()
+     * method is called.
+     */
+    private class LDAPRequest {
+
+        private final String name;
+        private Map<String, byte[][]> valueMap;
+        private final List<String> requestedAttributes;
+
+        LDAPRequest(String name) {
+            this.name = name;
+            requestedAttributes = new ArrayList<>(5);
+        }
+
+        String getName() {
+            return name;
+        }
+
+        void addRequestedAttribute(String attrId) {
+            if (valueMap != null) {
+                throw new IllegalStateException("Request already sent");
+            }
+            requestedAttributes.add(attrId);
+        }
+
+        /**
+         * Gets one or more binary values from an attribute.
+         *
+         * @param name          the location holding the attribute
+         * @param attrId                the attribute identifier
+         * @return                      an array of binary values (byte arrays)
+         * @throws NamingException      if a naming exception occurs
+         */
+        byte[][] getValues(String attrId) throws NamingException {
+            if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) {
+                System.out.println("Cache hits: " + cacheHits + "; misses: "
+                        + cacheMisses);
+            }
+            String cacheKey = name + "|" + attrId;
+            byte[][] values = valueCache.get(cacheKey);
+            if (values != null) {
+                cacheHits++;
+                return values;
+            }
+            cacheMisses++;
+            Map<String, byte[][]> attrs = getValueMap();
+            values = attrs.get(attrId);
+            return values;
+        }
+
+        /**
+         * Get a map containing the values for this request. The first time
+         * this method is called on an object, the LDAP request is sent,
+         * the results parsed and added to a private map and also to the
+         * cache of this LDAPCertStore. Subsequent calls return the private
+         * map immediately.
+         *
+         * The map contains an entry for each requested attribute. The
+         * attribute name is the key, values are byte[][]. If there are no
+         * values for that attribute, values are byte[0][].
+         *
+         * @return                      the value Map
+         * @throws NamingException      if a naming exception occurs
+         */
+        private Map<String, byte[][]> getValueMap() throws NamingException {
+            if (valueMap != null) {
+                return valueMap;
+            }
+            if (DEBUG) {
+                System.out.println("Request: " + name + ":" + requestedAttributes);
+                requests++;
+                if (requests % 5 == 0) {
+                    System.out.println("LDAP requests: " + requests);
+                }
+            }
+            valueMap = new HashMap<>(8);
+            String[] attrIds = requestedAttributes.toArray(STRING0);
+            Attributes attrs;
+            try {
+                attrs = ctx.getAttributes(name, attrIds);
+            } catch (NameNotFoundException e) {
+                // name does not exist on this LDAP server
+                // treat same as not attributes found
+                attrs = EMPTY_ATTRIBUTES;
+            }
+            for (String attrId : requestedAttributes) {
+                Attribute attr = attrs.get(attrId);
+                byte[][] values = getAttributeValues(attr);
+                cacheAttribute(attrId, values);
+                valueMap.put(attrId, values);
+            }
+            return valueMap;
+        }
+
+        /**
+         * Add the values to the cache.
+         */
+        private void cacheAttribute(String attrId, byte[][] values) {
+            String cacheKey = name + "|" + attrId;
+            valueCache.put(cacheKey, values);
+        }
+
+        /**
+         * Get the values for the given attribute. If the attribute is null
+         * or does not contain any values, a zero length byte array is
+         * returned. NOTE that it is assumed that all values are byte arrays.
+         */
+        private byte[][] getAttributeValues(Attribute attr)
+                throws NamingException {
+            byte[][] values;
+            if (attr == null) {
+                values = BB0;
+            } else {
+                values = new byte[attr.size()][];
+                int i = 0;
+                NamingEnumeration<?> enum_ = attr.getAll();
+                while (enum_.hasMore()) {
+                    Object obj = enum_.next();
+                    if (debug != null) {
+                        if (obj instanceof String) {
+                            debug.println("LDAPCertStore.getAttrValues() "
+                                + "enum.next is a string!: " + obj);
+                        }
+                    }
+                    byte[] value = (byte[])obj;
+                    values[i++] = value;
+                }
+            }
+            return values;
+        }
+
+    }
+
+    /*
+     * Gets certificates from an attribute id and location in the LDAP
+     * directory. Returns a Collection containing only the Certificates that
+     * match the specified CertSelector.
+     *
+     * @param name the location holding the attribute
+     * @param id the attribute identifier
+     * @param sel a CertSelector that the Certificates must match
+     * @return a Collection of Certificates found
+     * @throws CertStoreException       if an exception occurs
+     */
+    private Collection<X509Certificate> getCertificates(LDAPRequest request,
+        String id, X509CertSelector sel) throws CertStoreException {
+
+        /* fetch encoded certs from storage */
+        byte[][] encodedCert;
+        try {
+            encodedCert = request.getValues(id);
+        } catch (NamingException namingEx) {
+            throw new CertStoreException(namingEx);
+        }
+
+        int n = encodedCert.length;
+        if (n == 0) {
+            return Collections.emptySet();
+        }
+
+        List<X509Certificate> certs = new ArrayList<>(n);
+        /* decode certs and check if they satisfy selector */
+        for (int i = 0; i < n; i++) {
+            ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]);
+            try {
+                Certificate cert = cf.generateCertificate(bais);
+                if (sel.match(cert)) {
+                  certs.add((X509Certificate)cert);
+                }
+            } catch (CertificateException e) {
+                if (debug != null) {
+                    debug.println("LDAPCertStore.getCertificates() encountered "
+                        + "exception while parsing cert, skipping the bad data: ");
+                    HexDumpEncoder encoder = new HexDumpEncoder();
+                    debug.println(
+                        "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]");
+                }
+            }
+        }
+
+        return certs;
+    }
+
+    /*
+     * Gets certificate pairs from an attribute id and location in the LDAP
+     * directory.
+     *
+     * @param name the location holding the attribute
+     * @param id the attribute identifier
+     * @return a Collection of X509CertificatePairs found
+     * @throws CertStoreException       if an exception occurs
+     */
+    private Collection<X509CertificatePair> getCertPairs(
+        LDAPRequest request, String id) throws CertStoreException {
+
+        /* fetch the encoded cert pairs from storage */
+        byte[][] encodedCertPair;
+        try {
+            encodedCertPair = request.getValues(id);
+        } catch (NamingException namingEx) {
+            throw new CertStoreException(namingEx);
+        }
+
+        int n = encodedCertPair.length;
+        if (n == 0) {
+            return Collections.emptySet();
+        }
+
+        List<X509CertificatePair> certPairs = new ArrayList<>(n);
+        /* decode each cert pair and add it to the Collection */
+        for (int i = 0; i < n; i++) {
+            try {
+                X509CertificatePair certPair =
+                    X509CertificatePair.generateCertificatePair(encodedCertPair[i]);
+                certPairs.add(certPair);
+            } catch (CertificateException e) {
+                if (debug != null) {
+                    debug.println(
+                        "LDAPCertStore.getCertPairs() encountered exception "
+                        + "while parsing cert, skipping the bad data: ");
+                    HexDumpEncoder encoder = new HexDumpEncoder();
+                    debug.println(
+                        "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]");
+                }
+            }
+        }
+
+        return certPairs;
+    }
+
+    /*
+     * Looks at certificate pairs stored in the crossCertificatePair attribute
+     * at the specified location in the LDAP directory. Returns a Collection
+     * containing all Certificates stored in the forward component that match
+     * the forward CertSelector and all Certificates stored in the reverse
+     * component that match the reverse CertSelector.
+     * <p>
+     * If either forward or reverse is null, all certificates from the
+     * corresponding component will be rejected.
+     *
+     * @param name the location to look in
+     * @param forward the forward CertSelector (or null)
+     * @param reverse the reverse CertSelector (or null)
+     * @return a Collection of Certificates found
+     * @throws CertStoreException       if an exception occurs
+     */
+    private Collection<X509Certificate> getMatchingCrossCerts(
+            LDAPRequest request, X509CertSelector forward,
+            X509CertSelector reverse)
+            throws CertStoreException {
+        // Get the cert pairs
+        Collection<X509CertificatePair> certPairs =
+                                getCertPairs(request, CROSS_CERT);
+
+        // Find Certificates that match and put them in a list
+        ArrayList<X509Certificate> matchingCerts = new ArrayList<>();
+        for (X509CertificatePair certPair : certPairs) {
+            X509Certificate cert;
+            if (forward != null) {
+                cert = certPair.getForward();
+                if ((cert != null) && forward.match(cert)) {
+                    matchingCerts.add(cert);
+                }
+            }
+            if (reverse != null) {
+                cert = certPair.getReverse();
+                if ((cert != null) && reverse.match(cert)) {
+                    matchingCerts.add(cert);
+                }
+            }
+        }
+        return matchingCerts;
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>Certificate</code>s that
+     * match the specified selector. If no <code>Certificate</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     * <p>
+     * It is not practical to search every entry in the LDAP database for
+     * matching <code>Certificate</code>s. Instead, the <code>CertSelector</code>
+     * is examined in order to determine where matching <code>Certificate</code>s
+     * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587).
+     * If the subject is specified, its directory entry is searched. If the
+     * issuer is specified, its directory entry is searched. If neither the
+     * subject nor the issuer are specified (or the selector is not an
+     * <code>X509CertSelector</code>), a <code>CertStoreException</code> is
+     * thrown.
+     *
+     * @param selector a <code>CertSelector</code> used to select which
+     *  <code>Certificate</code>s should be returned.
+     * @return a <code>Collection</code> of <code>Certificate</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    public synchronized Collection<X509Certificate> engineGetCertificates
+            (CertSelector selector) throws CertStoreException {
+        if (debug != null) {
+            debug.println("LDAPCertStore.engineGetCertificates() selector: "
+                + String.valueOf(selector));
+        }
+
+        if (selector == null) {
+            selector = new X509CertSelector();
+        }
+        if (!(selector instanceof X509CertSelector)) {
+            throw new CertStoreException("LDAPCertStore needs an X509CertSelector " +
+                                         "to find certs");
+        }
+        X509CertSelector xsel = (X509CertSelector) selector;
+        int basicConstraints = xsel.getBasicConstraints();
+        String subject = xsel.getSubjectAsString();
+        String issuer = xsel.getIssuerAsString();
+        HashSet<X509Certificate> certs = new HashSet<>();
+        if (debug != null) {
+            debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: "
+                + basicConstraints);
+        }
+
+        // basicConstraints:
+        // -2: only EE certs accepted
+        // -1: no check is done
+        //  0: any CA certificate accepted
+        // >1: certificate's basicConstraints extension pathlen must match
+        if (subject != null) {
+            if (debug != null) {
+                debug.println("LDAPCertStore.engineGetCertificates() "
+                    + "subject is not null");
+            }
+            LDAPRequest request = new LDAPRequest(subject);
+            if (basicConstraints > -2) {
+                request.addRequestedAttribute(CROSS_CERT);
+                request.addRequestedAttribute(CA_CERT);
+                request.addRequestedAttribute(ARL);
+                if (prefetchCRLs) {
+                    request.addRequestedAttribute(CRL);
+                }
+            }
+            if (basicConstraints < 0) {
+                request.addRequestedAttribute(USER_CERT);
+            }
+
+            if (basicConstraints > -2) {
+                certs.addAll(getMatchingCrossCerts(request, xsel, null));
+                if (debug != null) {
+                    debug.println("LDAPCertStore.engineGetCertificates() after "
+                        + "getMatchingCrossCerts(subject,xsel,null),certs.size(): "
+                        + certs.size());
+                }
+                certs.addAll(getCertificates(request, CA_CERT, xsel));
+                if (debug != null) {
+                    debug.println("LDAPCertStore.engineGetCertificates() after "
+                        + "getCertificates(subject,CA_CERT,xsel),certs.size(): "
+                        + certs.size());
+                }
+            }
+            if (basicConstraints < 0) {
+                certs.addAll(getCertificates(request, USER_CERT, xsel));
+                if (debug != null) {
+                    debug.println("LDAPCertStore.engineGetCertificates() after "
+                        + "getCertificates(subject,USER_CERT, xsel),certs.size(): "
+                        + certs.size());
+                }
+            }
+        } else {
+            if (debug != null) {
+                debug.println
+                    ("LDAPCertStore.engineGetCertificates() subject is null");
+            }
+            if (basicConstraints == -2) {
+                throw new CertStoreException("need subject to find EE certs");
+            }
+            if (issuer == null) {
+                throw new CertStoreException("need subject or issuer to find certs");
+            }
+        }
+        if (debug != null) {
+            debug.println("LDAPCertStore.engineGetCertificates() about to "
+                + "getMatchingCrossCerts...");
+        }
+        if ((issuer != null) && (basicConstraints > -2)) {
+            LDAPRequest request = new LDAPRequest(issuer);
+            request.addRequestedAttribute(CROSS_CERT);
+            request.addRequestedAttribute(CA_CERT);
+            request.addRequestedAttribute(ARL);
+            if (prefetchCRLs) {
+                request.addRequestedAttribute(CRL);
+            }
+
+            certs.addAll(getMatchingCrossCerts(request, null, xsel));
+            if (debug != null) {
+                debug.println("LDAPCertStore.engineGetCertificates() after "
+                    + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): "
+                    + certs.size());
+            }
+            certs.addAll(getCertificates(request, CA_CERT, xsel));
+            if (debug != null) {
+                debug.println("LDAPCertStore.engineGetCertificates() after "
+                    + "getCertificates(issuer,CA_CERT,xsel),certs.size(): "
+                    + certs.size());
+            }
+        }
+        if (debug != null) {
+            debug.println("LDAPCertStore.engineGetCertificates() returning certs");
+        }
+        return certs;
+    }
+
+    /*
+     * Gets CRLs from an attribute id and location in the LDAP directory.
+     * Returns a Collection containing only the CRLs that match the
+     * specified CRLSelector.
+     *
+     * @param name the location holding the attribute
+     * @param id the attribute identifier
+     * @param sel a CRLSelector that the CRLs must match
+     * @return a Collection of CRLs found
+     * @throws CertStoreException       if an exception occurs
+     */
+    private Collection<X509CRL> getCRLs(LDAPRequest request, String id,
+            X509CRLSelector sel) throws CertStoreException {
+
+        /* fetch the encoded crls from storage */
+        byte[][] encodedCRL;
+        try {
+            encodedCRL = request.getValues(id);
+        } catch (NamingException namingEx) {
+            throw new CertStoreException(namingEx);
+        }
+
+        int n = encodedCRL.length;
+        if (n == 0) {
+            return Collections.emptySet();
+        }
+
+        List<X509CRL> crls = new ArrayList<>(n);
+        /* decode each crl and check if it matches selector */
+        for (int i = 0; i < n; i++) {
+            try {
+                CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i]));
+                if (sel.match(crl)) {
+                    crls.add((X509CRL)crl);
+                }
+            } catch (CRLException e) {
+                if (debug != null) {
+                    debug.println("LDAPCertStore.getCRLs() encountered exception"
+                        + " while parsing CRL, skipping the bad data: ");
+                    HexDumpEncoder encoder = new HexDumpEncoder();
+                    debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]");
+                }
+            }
+        }
+
+        return crls;
+    }
+
+    /**
+     * Returns a <code>Collection</code> of <code>CRL</code>s that
+     * match the specified selector. If no <code>CRL</code>s
+     * match the selector, an empty <code>Collection</code> will be returned.
+     * <p>
+     * It is not practical to search every entry in the LDAP database for
+     * matching <code>CRL</code>s. Instead, the <code>CRLSelector</code>
+     * is examined in order to determine where matching <code>CRL</code>s
+     * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587).
+     * If issuerNames or certChecking are specified, the issuer's directory
+     * entry is searched. If neither issuerNames or certChecking are specified
+     * (or the selector is not an <code>X509CRLSelector</code>), a
+     * <code>CertStoreException</code> is thrown.
+     *
+     * @param selector A <code>CRLSelector</code> used to select which
+     *  <code>CRL</code>s should be returned. Specify <code>null</code>
+     *  to return all <code>CRL</code>s.
+     * @return A <code>Collection</code> of <code>CRL</code>s that
+     *         match the specified selector
+     * @throws CertStoreException if an exception occurs
+     */
+    public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector)
+            throws CertStoreException {
+        if (debug != null) {
+            debug.println("LDAPCertStore.engineGetCRLs() selector: "
+                + selector);
+        }
+        // Set up selector and collection to hold CRLs
+        if (selector == null) {
+            selector = new X509CRLSelector();
+        }
+        if (!(selector instanceof X509CRLSelector)) {
+            throw new CertStoreException("need X509CRLSelector to find CRLs");
+        }
+        X509CRLSelector xsel = (X509CRLSelector) selector;
+        HashSet<X509CRL> crls = new HashSet<>();
+
+        // Look in directory entry for issuer of cert we're checking.
+        Collection<Object> issuerNames;
+        X509Certificate certChecking = xsel.getCertificateChecking();
+        if (certChecking != null) {
+            issuerNames = new HashSet<>();
+            X500Principal issuer = certChecking.getIssuerX500Principal();
+            issuerNames.add(issuer.getName(X500Principal.RFC2253));
+        } else {
+            // But if we don't know which cert we're checking, try the directory
+            // entries of all acceptable CRL issuers
+            issuerNames = xsel.getIssuerNames();
+            if (issuerNames == null) {
+                throw new CertStoreException("need issuerNames or certChecking to "
+                    + "find CRLs");
+            }
+        }
+        for (Object nameObject : issuerNames) {
+            String issuerName;
+            if (nameObject instanceof byte[]) {
+                try {
+                    X500Principal issuer = new X500Principal((byte[])nameObject);
+                    issuerName = issuer.getName(X500Principal.RFC2253);
+                } catch (IllegalArgumentException e) {
+                    continue;
+                }
+            } else {
+                issuerName = (String)nameObject;
+            }
+            // If all we want is CA certs, try to get the (probably shorter) ARL
+            Collection<X509CRL> entryCRLs = Collections.emptySet();
+            if (certChecking == null || certChecking.getBasicConstraints() != -1) {
+                LDAPRequest request = new LDAPRequest(issuerName);
+                request.addRequestedAttribute(CROSS_CERT);
+                request.addRequestedAttribute(CA_CERT);
+                request.addRequestedAttribute(ARL);
+                if (prefetchCRLs) {
+                    request.addRequestedAttribute(CRL);
+                }
+                try {
+                    entryCRLs = getCRLs(request, ARL, xsel);
+                    if (entryCRLs.isEmpty()) {
+                        // no ARLs found. We assume that means that there are
+                        // no ARLs on this server at all and prefetch the CRLs.
+                        prefetchCRLs = true;
+                    } else {
+                        crls.addAll(entryCRLs);
+                    }
+                } catch (CertStoreException e) {
+                    if (debug != null) {
+                        debug.println("LDAPCertStore.engineGetCRLs non-fatal error "
+                            + "retrieving ARLs:" + e);
+                        e.printStackTrace();
+                    }
+                }
+            }
+            // Otherwise, get the CRL
+            // if certChecking is null, we don't know if we should look in ARL or CRL
+            // attribute, so check both for matching CRLs.
+            if (entryCRLs.isEmpty() || certChecking == null) {
+                LDAPRequest request = new LDAPRequest(issuerName);
+                request.addRequestedAttribute(CRL);
+                entryCRLs = getCRLs(request, CRL, xsel);
+                crls.addAll(entryCRLs);
+            }
+        }
+        return crls;
+    }
+
+    // converts an LDAP URI into LDAPCertStoreParameters
+    static LDAPCertStoreParameters getParameters(URI uri) {
+        String host = uri.getHost();
+        if (host == null) {
+            return new SunLDAPCertStoreParameters();
+        } else {
+            int port = uri.getPort();
+            return (port == -1
+                    ? new SunLDAPCertStoreParameters(host)
+                    : new SunLDAPCertStoreParameters(host, port));
+        }
+    }
+
+    /*
+     * Subclass of LDAPCertStoreParameters with overridden equals/hashCode
+     * methods. This is necessary because the parameters are used as
+     * keys in the LDAPCertStore cache.
+     */
+    private static class SunLDAPCertStoreParameters
+        extends LDAPCertStoreParameters {
+
+        private volatile int hashCode = 0;
+
+        SunLDAPCertStoreParameters(String serverName, int port) {
+            super(serverName, port);
+        }
+        SunLDAPCertStoreParameters(String serverName) {
+            super(serverName);
+        }
+        SunLDAPCertStoreParameters() {
+            super();
+        }
+        public boolean equals(Object obj) {
+            if (!(obj instanceof LDAPCertStoreParameters)) {
+                return false;
+            }
+            LDAPCertStoreParameters params = (LDAPCertStoreParameters) obj;
+            return (getPort() == params.getPort() &&
+                    getServerName().equalsIgnoreCase(params.getServerName()));
+        }
+        public int hashCode() {
+            if (hashCode == 0) {
+                int result = 17;
+                result = 37*result + getPort();
+                result = 37*result +
+                    getServerName().toLowerCase(Locale.ENGLISH).hashCode();
+                hashCode = result;
+            }
+            return hashCode;
+        }
+    }
+
+    /*
+     * This inner class wraps an existing X509CertSelector and adds
+     * additional criteria to match on when the certificate's subject is
+     * different than the LDAP Distinguished Name entry. The LDAPCertStore
+     * implementation uses the subject DN as the directory entry for
+     * looking up certificates. This can be problematic if the certificates
+     * that you want to fetch have a different subject DN than the entry
+     * where they are stored. You could set the selector's subject to the
+     * LDAP DN entry, but then the resulting match would fail to find the
+     * desired certificates because the subject DNs would not match. This
+     * class avoids that problem by introducing a certSubject which should
+     * be set to the certificate's subject DN when it is different than
+     * the LDAP DN.
+     */
+    static class LDAPCertSelector extends X509CertSelector {
+
+        private X500Principal certSubject;
+        private X509CertSelector selector;
+        private X500Principal subject;
+
+        /**
+         * Creates an LDAPCertSelector.
+         *
+         * @param selector the X509CertSelector to wrap
+         * @param certSubject the subject DN of the certificate that you want
+         *      to retrieve via LDAP
+         * @param ldapDN the LDAP DN where the certificate is stored
+         */
+        LDAPCertSelector(X509CertSelector selector, X500Principal certSubject,
+            String ldapDN) throws IOException {
+            this.selector = selector == null ? new X509CertSelector() : selector;
+            this.certSubject = certSubject;
+            this.subject = new X500Name(ldapDN).asX500Principal();
+        }
+
+        // we only override the get (accessor methods) since the set methods
+        // will not be invoked by the code that uses this LDAPCertSelector.
+        public X509Certificate getCertificate() {
+            return selector.getCertificate();
+        }
+        public BigInteger getSerialNumber() {
+            return selector.getSerialNumber();
+        }
+        public X500Principal getIssuer() {
+            return selector.getIssuer();
+        }
+        public String getIssuerAsString() {
+            return selector.getIssuerAsString();
+        }
+        public byte[] getIssuerAsBytes() throws IOException {
+            return selector.getIssuerAsBytes();
+        }
+        public X500Principal getSubject() {
+            // return the ldap DN
+            return subject;
+        }
+        public String getSubjectAsString() {
+            // return the ldap DN
+            return subject.getName();
+        }
+        public byte[] getSubjectAsBytes() throws IOException {
+            // return the encoded ldap DN
+            return subject.getEncoded();
+        }
+        public byte[] getSubjectKeyIdentifier() {
+            return selector.getSubjectKeyIdentifier();
+        }
+        public byte[] getAuthorityKeyIdentifier() {
+            return selector.getAuthorityKeyIdentifier();
+        }
+        public Date getCertificateValid() {
+            return selector.getCertificateValid();
+        }
+        public Date getPrivateKeyValid() {
+            return selector.getPrivateKeyValid();
+        }
+        public String getSubjectPublicKeyAlgID() {
+            return selector.getSubjectPublicKeyAlgID();
+        }
+        public PublicKey getSubjectPublicKey() {
+            return selector.getSubjectPublicKey();
+        }
+        public boolean[] getKeyUsage() {
+            return selector.getKeyUsage();
+        }
+        public Set<String> getExtendedKeyUsage() {
+            return selector.getExtendedKeyUsage();
+        }
+        public boolean getMatchAllSubjectAltNames() {
+            return selector.getMatchAllSubjectAltNames();
+        }
+        public Collection<List<?>> getSubjectAlternativeNames() {
+            return selector.getSubjectAlternativeNames();
+        }
+        public byte[] getNameConstraints() {
+            return selector.getNameConstraints();
+        }
+        public int getBasicConstraints() {
+            return selector.getBasicConstraints();
+        }
+        public Set<String> getPolicy() {
+            return selector.getPolicy();
+        }
+        public Collection<List<?>> getPathToNames() {
+            return selector.getPathToNames();
+        }
+
+        public boolean match(Certificate cert) {
+            // temporarily set the subject criterion to the certSubject
+            // so that match will not reject the desired certificates
+            selector.setSubject(certSubject);
+            boolean match = selector.match(cert);
+            selector.setSubject(subject);
+            return match;
+        }
+    }
+
+    /**
+     * This class has the same purpose as LDAPCertSelector except it is for
+     * X.509 CRLs.
+     */
+    static class LDAPCRLSelector extends X509CRLSelector {
+
+        private X509CRLSelector selector;
+        private Collection<X500Principal> certIssuers;
+        private Collection<X500Principal> issuers;
+        private HashSet<Object> issuerNames;
+
+        /**
+         * Creates an LDAPCRLSelector.
+         *
+         * @param selector the X509CRLSelector to wrap
+         * @param certIssuers the issuer DNs of the CRLs that you want
+         *      to retrieve via LDAP
+         * @param ldapDN the LDAP DN where the CRL is stored
+         */
+        LDAPCRLSelector(X509CRLSelector selector,
+            Collection<X500Principal> certIssuers, String ldapDN)
+            throws IOException {
+            this.selector = selector == null ? new X509CRLSelector() : selector;
+            this.certIssuers = certIssuers;
+            issuerNames = new HashSet<>();
+            issuerNames.add(ldapDN);
+            issuers = new HashSet<>();
+            issuers.add(new X500Name(ldapDN).asX500Principal());
+        }
+        // we only override the get (accessor methods) since the set methods
+        // will not be invoked by the code that uses this LDAPCRLSelector.
+        public Collection<X500Principal> getIssuers() {
+            // return the ldap DN
+            return Collections.unmodifiableCollection(issuers);
+        }
+        public Collection<Object> getIssuerNames() {
+            // return the ldap DN
+            return Collections.unmodifiableCollection(issuerNames);
+        }
+        public BigInteger getMinCRL() {
+            return selector.getMinCRL();
+        }
+        public BigInteger getMaxCRL() {
+            return selector.getMaxCRL();
+        }
+        public Date getDateAndTime() {
+            return selector.getDateAndTime();
+        }
+        public X509Certificate getCertificateChecking() {
+            return selector.getCertificateChecking();
+        }
+        public boolean match(CRL crl) {
+            // temporarily set the issuer criterion to the certIssuers
+            // so that match will not reject the desired CRL
+            selector.setIssuers(certIssuers);
+            boolean match = selector.match(crl);
+            selector.setIssuers(issuers);
+            return match;
+        }
+    }
+}
diff --git a/ojluni/src/main/java/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java b/ojluni/src/main/java/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java
new file mode 100644
index 0000000..8e6899b
--- /dev/null
+++ b/ojluni/src/main/java/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath.ldap;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.security.NoSuchAlgorithmException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509CRLSelector;
+import javax.naming.CommunicationException;
+import javax.naming.ServiceUnavailableException;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.provider.certpath.CertStoreHelper;
+
+/**
+ * LDAP implementation of CertStoreHelper.
+ */
+
+public final class LDAPCertStoreHelper
+    extends CertStoreHelper
+{
+    @Override
+    public CertStore getCertStore(URI uri)
+        throws NoSuchAlgorithmException, InvalidAlgorithmParameterException
+    {
+        return LDAPCertStore.getInstance(LDAPCertStore.getParameters(uri));
+    }
+
+    @Override
+    public X509CertSelector wrap(X509CertSelector selector,
+                                 X500Principal certSubject,
+                                 String ldapDN)
+        throws IOException
+    {
+        return new LDAPCertStore.LDAPCertSelector(selector, certSubject, ldapDN);
+    }
+
+    @Override
+    public X509CRLSelector wrap(X509CRLSelector selector,
+                                Collection<X500Principal> certIssuers,
+                                String ldapDN)
+        throws IOException
+    {
+        return new LDAPCertStore.LDAPCRLSelector(selector, certIssuers, ldapDN);
+    }
+
+    @Override
+    public boolean isCausedByNetworkIssue(CertStoreException e) {
+        Throwable t = e.getCause();
+        return (t != null && (t instanceof ServiceUnavailableException ||
+                              t instanceof CommunicationException));
+    }
+}
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index ce60eb7..3177c60b 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -1314,11 +1314,41 @@
     ojluni/src/main/java/sun/security/pkcs/PKCS9Attributes.java \
     ojluni/src/main/java/sun/security/pkcs/SignerInfo.java \
     ojluni/src/main/java/sun/security/pkcs/SigningCertificateInfo.java \
+    ojluni/src/main/java/sun/security/provider/certpath/AdaptableX509CertSelector.java \
+    ojluni/src/main/java/sun/security/provider/certpath/AdjacencyList.java \
     ojluni/src/main/java/sun/security/provider/certpath/AlgorithmChecker.java \
+    ojluni/src/main/java/sun/security/provider/certpath/BasicChecker.java \
+    ojluni/src/main/java/sun/security/provider/certpath/BuildStep.java \
+    ojluni/src/main/java/sun/security/provider/certpath/Builder.java \
+    ojluni/src/main/java/sun/security/provider/certpath/CertId.java \
     ojluni/src/main/java/sun/security/provider/certpath/CertPathHelper.java \
+    ojluni/src/main/java/sun/security/provider/certpath/CertStoreHelper.java \
+    ojluni/src/main/java/sun/security/provider/certpath/ConstraintsChecker.java \
+    ojluni/src/main/java/sun/security/provider/certpath/DistributionPointFetcher.java \
+    ojluni/src/main/java/sun/security/provider/certpath/ForwardBuilder.java \
+    ojluni/src/main/java/sun/security/provider/certpath/ForwardState.java \
+    ojluni/src/main/java/sun/security/provider/certpath/KeyChecker.java \
+    ojluni/src/main/java/sun/security/provider/certpath/OCSP.java \
+    ojluni/src/main/java/sun/security/provider/certpath/OCSPRequest.java \
+    ojluni/src/main/java/sun/security/provider/certpath/OCSPResponse.java \
+    ojluni/src/main/java/sun/security/provider/certpath/PKIX.java \
+    ojluni/src/main/java/sun/security/provider/certpath/PKIXCertPathValidator.java \
+    ojluni/src/main/java/sun/security/provider/certpath/PKIXMasterCertPathValidator.java \
+    ojluni/src/main/java/sun/security/provider/certpath/PolicyChecker.java \
+    ojluni/src/main/java/sun/security/provider/certpath/PolicyNodeImpl.java \
+    ojluni/src/main/java/sun/security/provider/certpath/ReverseBuilder.java \
+    ojluni/src/main/java/sun/security/provider/certpath/ReverseState.java \
+    ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java \
+    ojluni/src/main/java/sun/security/provider/certpath/State.java \
+    ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilder.java \
+    ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderException.java \
+    ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderParameters.java \
+    ojluni/src/main/java/sun/security/provider/certpath/SunCertPathBuilderResult.java \
+    ojluni/src/main/java/sun/security/provider/certpath/URICertStore.java \
     ojluni/src/main/java/sun/security/provider/certpath/UntrustedChecker.java \
-    ojluni/src/main/java/sun/security/provider/certpath/X509CertificatePair.java \
+    ojluni/src/main/java/sun/security/provider/certpath/Vertex.java \
     ojluni/src/main/java/sun/security/provider/certpath/X509CertPath.java \
+    ojluni/src/main/java/sun/security/provider/certpath/X509CertificatePair.java \
     ojluni/src/main/java/sun/security/provider/NativePRNG.java \
     ojluni/src/main/java/sun/security/provider/NativeSeedGenerator.java \
     ojluni/src/main/java/sun/security/provider/SecureRandom.java \