alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (X509CertSelector.java)

This example Java source code file (X509CertSelector.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

date, derinputstream, extension_oids, generalnameinterface, hashset, ioexception, iterator, key, list, math, object, security, set, string, util, x500principal, x509certselector

The X509CertSelector.java Java example source code

/*
 * 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 java.security.cert;

import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.*;
import javax.security.auth.x500.X500Principal;

import sun.misc.HexDumpEncoder;
import sun.security.util.Debug;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.*;

/**
 * A {@code CertSelector} that selects {@code X509Certificates} that
 * match all specified criteria. This class is particularly useful when
 * selecting certificates from a {@code CertStore} to build a
 * PKIX-compliant certification path.
 * <p>
 * When first constructed, an {@code X509CertSelector} has no criteria
 * enabled and each of the {@code get} methods return a default value
 * ({@code null}, or {@code -1} for the {@link #getBasicConstraints
 * getBasicConstraints} method). Therefore, the {@link #match match}
 * method would return {@code true} for any {@code X509Certificate}.
 * Typically, several criteria are enabled (by calling
 * {@link #setIssuer setIssuer} or
 * {@link #setKeyUsage setKeyUsage}, for instance) and then the
 * {@code X509CertSelector} is passed to
 * {@link CertStore#getCertificates CertStore.getCertificates} or some similar
 * method.
 * <p>
 * Several criteria can be enabled (by calling {@link #setIssuer setIssuer}
 * and {@link #setSerialNumber setSerialNumber},
 * for example) such that the {@code match} method
 * usually uniquely matches a single {@code X509Certificate}. We say
 * usually, since it is possible for two issuing CAs to have the same
 * distinguished name and each issue a certificate with the same serial
 * number. Other unique combinations include the issuer, subject,
 * subjectKeyIdentifier and/or the subjectPublicKey criteria.
 * <p>
 * Please refer to <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280:
 * Internet X.509 Public Key Infrastructure Certificate and CRL Profile</a> for
 * definitions of the X.509 certificate extensions mentioned below.
 * <p>
 * <b>Concurrent Access
 * <p>
 * Unless otherwise specified, the methods defined in this class are not
 * thread-safe. Multiple threads that need to access a single
 * object concurrently should synchronize amongst themselves and
 * provide the necessary locking. Multiple threads each manipulating
 * separate objects need not synchronize.
 *
 * @see CertSelector
 * @see X509Certificate
 *
 * @since       1.4
 * @author      Steve Hanna
 */
public class X509CertSelector implements CertSelector {

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

    private final static ObjectIdentifier ANY_EXTENDED_KEY_USAGE =
        ObjectIdentifier.newInternal(new int[] {2, 5, 29, 37, 0});

    static {
        CertPathHelperImpl.initialize();
    }

    private BigInteger serialNumber;
    private X500Principal issuer;
    private X500Principal subject;
    private byte[] subjectKeyID;
    private byte[] authorityKeyID;
    private Date certificateValid;
    private Date privateKeyValid;
    private ObjectIdentifier subjectPublicKeyAlgID;
    private PublicKey subjectPublicKey;
    private byte[] subjectPublicKeyBytes;
    private boolean[] keyUsage;
    private Set<String> keyPurposeSet;
    private Set<ObjectIdentifier> keyPurposeOIDSet;
    private Set<List subjectAlternativeNames;
    private Set<GeneralNameInterface> subjectAlternativeGeneralNames;
    private CertificatePolicySet policy;
    private Set<String> policySet;
    private Set<List pathToNames;
    private Set<GeneralNameInterface> pathToGeneralNames;
    private NameConstraintsExtension nc;
    private byte[] ncBytes;
    private int basicConstraints = -1;
    private X509Certificate x509Cert;
    private boolean matchAllSubjectAltNames = true;

    private static final Boolean FALSE = Boolean.FALSE;

    private static final int PRIVATE_KEY_USAGE_ID = 0;
    private static final int SUBJECT_ALT_NAME_ID = 1;
    private static final int NAME_CONSTRAINTS_ID = 2;
    private static final int CERT_POLICIES_ID = 3;
    private static final int EXTENDED_KEY_USAGE_ID = 4;
    private static final int NUM_OF_EXTENSIONS = 5;
    private static final String[] EXTENSION_OIDS = new String[NUM_OF_EXTENSIONS];

    static {
        EXTENSION_OIDS[PRIVATE_KEY_USAGE_ID]  = "2.5.29.16";
        EXTENSION_OIDS[SUBJECT_ALT_NAME_ID]   = "2.5.29.17";
        EXTENSION_OIDS[NAME_CONSTRAINTS_ID]   = "2.5.29.30";
        EXTENSION_OIDS[CERT_POLICIES_ID]      = "2.5.29.32";
        EXTENSION_OIDS[EXTENDED_KEY_USAGE_ID] = "2.5.29.37";
    };

    /* Constants representing the GeneralName types */
    static final int NAME_ANY = 0;
    static final int NAME_RFC822 = 1;
    static final int NAME_DNS = 2;
    static final int NAME_X400 = 3;
    static final int NAME_DIRECTORY = 4;
    static final int NAME_EDI = 5;
    static final int NAME_URI = 6;
    static final int NAME_IP = 7;
    static final int NAME_OID = 8;

    /**
     * Creates an {@code X509CertSelector}. Initially, no criteria are set
     * so any {@code X509Certificate} will match.
     */
    public X509CertSelector() {
        // empty
    }

    /**
     * Sets the certificateEquals criterion. The specified
     * {@code X509Certificate} must be equal to the
     * {@code X509Certificate} passed to the {@code match} method.
     * If {@code null}, then this check is not applied.
     *
     * <p>This method is particularly useful when it is necessary to
     * match a single certificate. Although other criteria can be specified
     * in conjunction with the certificateEquals criterion, it is usually not
     * practical or necessary.
     *
     * @param cert the {@code X509Certificate} to match (or
     * {@code null})
     * @see #getCertificate
     */
    public void setCertificate(X509Certificate cert) {
        x509Cert = cert;
    }

    /**
     * Sets the serialNumber criterion. The specified serial number
     * must match the certificate serial number in the
     * {@code X509Certificate}. If {@code null}, any certificate
     * serial number will do.
     *
     * @param serial the certificate serial number to match
     *        (or {@code null})
     * @see #getSerialNumber
     */
    public void setSerialNumber(BigInteger serial) {
        serialNumber = serial;
    }

    /**
     * Sets the issuer criterion. The specified distinguished name
     * must match the issuer distinguished name in the
     * {@code X509Certificate}. If {@code null}, any issuer
     * distinguished name will do.
     *
     * @param issuer a distinguished name as X500Principal
     *                 (or {@code null})
     * @since 1.5
     */
    public void setIssuer(X500Principal issuer) {
        this.issuer = issuer;
    }

    /**
     * <strong>Denigrated, use {@linkplain #setIssuer(X500Principal)}
     * or {@linkplain #setIssuer(byte[])} instead. This method should not be
     * relied on as it can fail to match some certificates because of a loss of
     * encoding information in the
     * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253 String form
     * of some distinguished names.
     * <p>
     * Sets the issuer criterion. The specified distinguished name
     * must match the issuer distinguished name in the
     * {@code X509Certificate}. If {@code null}, any issuer
     * distinguished name will do.
     * <p>
     * If {@code issuerDN} is not {@code null}, it should contain a
     * distinguished name, in RFC 2253 format.
     *
     * @param issuerDN a distinguished name in RFC 2253 format
     *                 (or {@code null})
     * @throws IOException if a parsing error occurs (incorrect form for DN)
     */
    public void setIssuer(String issuerDN) throws IOException {
        if (issuerDN == null) {
            issuer = null;
        } else {
            issuer = new X500Name(issuerDN).asX500Principal();
        }
    }

    /**
     * Sets the issuer criterion. The specified distinguished name
     * must match the issuer distinguished name in the
     * {@code X509Certificate}. If {@code null} is specified,
     * the issuer criterion is disabled and any issuer distinguished name will
     * do.
     * <p>
     * If {@code issuerDN} is not {@code null}, it should contain a
     * single DER encoded distinguished name, as defined in X.501. The ASN.1
     * notation for this structure is as follows.
     * <pre>{@code
     * Name ::= CHOICE {
     *   RDNSequence }
     *
     * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
     *
     * RelativeDistinguishedName ::=
     *   SET SIZE (1 .. MAX) OF AttributeTypeAndValue
     *
     * AttributeTypeAndValue ::= SEQUENCE {
     *   type     AttributeType,
     *   value    AttributeValue }
     *
     * AttributeType ::= OBJECT IDENTIFIER
     *
     * AttributeValue ::= ANY DEFINED BY AttributeType
     * ....
     * DirectoryString ::= CHOICE {
     *       teletexString           TeletexString (SIZE (1..MAX)),
     *       printableString         PrintableString (SIZE (1..MAX)),
     *       universalString         UniversalString (SIZE (1..MAX)),
     *       utf8String              UTF8String (SIZE (1.. MAX)),
     *       bmpString               BMPString (SIZE (1..MAX)) }
     * }</pre>
     * <p>
     * Note that the byte array specified here is cloned to protect against
     * subsequent modifications.
     *
     * @param issuerDN a byte array containing the distinguished name
     *                 in ASN.1 DER encoded form (or {@code null})
     * @throws IOException if an encoding error occurs (incorrect form for DN)
     */
    public void setIssuer(byte[] issuerDN) throws IOException {
        try {
            issuer = (issuerDN == null ? null : new X500Principal(issuerDN));
        } catch (IllegalArgumentException e) {
            throw new IOException("Invalid name", e);
        }
    }

    /**
     * Sets the subject criterion. The specified distinguished name
     * must match the subject distinguished name in the
     * {@code X509Certificate}. If {@code null}, any subject
     * distinguished name will do.
     *
     * @param subject a distinguished name as X500Principal
     *                  (or {@code null})
     * @since 1.5
     */
    public void setSubject(X500Principal subject) {
        this.subject = subject;
    }

    /**
     * <strong>Denigrated, use {@linkplain #setSubject(X500Principal)}
     * or {@linkplain #setSubject(byte[])} instead. This method should not be
     * relied on as it can fail to match some certificates because of a loss of
     * encoding information in the RFC 2253 String form of some distinguished
     * names.
     * <p>
     * Sets the subject criterion. The specified distinguished name
     * must match the subject distinguished name in the
     * {@code X509Certificate}. If {@code null}, any subject
     * distinguished name will do.
     * <p>
     * If {@code subjectDN} is not {@code null}, it should contain a
     * distinguished name, in RFC 2253 format.
     *
     * @param subjectDN a distinguished name in RFC 2253 format
     *                  (or {@code null})
     * @throws IOException if a parsing error occurs (incorrect form for DN)
     */
    public void setSubject(String subjectDN) throws IOException {
        if (subjectDN == null) {
            subject = null;
        } else {
            subject = new X500Name(subjectDN).asX500Principal();
        }
    }

    /**
     * Sets the subject criterion. The specified distinguished name
     * must match the subject distinguished name in the
     * {@code X509Certificate}. If {@code null}, any subject
     * distinguished name will do.
     * <p>
     * If {@code subjectDN} is not {@code null}, it should contain a
     * single DER encoded distinguished name, as defined in X.501. For the ASN.1
     * notation for this structure, see
     * {@link #setIssuer(byte [] issuerDN) setIssuer(byte [] issuerDN)}.
     *
     * @param subjectDN a byte array containing the distinguished name in
     *                  ASN.1 DER format (or {@code null})
     * @throws IOException if an encoding error occurs (incorrect form for DN)
     */
    public void setSubject(byte[] subjectDN) throws IOException {
        try {
            subject = (subjectDN == null ? null : new X500Principal(subjectDN));
        } catch (IllegalArgumentException e) {
            throw new IOException("Invalid name", e);
        }
    }

    /**
     * Sets the subjectKeyIdentifier criterion. The
     * {@code X509Certificate} must contain a SubjectKeyIdentifier
     * extension for which the contents of the extension
     * matches the specified criterion value.
     * If the criterion value is {@code null}, no
     * subjectKeyIdentifier check will be done.
     * <p>
     * If {@code subjectKeyID} is not {@code null}, it
     * should contain a single DER encoded value corresponding to the contents
     * of the extension value (not including the object identifier,
     * criticality setting, and encapsulating OCTET STRING)
     * for a SubjectKeyIdentifier extension.
     * The ASN.1 notation for this structure follows.
     *
     * <pre>{@code
     * SubjectKeyIdentifier ::= KeyIdentifier
     *
     * KeyIdentifier ::= OCTET STRING
     * }</pre>
     * <p>
     * Since the format of subject key identifiers is not mandated by
     * any standard, subject key identifiers are not parsed by the
     * {@code X509CertSelector}. Instead, the values are compared using
     * a byte-by-byte comparison.
     * <p>
     * Note that the byte array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param subjectKeyID the subject key identifier (or {@code null})
     * @see #getSubjectKeyIdentifier
     */
    public void setSubjectKeyIdentifier(byte[] subjectKeyID) {
        if (subjectKeyID == null) {
            this.subjectKeyID = null;
        } else {
            this.subjectKeyID = subjectKeyID.clone();
        }
    }

    /**
     * Sets the authorityKeyIdentifier criterion. The
     * {@code X509Certificate} must contain an
     * AuthorityKeyIdentifier extension for which the contents of the
     * extension value matches the specified criterion value.
     * If the criterion value is {@code null}, no
     * authorityKeyIdentifier check will be done.
     * <p>
     * If {@code authorityKeyID} is not {@code null}, it
     * should contain a single DER encoded value corresponding to the contents
     * of the extension value (not including the object identifier,
     * criticality setting, and encapsulating OCTET STRING)
     * for an AuthorityKeyIdentifier extension.
     * The ASN.1 notation for this structure follows.
     *
     * <pre>{@code
     * AuthorityKeyIdentifier ::= SEQUENCE {
     *    keyIdentifier             [0] KeyIdentifier           OPTIONAL,
     *    authorityCertIssuer       [1] GeneralNames            OPTIONAL,
     *    authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }
     *
     * KeyIdentifier ::= OCTET STRING
     * }</pre>
     * <p>
     * Authority key identifiers are not parsed by the
     * {@code X509CertSelector}.  Instead, the values are
     * compared using a byte-by-byte comparison.
     * <p>
     * When the {@code keyIdentifier} field of
     * {@code AuthorityKeyIdentifier} is populated, the value is
     * usually taken from the {@code SubjectKeyIdentifier} extension
     * in the issuer's certificate.  Note, however, that the result of
     * {@code X509Certificate.getExtensionValue(<SubjectKeyIdentifier Object
     * Identifier>)} on the issuer's certificate may NOT be used
     * directly as the input to {@code setAuthorityKeyIdentifier}.
     * This is because the SubjectKeyIdentifier contains
     * only a KeyIdentifier OCTET STRING, and not a SEQUENCE of
     * KeyIdentifier, GeneralNames, and CertificateSerialNumber.
     * In order to use the extension value of the issuer certificate's
     * {@code SubjectKeyIdentifier}
     * extension, it will be necessary to extract the value of the embedded
     * {@code KeyIdentifier} OCTET STRING, then DER encode this OCTET
     * STRING inside a SEQUENCE.
     * For more details on SubjectKeyIdentifier, see
     * {@link #setSubjectKeyIdentifier(byte[] subjectKeyID)}.
     * <p>
     * Note also that the byte array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param authorityKeyID the authority key identifier
     *        (or {@code null})
     * @see #getAuthorityKeyIdentifier
     */
    public void setAuthorityKeyIdentifier(byte[] authorityKeyID) {
        if (authorityKeyID == null) {
            this.authorityKeyID = null;
        } else {
            this.authorityKeyID = authorityKeyID.clone();
        }
    }

    /**
     * Sets the certificateValid criterion. The specified date must fall
     * within the certificate validity period for the
     * {@code X509Certificate}. If {@code null}, no certificateValid
     * check will be done.
     * <p>
     * Note that the {@code Date} supplied here is cloned to protect
     * against subsequent modifications.
     *
     * @param certValid the {@code Date} to check (or {@code null})
     * @see #getCertificateValid
     */
    public void setCertificateValid(Date certValid) {
        if (certValid == null) {
            certificateValid = null;
        } else {
            certificateValid = (Date)certValid.clone();
        }
    }

    /**
     * Sets the privateKeyValid criterion. The specified date must fall
     * within the private key validity period for the
     * {@code X509Certificate}. If {@code null}, no privateKeyValid
     * check will be done.
     * <p>
     * Note that the {@code Date} supplied here is cloned to protect
     * against subsequent modifications.
     *
     * @param privateKeyValid the {@code Date} to check (or
     *                        {@code null})
     * @see #getPrivateKeyValid
     */
    public void setPrivateKeyValid(Date privateKeyValid) {
        if (privateKeyValid == null) {
            this.privateKeyValid = null;
        } else {
            this.privateKeyValid = (Date)privateKeyValid.clone();
        }
    }

    /**
     * Sets the subjectPublicKeyAlgID criterion. The
     * {@code X509Certificate} must contain a subject public key
     * with the specified algorithm. If {@code null}, no
     * subjectPublicKeyAlgID check will be done.
     *
     * @param oid The object identifier (OID) of the algorithm to check
     *            for (or {@code null}). An OID is represented by a
     *            set of nonnegative integers separated by periods.
     * @throws IOException if the OID is invalid, such as
     * the first component being not 0, 1 or 2 or the second component
     * being greater than 39.
     *
     * @see #getSubjectPublicKeyAlgID
     */
    public void setSubjectPublicKeyAlgID(String oid) throws IOException {
        if (oid == null) {
            subjectPublicKeyAlgID = null;
        } else {
            subjectPublicKeyAlgID = new ObjectIdentifier(oid);
        }
    }

    /**
     * Sets the subjectPublicKey criterion. The
     * {@code X509Certificate} must contain the specified subject public
     * key. If {@code null}, no subjectPublicKey check will be done.
     *
     * @param key the subject public key to check for (or {@code null})
     * @see #getSubjectPublicKey
     */
    public void setSubjectPublicKey(PublicKey key) {
        if (key == null) {
            subjectPublicKey = null;
            subjectPublicKeyBytes = null;
        } else {
            subjectPublicKey = key;
            subjectPublicKeyBytes = key.getEncoded();
        }
    }

    /**
     * Sets the subjectPublicKey criterion. The {@code X509Certificate}
     * must contain the specified subject public key. If {@code null},
     * no subjectPublicKey check will be done.
     * <p>
     * Because this method allows the public key to be specified as a byte
     * array, it may be used for unknown key types.
     * <p>
     * If {@code key} is not {@code null}, it should contain a
     * single DER encoded SubjectPublicKeyInfo structure, as defined in X.509.
     * The ASN.1 notation for this structure is as follows.
     * <pre>{@code
     * SubjectPublicKeyInfo  ::=  SEQUENCE  {
     *   algorithm            AlgorithmIdentifier,
     *   subjectPublicKey     BIT STRING  }
     *
     * AlgorithmIdentifier  ::=  SEQUENCE  {
     *   algorithm               OBJECT IDENTIFIER,
     *   parameters              ANY DEFINED BY algorithm OPTIONAL  }
     *                              -- contains a value of the type
     *                              -- registered for use with the
     *                              -- algorithm object identifier value
     * }</pre>
     * <p>
     * Note that the byte array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param key a byte array containing the subject public key in ASN.1 DER
     *            form (or {@code null})
     * @throws IOException if an encoding error occurs (incorrect form for
     * subject public key)
     * @see #getSubjectPublicKey
     */
    public void setSubjectPublicKey(byte[] key) throws IOException {
        if (key == null) {
            subjectPublicKey = null;
            subjectPublicKeyBytes = null;
        } else {
            subjectPublicKeyBytes = key.clone();
            subjectPublicKey = X509Key.parse(new DerValue(subjectPublicKeyBytes));
        }
    }

    /**
     * Sets the keyUsage criterion. The {@code X509Certificate}
     * must allow the specified keyUsage values. If {@code null}, no
     * keyUsage check will be done. Note that an {@code X509Certificate}
     * that has no keyUsage extension implicitly allows all keyUsage values.
     * <p>
     * Note that the boolean array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param keyUsage a boolean array in the same format as the boolean
     *                 array returned by
     * {@link X509Certificate#getKeyUsage() X509Certificate.getKeyUsage()}.
     *                 Or {@code null}.
     * @see #getKeyUsage
     */
    public void setKeyUsage(boolean[] keyUsage) {
        if (keyUsage == null) {
            this.keyUsage = null;
        } else {
            this.keyUsage = keyUsage.clone();
        }
    }

    /**
     * Sets the extendedKeyUsage criterion. The {@code X509Certificate}
     * must allow the specified key purposes in its extended key usage
     * extension. If {@code keyPurposeSet} is empty or {@code null},
     * no extendedKeyUsage check will be done. Note that an
     * {@code X509Certificate} that has no extendedKeyUsage extension
     * implicitly allows all key purposes.
     * <p>
     * Note that the {@code Set} is cloned to protect against
     * subsequent modifications.
     *
     * @param keyPurposeSet a {@code Set} of key purpose OIDs in string
     * format (or {@code null}). Each OID is represented by a set of
     * nonnegative integers separated by periods.
     * @throws IOException if the OID is invalid, such as
     * the first component being not 0, 1 or 2 or the second component
     * being greater than 39.
     * @see #getExtendedKeyUsage
     */
    public void setExtendedKeyUsage(Set<String> keyPurposeSet) throws IOException {
        if ((keyPurposeSet == null) || keyPurposeSet.isEmpty()) {
            this.keyPurposeSet = null;
            keyPurposeOIDSet = null;
        } else {
            this.keyPurposeSet =
                Collections.unmodifiableSet(new HashSet<String>(keyPurposeSet));
            keyPurposeOIDSet = new HashSet<ObjectIdentifier>();
            for (String s : this.keyPurposeSet) {
                keyPurposeOIDSet.add(new ObjectIdentifier(s));
            }
        }
    }

    /**
     * Enables/disables matching all of the subjectAlternativeNames
     * specified in the {@link #setSubjectAlternativeNames
     * setSubjectAlternativeNames} or {@link #addSubjectAlternativeName
     * addSubjectAlternativeName} methods. If enabled,
     * the {@code X509Certificate} must contain all of the
     * specified subject alternative names. If disabled, the
     * {@code X509Certificate} must contain at least one of the
     * specified subject alternative names.
     *
     * <p>The matchAllNames flag is {@code true} by default.
     *
     * @param matchAllNames if {@code true}, the flag is enabled;
     * if {@code false}, the flag is disabled.
     * @see #getMatchAllSubjectAltNames
     */
    public void setMatchAllSubjectAltNames(boolean matchAllNames) {
        this.matchAllSubjectAltNames = matchAllNames;
    }

    /**
     * Sets the subjectAlternativeNames criterion. The
     * {@code X509Certificate} must contain all or at least one of the
     * specified subjectAlternativeNames, depending on the value of
     * the matchAllNames flag (see {@link #setMatchAllSubjectAltNames
     * setMatchAllSubjectAltNames}).
     * <p>
     * This method allows the caller to specify, with a single method call,
     * the complete set of subject alternative names for the
     * subjectAlternativeNames criterion. The specified value replaces
     * the previous value for the subjectAlternativeNames criterion.
     * <p>
     * The {@code names} parameter (if not {@code null}) is a
     * {@code Collection} with one
     * entry for each name to be included in the subject alternative name
     * criterion. Each entry is a {@code List} whose first entry is an
     * {@code Integer} (the name type, 0-8) and whose second
     * entry is a {@code String} or a byte array (the name, in
     * string or ASN.1 DER encoded form, respectively).
     * There can be multiple names of the same type. If {@code null}
     * is supplied as the value for this argument, no
     * subjectAlternativeNames check will be performed.
     * <p>
     * Each subject alternative name in the {@code Collection}
     * may be specified either as a {@code String} or as an ASN.1 encoded
     * byte array. For more details about the formats used, see
     * {@link #addSubjectAlternativeName(int type, String name)
     * addSubjectAlternativeName(int type, String name)} and
     * {@link #addSubjectAlternativeName(int type, byte [] name)
     * addSubjectAlternativeName(int type, byte [] name)}.
     * <p>
     * <strong>Note: for distinguished names, specify the byte
     * array form instead of the String form. See the note in
     * {@link #addSubjectAlternativeName(int, String)} for more information.
     * <p>
     * Note that the {@code names} parameter can contain duplicate
     * names (same name and name type), but they may be removed from the
     * {@code Collection} of names returned by the
     * {@link #getSubjectAlternativeNames getSubjectAlternativeNames} method.
     * <p>
     * Note that a deep copy is performed on the {@code Collection} to
     * protect against subsequent modifications.
     *
     * @param names a {@code Collection} of names (or {@code null})
     * @throws IOException if a parsing error occurs
     * @see #getSubjectAlternativeNames
     */
    public void setSubjectAlternativeNames(Collection<List names)
            throws IOException {
        if (names == null) {
            subjectAlternativeNames = null;
            subjectAlternativeGeneralNames = null;
        } else {
            if (names.isEmpty()) {
                subjectAlternativeNames = null;
                subjectAlternativeGeneralNames = null;
                return;
            }
            Set<List tempNames = cloneAndCheckNames(names);
            // Ensure that we either set both of these or neither
            subjectAlternativeGeneralNames = parseNames(tempNames);
            subjectAlternativeNames = tempNames;
        }
    }

    /**
     * Adds a name to the subjectAlternativeNames criterion. The
     * {@code X509Certificate} must contain all or at least one
     * of the specified subjectAlternativeNames, depending on the value of
     * the matchAllNames flag (see {@link #setMatchAllSubjectAltNames
     * setMatchAllSubjectAltNames}).
     * <p>
     * This method allows the caller to add a name to the set of subject
     * alternative names.
     * The specified name is added to any previous value for the
     * subjectAlternativeNames criterion. If the specified name is a
     * duplicate, it may be ignored.
     * <p>
     * The name is provided in string format.
     * <a href="http://www.ietf.org/rfc/rfc822.txt">RFC 822, DNS, and URI
     * names use the well-established string formats for those types (subject to
     * the restrictions included in RFC 3280). IPv4 address names are
     * supplied using dotted quad notation. OID address names are represented
     * as a series of nonnegative integers separated by periods. And
     * directory names (distinguished names) are supplied in RFC 2253 format.
     * No standard string format is defined for otherNames, X.400 names,
     * EDI party names, IPv6 address names, or any other type of names. They
     * should be specified using the
     * {@link #addSubjectAlternativeName(int type, byte [] name)
     * addSubjectAlternativeName(int type, byte [] name)}
     * method.
     * <p>
     * <strong>Note: for distinguished names, use
     * {@linkplain #addSubjectAlternativeName(int, byte[])} instead.
     * This method should not be relied on as it can fail to match some
     * certificates because of a loss of encoding information in the RFC 2253
     * String form of some distinguished names.
     *
     * @param type the name type (0-8, as specified in
     *             RFC 3280, section 4.2.1.7)
     * @param name the name in string form (not {@code null})
     * @throws IOException if a parsing error occurs
     */
    public void addSubjectAlternativeName(int type, String name)
            throws IOException {
        addSubjectAlternativeNameInternal(type, name);
    }

    /**
     * Adds a name to the subjectAlternativeNames criterion. The
     * {@code X509Certificate} must contain all or at least one
     * of the specified subjectAlternativeNames, depending on the value of
     * the matchAllNames flag (see {@link #setMatchAllSubjectAltNames
     * setMatchAllSubjectAltNames}).
     * <p>
     * This method allows the caller to add a name to the set of subject
     * alternative names.
     * The specified name is added to any previous value for the
     * subjectAlternativeNames criterion. If the specified name is a
     * duplicate, it may be ignored.
     * <p>
     * The name is provided as a byte array. This byte array should contain
     * the DER encoded name, as it would appear in the GeneralName structure
     * defined in RFC 3280 and X.509. The encoded byte array should only contain
     * the encoded value of the name, and should not include the tag associated
     * with the name in the GeneralName structure. The ASN.1 definition of this
     * structure appears below.
     * <pre>{@code
     *  GeneralName ::= CHOICE {
     *       otherName                       [0]     OtherName,
     *       rfc822Name                      [1]     IA5String,
     *       dNSName                         [2]     IA5String,
     *       x400Address                     [3]     ORAddress,
     *       directoryName                   [4]     Name,
     *       ediPartyName                    [5]     EDIPartyName,
     *       uniformResourceIdentifier       [6]     IA5String,
     *       iPAddress                       [7]     OCTET STRING,
     *       registeredID                    [8]     OBJECT IDENTIFIER}
     * }</pre>
     * <p>
     * Note that the byte array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param type the name type (0-8, as listed above)
     * @param name a byte array containing the name in ASN.1 DER encoded form
     * @throws IOException if a parsing error occurs
     */
    public void addSubjectAlternativeName(int type, byte[] name)
            throws IOException {
        // clone because byte arrays are modifiable
        addSubjectAlternativeNameInternal(type, name.clone());
    }

    /**
     * A private method that adds a name (String or byte array) to the
     * subjectAlternativeNames criterion. The {@code X509Certificate}
     * must contain the specified subjectAlternativeName.
     *
     * @param type the name type (0-8, as specified in
     *             RFC 3280, section 4.2.1.7)
     * @param name the name in string or byte array form
     * @throws IOException if a parsing error occurs
     */
    private void addSubjectAlternativeNameInternal(int type, Object name)
            throws IOException {
        // First, ensure that the name parses
        GeneralNameInterface tempName = makeGeneralNameInterface(type, name);
        if (subjectAlternativeNames == null) {
            subjectAlternativeNames = new HashSet<List();
        }
        if (subjectAlternativeGeneralNames == null) {
            subjectAlternativeGeneralNames = new HashSet<GeneralNameInterface>();
        }
        List<Object> list = new ArrayList(2);
        list.add(Integer.valueOf(type));
        list.add(name);
        subjectAlternativeNames.add(list);
        subjectAlternativeGeneralNames.add(tempName);
    }

    /**
     * Parse an argument of the form passed to setSubjectAlternativeNames,
     * returning a {@code Collection} of
     * {@code GeneralNameInterface}s.
     * Throw an IllegalArgumentException or a ClassCastException
     * if the argument is malformed.
     *
     * @param names a Collection with one entry per name.
     *              Each entry is a {@code List} whose first entry
     *              is an Integer (the name type, 0-8) and whose second
     *              entry is a String or a byte array (the name, in
     *              string or ASN.1 DER encoded form, respectively).
     *              There can be multiple names of the same type. Null is
     *              not an acceptable value.
     * @return a Set of {@code GeneralNameInterface}s
     * @throws IOException if a parsing error occurs
     */
    private static Set<GeneralNameInterface> parseNames(Collection> names) throws IOException {
        Set<GeneralNameInterface> genNames = new HashSet();
        for (List<?> nameList : names) {
            if (nameList.size() != 2) {
                throw new IOException("name list size not 2");
            }
            Object o =  nameList.get(0);
            if (!(o instanceof Integer)) {
                throw new IOException("expected an Integer");
            }
            int nameType = ((Integer)o).intValue();
            o = nameList.get(1);
            genNames.add(makeGeneralNameInterface(nameType, o));
        }

        return genNames;
    }

    /**
     * Compare for equality two objects of the form passed to
     * setSubjectAlternativeNames (or X509CRLSelector.setIssuerNames).
     * Throw an {@code IllegalArgumentException} or a
     * {@code ClassCastException} if one of the objects is malformed.
     *
     * @param object1 a Collection containing the first object to compare
     * @param object2 a Collection containing the second object to compare
     * @return true if the objects are equal, false otherwise
     */
    static boolean equalNames(Collection<?> object1, Collection object2) {
        if ((object1 == null) || (object2 == null)) {
            return object1 == object2;
        }
        return object1.equals(object2);
    }

    /**
     * Make a {@code GeneralNameInterface} out of a name type (0-8) and an
     * Object that may be a byte array holding the ASN.1 DER encoded
     * name or a String form of the name.  Except for X.509
     * Distinguished Names, the String form of the name must not be the
     * result from calling toString on an existing GeneralNameInterface
     * implementing class.  The output of toString is not compatible
     * with the String constructors for names other than Distinguished
     * Names.
     *
     * @param type name type (0-8)
     * @param name name as ASN.1 Der-encoded byte array or String
     * @return a GeneralNameInterface name
     * @throws IOException if a parsing error occurs
     */
    static GeneralNameInterface makeGeneralNameInterface(int type, Object name)
            throws IOException {
        GeneralNameInterface result;
        if (debug != null) {
            debug.println("X509CertSelector.makeGeneralNameInterface("
                + type + ")...");
        }

        if (name instanceof String) {
            if (debug != null) {
                debug.println("X509CertSelector.makeGeneralNameInterface() "
                    + "name is String: " + name);
            }
            switch (type) {
            case NAME_RFC822:
                result = new RFC822Name((String)name);
                break;
            case NAME_DNS:
                result = new DNSName((String)name);
                break;
            case NAME_DIRECTORY:
                result = new X500Name((String)name);
                break;
            case NAME_URI:
                result = new URIName((String)name);
                break;
            case NAME_IP:
                result = new IPAddressName((String)name);
                break;
            case NAME_OID:
                result = new OIDName((String)name);
                break;
            default:
                throw new IOException("unable to parse String names of type "
                                      + type);
            }
            if (debug != null) {
                debug.println("X509CertSelector.makeGeneralNameInterface() "
                    + "result: " + result.toString());
            }
        } else if (name instanceof byte[]) {
            DerValue val = new DerValue((byte[]) name);
            if (debug != null) {
                debug.println
                    ("X509CertSelector.makeGeneralNameInterface() is byte[]");
            }

            switch (type) {
            case NAME_ANY:
                result = new OtherName(val);
                break;
            case NAME_RFC822:
                result = new RFC822Name(val);
                break;
            case NAME_DNS:
                result = new DNSName(val);
                break;
            case NAME_X400:
                result = new X400Address(val);
                break;
            case NAME_DIRECTORY:
                result = new X500Name(val);
                break;
            case NAME_EDI:
                result = new EDIPartyName(val);
                break;
            case NAME_URI:
                result = new URIName(val);
                break;
            case NAME_IP:
                result = new IPAddressName(val);
                break;
            case NAME_OID:
                result = new OIDName(val);
                break;
            default:
                throw new IOException("unable to parse byte array names of "
                    + "type " + type);
            }
            if (debug != null) {
                debug.println("X509CertSelector.makeGeneralNameInterface() result: "
                    + result.toString());
            }
        } else {
            if (debug != null) {
                debug.println("X509CertSelector.makeGeneralName() input name "
                    + "not String or byte array");
            }
            throw new IOException("name not String or byte array");
        }
        return result;
    }


    /**
     * Sets the name constraints criterion. The {@code X509Certificate}
     * must have subject and subject alternative names that
     * meet the specified name constraints.
     * <p>
     * The name constraints are specified as a byte array. This byte array
     * should contain the DER encoded form of the name constraints, as they
     * would appear in the NameConstraints structure defined in RFC 3280
     * and X.509. The ASN.1 definition of this structure appears below.
     *
     * <pre>{@code
     *  NameConstraints ::= SEQUENCE {
     *       permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
     *       excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
     *
     *  GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
     *
     *  GeneralSubtree ::= SEQUENCE {
     *       base                    GeneralName,
     *       minimum         [0]     BaseDistance DEFAULT 0,
     *       maximum         [1]     BaseDistance OPTIONAL }
     *
     *  BaseDistance ::= INTEGER (0..MAX)
     *
     *  GeneralName ::= CHOICE {
     *       otherName                       [0]     OtherName,
     *       rfc822Name                      [1]     IA5String,
     *       dNSName                         [2]     IA5String,
     *       x400Address                     [3]     ORAddress,
     *       directoryName                   [4]     Name,
     *       ediPartyName                    [5]     EDIPartyName,
     *       uniformResourceIdentifier       [6]     IA5String,
     *       iPAddress                       [7]     OCTET STRING,
     *       registeredID                    [8]     OBJECT IDENTIFIER}
     * }</pre>
     * <p>
     * Note that the byte array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param bytes a byte array containing the ASN.1 DER encoding of
     *              a NameConstraints extension to be used for checking
     *              name constraints. Only the value of the extension is
     *              included, not the OID or criticality flag. Can be
     *              {@code null},
     *              in which case no name constraints check will be performed.
     * @throws IOException if a parsing error occurs
     * @see #getNameConstraints
     */
    public void setNameConstraints(byte[] bytes) throws IOException {
        if (bytes == null) {
            ncBytes = null;
            nc = null;
        } else {
            ncBytes = bytes.clone();
            nc = new NameConstraintsExtension(FALSE, bytes);
        }
    }

    /**
     * Sets the basic constraints constraint. If the value is greater than or
     * equal to zero, {@code X509Certificates} must include a
     * basicConstraints extension with
     * a pathLen of at least this value. If the value is -2, only end-entity
     * certificates are accepted. If the value is -1, no check is done.
     * <p>
     * This constraint is useful when building a certification path forward
     * (from the target toward the trust anchor. If a partial path has been
     * built, any candidate certificate must have a maxPathLen value greater
     * than or equal to the number of certificates in the partial path.
     *
     * @param minMaxPathLen the value for the basic constraints constraint
     * @throws IllegalArgumentException if the value is less than -2
     * @see #getBasicConstraints
     */
    public void setBasicConstraints(int minMaxPathLen) {
        if (minMaxPathLen < -2) {
            throw new IllegalArgumentException("basic constraints less than -2");
        }
        basicConstraints = minMaxPathLen;
    }

    /**
     * Sets the policy constraint. The {@code X509Certificate} must
     * include at least one of the specified policies in its certificate
     * policies extension. If {@code certPolicySet} is empty, then the
     * {@code X509Certificate} must include at least some specified policy
     * in its certificate policies extension. If {@code certPolicySet} is
     * {@code null}, no policy check will be performed.
     * <p>
     * Note that the {@code Set} is cloned to protect against
     * subsequent modifications.
     *
     * @param certPolicySet a {@code Set} of certificate policy OIDs in
     *                      string format (or {@code null}). Each OID is
     *                      represented by a set of nonnegative integers
     *                    separated by periods.
     * @throws IOException if a parsing error occurs on the OID such as
     * the first component is not 0, 1 or 2 or the second component is
     * greater than 39.
     * @see #getPolicy
     */
    public void setPolicy(Set<String> certPolicySet) throws IOException {
        if (certPolicySet == null) {
            policySet = null;
            policy = null;
        } else {
            // Snapshot set and parse it
            Set<String> tempSet = Collections.unmodifiableSet
                                        (new HashSet<String>(certPolicySet));
            /* Convert to Vector of ObjectIdentifiers */
            Iterator<String> i = tempSet.iterator();
            Vector<CertificatePolicyId> polIdVector = new Vector();
            while (i.hasNext()) {
                Object o = i.next();
                if (!(o instanceof String)) {
                    throw new IOException("non String in certPolicySet");
                }
                polIdVector.add(new CertificatePolicyId(new ObjectIdentifier(
                  (String)o)));
            }
            // If everything went OK, make the changes
            policySet = tempSet;
            policy = new CertificatePolicySet(polIdVector);
        }
    }

    /**
     * Sets the pathToNames criterion. The {@code X509Certificate} must
     * not include name constraints that would prohibit building a
     * path to the specified names.
     * <p>
     * This method allows the caller to specify, with a single method call,
     * the complete set of names which the {@code X509Certificates}'s
     * name constraints must permit. The specified value replaces
     * the previous value for the pathToNames criterion.
     * <p>
     * This constraint is useful when building a certification path forward
     * (from the target toward the trust anchor. If a partial path has been
     * built, any candidate certificate must not include name constraints that
     * would prohibit building a path to any of the names in the partial path.
     * <p>
     * The {@code names} parameter (if not {@code null}) is a
     * {@code Collection} with one
     * entry for each name to be included in the pathToNames
     * criterion. Each entry is a {@code List} whose first entry is an
     * {@code Integer} (the name type, 0-8) and whose second
     * entry is a {@code String} or a byte array (the name, in
     * string or ASN.1 DER encoded form, respectively).
     * There can be multiple names of the same type. If {@code null}
     * is supplied as the value for this argument, no
     * pathToNames check will be performed.
     * <p>
     * Each name in the {@code Collection}
     * may be specified either as a {@code String} or as an ASN.1 encoded
     * byte array. For more details about the formats used, see
     * {@link #addPathToName(int type, String name)
     * addPathToName(int type, String name)} and
     * {@link #addPathToName(int type, byte [] name)
     * addPathToName(int type, byte [] name)}.
     * <p>
     * <strong>Note: for distinguished names, specify the byte
     * array form instead of the String form. See the note in
     * {@link #addPathToName(int, String)} for more information.
     * <p>
     * Note that the {@code names} parameter can contain duplicate
     * names (same name and name type), but they may be removed from the
     * {@code Collection} of names returned by the
     * {@link #getPathToNames getPathToNames} method.
     * <p>
     * Note that a deep copy is performed on the {@code Collection} to
     * protect against subsequent modifications.
     *
     * @param names a {@code Collection} with one entry per name
     *              (or {@code null})
     * @throws IOException if a parsing error occurs
     * @see #getPathToNames
     */
    public void setPathToNames(Collection<List names) throws IOException {
        if ((names == null) || names.isEmpty()) {
            pathToNames = null;
            pathToGeneralNames = null;
        } else {
            Set<List tempNames = cloneAndCheckNames(names);
            pathToGeneralNames = parseNames(tempNames);
            // Ensure that we either set both of these or neither
            pathToNames = tempNames;
        }
    }

    // called from CertPathHelper
    void setPathToNamesInternal(Set<GeneralNameInterface> names) {
        // set names to non-null dummy value
        // this breaks getPathToNames()
        pathToNames = Collections.<ListemptySet();
        pathToGeneralNames = names;
    }

    /**
     * Adds a name to the pathToNames criterion. The {@code X509Certificate}
     * must not include name constraints that would prohibit building a
     * path to the specified name.
     * <p>
     * This method allows the caller to add a name to the set of names which
     * the {@code X509Certificates}'s name constraints must permit.
     * The specified name is added to any previous value for the
     * pathToNames criterion.  If the name is a duplicate, it may be ignored.
     * <p>
     * The name is provided in string format. RFC 822, DNS, and URI names
     * use the well-established string formats for those types (subject to
     * the restrictions included in RFC 3280). IPv4 address names are
     * supplied using dotted quad notation. OID address names are represented
     * as a series of nonnegative integers separated by periods. And
     * directory names (distinguished names) are supplied in RFC 2253 format.
     * No standard string format is defined for otherNames, X.400 names,
     * EDI party names, IPv6 address names, or any other type of names. They
     * should be specified using the
     * {@link #addPathToName(int type, byte [] name)
     * addPathToName(int type, byte [] name)} method.
     * <p>
     * <strong>Note: for distinguished names, use
     * {@linkplain #addPathToName(int, byte[])} instead.
     * This method should not be relied on as it can fail to match some
     * certificates because of a loss of encoding information in the RFC 2253
     * String form of some distinguished names.
     *
     * @param type the name type (0-8, as specified in
     *             RFC 3280, section 4.2.1.7)
     * @param name the name in string form
     * @throws IOException if a parsing error occurs
     */
    public void addPathToName(int type, String name) throws IOException {
        addPathToNameInternal(type, name);
    }

    /**
     * Adds a name to the pathToNames criterion. The {@code X509Certificate}
     * must not include name constraints that would prohibit building a
     * path to the specified name.
     * <p>
     * This method allows the caller to add a name to the set of names which
     * the {@code X509Certificates}'s name constraints must permit.
     * The specified name is added to any previous value for the
     * pathToNames criterion. If the name is a duplicate, it may be ignored.
     * <p>
     * The name is provided as a byte array. This byte array should contain
     * the DER encoded name, as it would appear in the GeneralName structure
     * defined in RFC 3280 and X.509. The ASN.1 definition of this structure
     * appears in the documentation for
     * {@link #addSubjectAlternativeName(int type, byte [] name)
     * addSubjectAlternativeName(int type, byte [] name)}.
     * <p>
     * Note that the byte array supplied here is cloned to protect against
     * subsequent modifications.
     *
     * @param type the name type (0-8, as specified in
     *             RFC 3280, section 4.2.1.7)
     * @param name a byte array containing the name in ASN.1 DER encoded form
     * @throws IOException if a parsing error occurs
     */
    public void addPathToName(int type, byte [] name) throws IOException {
        // clone because byte arrays are modifiable
        addPathToNameInternal(type, name.clone());
    }

    /**
     * A private method that adds a name (String or byte array) to the
     * pathToNames criterion. The {@code X509Certificate} must contain
     * the specified pathToName.
     *
     * @param type the name type (0-8, as specified in
     *             RFC 3280, section 4.2.1.7)
     * @param name the name in string or byte array form
     * @throws IOException if an encoding error occurs (incorrect form for DN)
     */
    private void addPathToNameInternal(int type, Object name)
            throws IOException {
        // First, ensure that the name parses
        GeneralNameInterface tempName = makeGeneralNameInterface(type, name);
        if (pathToGeneralNames == null) {
            pathToNames = new HashSet<List();
            pathToGeneralNames = new HashSet<GeneralNameInterface>();
        }
        List<Object> list = new ArrayList(2);
        list.add(Integer.valueOf(type));
        list.add(name);
        pathToNames.add(list);
        pathToGeneralNames.add(tempName);
    }

    /**
     * Returns the certificateEquals criterion. The specified
     * {@code X509Certificate} must be equal to the
     * {@code X509Certificate} passed to the {@code match} method.
     * If {@code null}, this check is not applied.
     *
     * @return the {@code X509Certificate} to match (or {@code null})
     * @see #setCertificate
     */
    public X509Certificate getCertificate() {
        return x509Cert;
    }

    /**
     * Returns the serialNumber criterion. The specified serial number
     * must match the certificate serial number in the
     * {@code X509Certificate}. If {@code null}, any certificate
     * serial number will do.
     *
     * @return the certificate serial number to match
     *                (or {@code null})
     * @see #setSerialNumber
     */
    public BigInteger getSerialNumber() {
        return serialNumber;
    }

    /**
     * Returns the issuer criterion as an {@code X500Principal}. This
     * distinguished name must match the issuer distinguished name in the
     * {@code X509Certificate}. If {@code null}, the issuer criterion
     * is disabled and any issuer distinguished name will do.
     *
     * @return the required issuer distinguished name as X500Principal
     *         (or {@code null})
     * @since 1.5
     */
    public X500Principal getIssuer() {
        return issuer;
    }

    /**
     * <strong>Denigrated, use {@linkplain #getIssuer()} or
     * {@linkplain #getIssuerAsBytes()} instead. This method should not be
     * relied on as it can fail to match some certificates because of a loss of
     * encoding information in the RFC 2253 String form of some distinguished
     * names.
     * <p>
     * Returns the issuer criterion as a {@code String}. This
     * distinguished name must match the issuer distinguished name in the
     * {@code X509Certificate}. If {@code null}, the issuer criterion
     * is disabled and any issuer distinguished name will do.
     * <p>
     * If the value returned is not {@code null}, it is a
     * distinguished name, in RFC 2253 format.
     *
     * @return the required issuer distinguished name in RFC 2253 format
     *         (or {@code null})
     */
    public String getIssuerAsString() {
        return (issuer == null ? null : issuer.getName());
    }

    /**
     * Returns the issuer criterion as a byte array. This distinguished name
     * must match the issuer distinguished name in the
     * {@code X509Certificate}. If {@code null}, the issuer criterion
     * is disabled and any issuer distinguished name will do.
     * <p>
     * If the value returned is not {@code null}, it is a byte
     * array containing a single DER encoded distinguished name, as defined in
     * X.501. The ASN.1 notation for this structure is supplied in the
     * documentation for
     * {@link #setIssuer(byte [] issuerDN) setIssuer(byte [] issuerDN)}.
     * <p>
     * Note that the byte array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return a byte array containing the required issuer distinguished name
     *         in ASN.1 DER format (or {@code null})
     * @throws IOException if an encoding error occurs
     */
    public byte[] getIssuerAsBytes() throws IOException {
        return (issuer == null ? null: issuer.getEncoded());
    }

    /**
     * Returns the subject criterion as an {@code X500Principal}. This
     * distinguished name must match the subject distinguished name in the
     * {@code X509Certificate}. If {@code null}, the subject criterion
     * is disabled and any subject distinguished name will do.
     *
     * @return the required subject distinguished name as X500Principal
     *         (or {@code null})
     * @since 1.5
     */
    public X500Principal getSubject() {
        return subject;
    }

    /**
     * <strong>Denigrated, use {@linkplain #getSubject()} or
     * {@linkplain #getSubjectAsBytes()} instead. This method should not be
     * relied on as it can fail to match some certificates because of a loss of
     * encoding information in the RFC 2253 String form of some distinguished
     * names.
     * <p>
     * Returns the subject criterion as a {@code String}. This
     * distinguished name must match the subject distinguished name in the
     * {@code X509Certificate}. If {@code null}, the subject criterion
     * is disabled and any subject distinguished name will do.
     * <p>
     * If the value returned is not {@code null}, it is a
     * distinguished name, in RFC 2253 format.
     *
     * @return the required subject distinguished name in RFC 2253 format
     *         (or {@code null})
     */
    public String getSubjectAsString() {
        return (subject == null ? null : subject.getName());
    }

    /**
     * Returns the subject criterion as a byte array. This distinguished name
     * must match the subject distinguished name in the
     * {@code X509Certificate}. If {@code null}, the subject criterion
     * is disabled and any subject distinguished name will do.
     * <p>
     * If the value returned is not {@code null}, it is a byte
     * array containing a single DER encoded distinguished name, as defined in
     * X.501. The ASN.1 notation for this structure is supplied in the
     * documentation for
     * {@link #setSubject(byte [] subjectDN) setSubject(byte [] subjectDN)}.
     * <p>
     * Note that the byte array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return a byte array containing the required subject distinguished name
     *         in ASN.1 DER format (or {@code null})
     * @throws IOException if an encoding error occurs
     */
    public byte[] getSubjectAsBytes() throws IOException {
        return (subject == null ? null : subject.getEncoded());
    }

    /**
     * Returns the subjectKeyIdentifier criterion. The
     * {@code X509Certificate} must contain a SubjectKeyIdentifier
     * extension with the specified value. If {@code null}, no
     * subjectKeyIdentifier check will be done.
     * <p>
     * Note that the byte array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return the key identifier (or {@code null})
     * @see #setSubjectKeyIdentifier
     */
    public byte[] getSubjectKeyIdentifier() {
        if (subjectKeyID == null) {
            return null;
        }
        return subjectKeyID.clone();
    }

    /**
     * Returns the authorityKeyIdentifier criterion. The
     * {@code X509Certificate} must contain a AuthorityKeyIdentifier
     * extension with the specified value. If {@code null}, no
     * authorityKeyIdentifier check will be done.
     * <p>
     * Note that the byte array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return the key identifier (or {@code null})
     * @see #setAuthorityKeyIdentifier
     */
    public byte[] getAuthorityKeyIdentifier() {
        if (authorityKeyID == null) {
          return null;
        }
        return authorityKeyID.clone();
    }

    /**
     * Returns the certificateValid criterion. The specified date must fall
     * within the certificate validity period for the
     * {@code X509Certificate}. If {@code null}, no certificateValid
     * check will be done.
     * <p>
     * Note that the {@code Date} returned is cloned to protect against
     * subsequent modifications.
     *
     * @return the {@code Date} to check (or {@code null})
     * @see #setCertificateValid
     */
    public Date getCertificateValid() {
        if (certificateValid == null) {
            return null;
        }
        return (Date)certificateValid.clone();
    }

    /**
     * Returns the privateKeyValid criterion. The specified date must fall
     * within the private key validity period for the
     * {@code X509Certificate}. If {@code null}, no privateKeyValid
     * check will be done.
     * <p>
     * Note that the {@code Date} returned is cloned to protect against
     * subsequent modifications.
     *
     * @return the {@code Date} to check (or {@code null})
     * @see #setPrivateKeyValid
     */
    public Date getPrivateKeyValid() {
        if (privateKeyValid == null) {
            return null;
        }
        return (Date)privateKeyValid.clone();
    }

    /**
     * Returns the subjectPublicKeyAlgID criterion. The
     * {@code X509Certificate} must contain a subject public key
     * with the specified algorithm. If {@code null}, no
     * subjectPublicKeyAlgID check will be done.
     *
     * @return the object identifier (OID) of the signature algorithm to check
     *         for (or {@code null}). An OID is represented by a set of
     *         nonnegative integers separated by periods.
     * @see #setSubjectPublicKeyAlgID
     */
    public String getSubjectPublicKeyAlgID() {
        if (subjectPublicKeyAlgID == null) {
            return null;
        }
        return subjectPublicKeyAlgID.toString();
    }

    /**
     * Returns the subjectPublicKey criterion. The
     * {@code X509Certificate} must contain the specified subject
     * public key. If {@code null}, no subjectPublicKey check will be done.
     *
     * @return the subject public key to check for (or {@code null})
     * @see #setSubjectPublicKey
     */
    public PublicKey getSubjectPublicKey() {
        return subjectPublicKey;
    }

    /**
     * Returns the keyUsage criterion. The {@code X509Certificate}
     * must allow the specified keyUsage values. If null, no keyUsage
     * check will be done.
     * <p>
     * Note that the boolean array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return a boolean array in the same format as the boolean
     *                 array returned by
     * {@link X509Certificate#getKeyUsage() X509Certificate.getKeyUsage()}.
     *                 Or {@code null}.
     * @see #setKeyUsage
     */
    public boolean[] getKeyUsage() {
        if (keyUsage == null) {
            return null;
        }
        return keyUsage.clone();
    }

    /**
     * Returns the extendedKeyUsage criterion. The {@code X509Certificate}
     * must allow the specified key purposes in its extended key usage
     * extension. If the {@code keyPurposeSet} returned is empty or
     * {@code null}, no extendedKeyUsage check will be done. Note that an
     * {@code X509Certificate} that has no extendedKeyUsage extension
     * implicitly allows all key purposes.
     *
     * @return an immutable {@code Set} of key purpose OIDs in string
     * format (or {@code null})
     * @see #setExtendedKeyUsage
     */
    public Set<String> getExtendedKeyUsage() {
        return keyPurposeSet;
    }

    /**
     * Indicates if the {@code X509Certificate} must contain all
     * or at least one of the subjectAlternativeNames
     * specified in the {@link #setSubjectAlternativeNames
     * setSubjectAlternativeNames} or {@link #addSubjectAlternativeName
     * addSubjectAlternativeName} methods. If {@code true},
     * the {@code X509Certificate} must contain all of the
     * specified subject alternative names. If {@code false}, the
     * {@code X509Certificate} must contain at least one of the
     * specified subject alternative names.
     *
     * @return {@code true} if the flag is enabled;
     * {@code false} if the flag is disabled. The flag is
     * {@code true} by default.
     * @see #setMatchAllSubjectAltNames
     */
    public boolean getMatchAllSubjectAltNames() {
        return matchAllSubjectAltNames;
    }

    /**
     * Returns a copy of the subjectAlternativeNames criterion.
     * The {@code X509Certificate} must contain all or at least one
     * of the specified subjectAlternativeNames, depending on the value
     * of the matchAllNames flag (see {@link #getMatchAllSubjectAltNames
     * getMatchAllSubjectAltNames}). If the value returned is
     * {@code null}, no subjectAlternativeNames check will be performed.
     * <p>
     * If the value returned is not {@code null}, it is a
     * {@code Collection} with
     * one entry for each name to be included in the subject alternative name
     * criterion. Each entry is a {@code List} whose first entry is an
     * {@code Integer} (the name type, 0-8) and whose second
     * entry is a {@code String} or a byte array (the name, in
     * string or ASN.1 DER encoded form, respectively).
     * There can be multiple names of the same type.  Note that the
     * {@code Collection} returned may contain duplicate names (same name
     * and name type).
     * <p>
     * Each subject alternative name in the {@code Collection}
     * may be specified either as a {@code String} or as an ASN.1 encoded
     * byte array. For more details about the formats used, see
     * {@link #addSubjectAlternativeName(int type, String name)
     * addSubjectAlternativeName(int type, String name)} and
     * {@link #addSubjectAlternativeName(int type, byte [] name)
     * addSubjectAlternativeName(int type, byte [] name)}.
     * <p>
     * Note that a deep copy is performed on the {@code Collection} to
     * protect against subsequent modifications.
     *
     * @return a {@code Collection} of names (or {@code null})
     * @see #setSubjectAlternativeNames
     */
    public Collection<List getSubjectAlternativeNames() {
        if (subjectAlternativeNames == null) {
            return null;
        }
        return cloneNames(subjectAlternativeNames);
    }

    /**
     * Clone an object of the form passed to
     * setSubjectAlternativeNames and setPathToNames.
     * Throw a {@code RuntimeException} if the argument is malformed.
     * <p>
     * This method wraps cloneAndCheckNames, changing any
     * {@code IOException} into a {@code RuntimeException}. This
     * method should be used when the object being
     * cloned has already been checked, so there should never be any exceptions.
     *
     * @param names a {@code Collection} with one entry per name.
     *              Each entry is a {@code List} whose first entry
     *              is an Integer (the name type, 0-8) and whose second
     *              entry is a String or a byte array (the name, in
     *              string or ASN.1 DER encoded form, respectively).
     *              There can be multiple names of the same type. Null
     *              is not an acceptable value.
     * @return a deep copy of the specified {@code Collection}
     * @throws RuntimeException if a parsing error occurs
     */
    private static Set<List cloneNames(Collection> names) {
        try {
            return cloneAndCheckNames(names);
        } catch (IOException e) {
            throw new RuntimeException("cloneNames encountered IOException: " +
                                       e.getMessage());
        }
    }

    /**
     * Clone and check an argument of the form passed to
     * setSubjectAlternativeNames and setPathToNames.
     * Throw an {@code IOException} if the argument is malformed.
     *
     * @param names a {@code Collection} with one entry per name.
     *              Each entry is a {@code List} whose first entry
     *              is an Integer (the name type, 0-8) and whose second
     *              entry is a String or a byte array (the name, in
     *              string or ASN.1 DER encoded form, respectively).
     *              There can be multiple names of the same type.
     *              {@code null} is not an acceptable value.
     * @return a deep copy of the specified {@code Collection}
     * @throws IOException if a parsing error occurs
     */
    private static Set<List cloneAndCheckNames(Collection> names) throws IOException {
        // Copy the Lists and Collection
        Set<List namesCopy = new HashSet>();
        for (List<?> o : names)
        {
            namesCopy.add(new ArrayList<Object>(o));
        }

        // Check the contents of the Lists and clone any byte arrays
        for (List<?> list : namesCopy) {
            @SuppressWarnings("unchecked") // See javadoc for parameter "names".
            List<Object> nameList = (List)list;
            if (nameList.size() != 2) {
                throw new IOException("name list size not 2");
            }
            Object o = nameList.get(0);
            if (!(o instanceof Integer)) {
                throw new IOException("expected an Integer");
            }
            int nameType = ((Integer)o).intValue();
            if ((nameType < 0) || (nameType > 8)) {
                throw new IOException("name type not 0-8");
            }
            Object nameObject = nameList.get(1);
            if (!(nameObject instanceof byte[]) &&
                !(nameObject instanceof String)) {
                if (debug != null) {
                    debug.println("X509CertSelector.cloneAndCheckNames() "
                        + "name not byte array");
                }
                throw new IOException("name not byte array or String");
            }
            if (nameObject instanceof byte[]) {
                nameList.set(1, ((byte[]) nameObject).clone());
            }
        }
        return namesCopy;
    }

    /**
     * Returns the name constraints criterion. The {@code X509Certificate}
     * must have subject and subject alternative names that
     * meet the specified name constraints.
     * <p>
     * The name constraints are returned as a byte array. This byte array
     * contains the DER encoded form of the name constraints, as they
     * would appear in the NameConstraints structure defined in RFC 3280
     * and X.509. The ASN.1 notation for this structure is supplied in the
     * documentation for
     * {@link #setNameConstraints(byte [] bytes) setNameConstraints(byte [] bytes)}.
     * <p>
     * Note that the byte array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return a byte array containing the ASN.1 DER encoding of
     *         a NameConstraints extension used for checking name constraints.
     *         {@code null} if no name constraints check will be performed.
     * @see #setNameConstraints
     */
    public byte[] getNameConstraints() {
        if (ncBytes == null) {
            return null;
        } else {
            return ncBytes.clone();
        }
    }

    /**
     * Returns the basic constraints constraint. If the value is greater than
     * or equal to zero, the {@code X509Certificates} must include a
     * basicConstraints extension with a pathLen of at least this value.
     * If the value is -2, only end-entity certificates are accepted. If
     * the value is -1, no basicConstraints check is done.
     *
     * @return the value for the basic constraints constraint
     * @see #setBasicConstraints
     */
    public int getBasicConstraints() {
        return basicConstraints;
    }

    /**
     * Returns the policy criterion. The {@code X509Certificate} must
     * include at least one of the specified policies in its certificate policies
     * extension. If the {@code Set} returned is empty, then the
     * {@code X509Certificate} must include at least some specified policy
     * in its certificate policies extension. If the {@code Set} returned is
     * {@code null}, no policy check will be performed.
     *
     * @return an immutable {@code Set} of certificate policy OIDs in
     *         string format (or {@code null})
     * @see #setPolicy
     */
    public Set<String> getPolicy() {
        return policySet;
    }

    /**
     * Returns a copy of the pathToNames criterion. The
     * {@code X509Certificate} must not include name constraints that would
     * prohibit building a path to the specified names. If the value
     * returned is {@code null}, no pathToNames check will be performed.
     * <p>
     * If the value returned is not {@code null}, it is a
     * {@code Collection} with one
     * entry for each name to be included in the pathToNames
     * criterion. Each entry is a {@code List} whose first entry is an
     * {@code Integer} (the name type, 0-8) and whose second
     * entry is a {@code String} or a byte array (the name, in
     * string or ASN.1 DER encoded form, respectively).
     * There can be multiple names of the same type. Note that the
     * {@code Collection} returned may contain duplicate names (same
     * name and name type).
     * <p>
     * Each name in the {@code Collection}
     * may be specified either as a {@code String} or as an ASN.1 encoded
     * byte array. For more details about the formats used, see
     * {@link #addPathToName(int type, String name)
     * addPathToName(int type, String name)} and
     * {@link #addPathToName(int type, byte [] name)
     * addPathToName(int type, byte [] name)}.
     * <p>
     * Note that a deep copy is performed on the {@code Collection} to
     * protect against subsequent modifications.
     *
     * @return a {@code Collection} of names (or {@code null})
     * @see #setPathToNames
     */
    public Collection<List getPathToNames() {
        if (pathToNames == null) {
            return null;
        }
        return cloneNames(pathToNames);
    }

    /**
     * Return a printable representation of the {@code CertSelector}.
     *
     * @return a {@code String} describing the contents of the
     *         {@code CertSelector}
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("X509CertSelector: [\n");
        if (x509Cert != null) {
            sb.append("  Certificate: " + x509Cert.toString() + "\n");
        }
        if (serialNumber != null) {
            sb.append("  Serial Number: " + serialNumber.toString() + "\n");
        }
        if (issuer != null) {
            sb.append("  Issuer: " + getIssuerAsString() + "\n");
        }
        if (subject != null) {
            sb.append("  Subject: " + getSubjectAsString() + "\n");
        }
        sb.append("  matchAllSubjectAltNames flag: "
                  + String.valueOf(matchAllSubjectAltNames) + "\n");
        if (subjectAlternativeNames != null) {
            sb.append("  SubjectAlternativeNames:\n");
            Iterator<List i = subjectAlternativeNames.iterator();
            while (i.hasNext()) {
                List<?> list = i.next();
                sb.append("    type " + list.get(0) +
                          ", name " + list.get(1) + "\n");
            }
        }
        if (subjectKeyID != null) {
            HexDumpEncoder enc = new HexDumpEncoder();
            sb.append("  Subject Key Identifier: " +
                      enc.encodeBuffer(subjectKeyID) + "\n");
        }
        if (authorityKeyID != null) {
            HexDumpEncoder enc = new HexDumpEncoder();
            sb.append("  Authority Key Identifier: " +
                      enc.encodeBuffer(authorityKeyID) + "\n");
        }
        if (certificateValid != null) {
            sb.append("  Certificate Valid: " +
                      certificateValid.toString() + "\n");
        }
        if (privateKeyValid != null) {
            sb.append("  Private Key Valid: " +
                      privateKeyValid.toString() + "\n");
        }
        if (subjectPublicKeyAlgID != null) {
            sb.append("  Subject Public Key AlgID: " +
                      subjectPublicKeyAlgID.toString() + "\n");
        }
        if (subjectPublicKey != null) {
            sb.append("  Subject Public Key: " +
                      subjectPublicKey.toString() + "\n");
        }
        if (keyUsage != null) {
            sb.append("  Key Usage: " + keyUsageToString(keyUsage) + "\n");
        }
        if (keyPurposeSet != null) {
            sb.append("  Extended Key Usage: " +
                      keyPurposeSet.toString() + "\n");
        }
        if (policy != null) {
            sb.append("  Policy: " + policy.toString() + "\n");
        }
        if (pathToGeneralNames != null) {
            sb.append("  Path to names:\n");
            Iterator<GeneralNameInterface> i = pathToGeneralNames.iterator();
            while (i.hasNext()) {
                sb.append("    " + i.next() + "\n");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    // Copied from sun.security.x509.KeyUsageExtension
    // (without calling the superclass)
    /**
     * Returns a printable representation of the KeyUsage.
     */
    private static String keyUsageToString(boolean[] k) {
        String s = "KeyUsage [\n";
        try {
            if (k[0]) {
                s += "  DigitalSignature\n";
            }
            if (k[1]) {
                s += "  Non_repudiation\n";
            }
            if (k[2]) {
                s += "  Key_Encipherment\n";
            }
            if (k[3]) {
                s += "  Data_Encipherment\n";
            }
            if (k[4]) {
                s += "  Key_Agreement\n";
            }
            if (k[5]) {
                s += "  Key_CertSign\n";
            }
            if (k[6]) {
                s += "  Crl_Sign\n";
            }
            if (k[7]) {
                s += "  Encipher_Only\n";
            }
            if (k[8]) {
                s += "  Decipher_Only\n";
            }
        } catch (ArrayIndexOutOfBoundsException ex) {}

        s += "]\n";

        return (s);
    }

    /**
     * Returns an Extension object given any X509Certificate and extension oid.
     * Throw an {@code IOException} if the extension byte value is
     * malformed.
     *
     * @param cert a {@code X509Certificate}
     * @param extId an {@code integer} which specifies the extension index.
     * Currently, the supported extensions are as follows:
     * index 0 - PrivateKeyUsageExtension
     * index 1 - SubjectAlternativeNameExtension
     * index 2 - NameConstraintsExtension
     * index 3 - CertificatePoliciesExtension
     * index 4 - ExtendedKeyUsageExtension
     * @return an {@code Extension} object whose real type is as specified
     * by the extension oid.
     * @throws IOException if cannot construct the {@code Extension}
     * object with the extension encoding retrieved from the passed in
     * {@code X509Certificate}.
     */
    private static Extension getExtensionObject(X509Certificate cert, int extId)
            throws IOException {
        if (cert instanceof X509CertImpl) {
            X509CertImpl impl = (X509CertImpl)cert;
            switch (extId) {
            case PRIVATE_KEY_USAGE_ID:
                return impl.getPrivateKeyUsageExtension();
            case SUBJECT_ALT_NAME_ID:
                return impl.getSubjectAlternativeNameExtension();
            case NAME_CONSTRAINTS_ID:
                return impl.getNameConstraintsExtension();
            case CERT_POLICIES_ID:
                return impl.getCertificatePoliciesExtension();
            case EXTENDED_KEY_USAGE_ID:
                return impl.getExtendedKeyUsageExtension();
            default:
                return null;
            }
        }
        byte[] rawExtVal = cert.getExtensionValue(EXTENSION_OIDS[extId]);
        if (rawExtVal == null) {
            return null;
        }
        DerInputStream in = new DerInputStream(rawExtVal);
        byte[] encoded = in.getOctetString();
        switch (extId) {
        case PRIVATE_KEY_USAGE_ID:
            try {
                return new PrivateKeyUsageExtension(FALSE, encoded);
            } catch (CertificateException ex) {
                throw new IOException(ex.getMessage());
            }
        case SUBJECT_ALT_NAME_ID:
            return new SubjectAlternativeNameExtension(FALSE, encoded);
        case NAME_CONSTRAINTS_ID:
            return new NameConstraintsExtension(FALSE, encoded);
        case CERT_POLICIES_ID:
            return new CertificatePoliciesExtension(FALSE, encoded);
        case EXTENDED_KEY_USAGE_ID:
            return new ExtendedKeyUsageExtension(FALSE, encoded);
        default:
            return null;
        }
    }

    /**
     * Decides whether a {@code Certificate} should be selected.
     *
     * @param cert the {@code Certificate} to be checked
     * @return {@code true} if the {@code Certificate} should be
     *         selected, {@code false} otherwise
     */
    public boolean match(Certificate cert) {
        if (!(cert instanceof X509Certificate)) {
            return false;
        }
        X509Certificate xcert = (X509Certificate)cert;

        if (debug != null) {
            debug.println("X509CertSelector.match(SN: "
                + (xcert.getSerialNumber()).toString(16) + "\n  Issuer: "
                + xcert.getIssuerDN() + "\n  Subject: " + xcert.getSubjectDN()
                + ")");
        }

        /* match on X509Certificate */
        if (x509Cert != null) {
            if (!x509Cert.equals(xcert)) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "certs don't match");
                }
                return false;
            }
        }

        /* match on serial number */
        if (serialNumber != null) {
            if (!serialNumber.equals(xcert.getSerialNumber())) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "serial numbers don't match");
                }
                return false;
            }
        }

        /* match on issuer name */
        if (issuer != null) {
            if (!issuer.equals(xcert.getIssuerX500Principal())) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "issuer DNs don't match");
                }
                return false;
            }
        }

        /* match on subject name */
        if (subject != null) {
            if (!subject.equals(xcert.getSubjectX500Principal())) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "subject DNs don't match");
                }
                return false;
            }
        }

        /* match on certificate validity range */
        if (certificateValid != null) {
            try {
                xcert.checkValidity(certificateValid);
            } catch (CertificateException e) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "certificate not within validity period");
                }
                return false;
            }
        }

        /* match on subject public key */
        if (subjectPublicKeyBytes != null) {
            byte[] certKey = xcert.getPublicKey().getEncoded();
            if (!Arrays.equals(subjectPublicKeyBytes, certKey)) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "subject public keys don't match");
                }
                return false;
            }
        }

        boolean result = matchBasicConstraints(xcert)
                      && matchKeyUsage(xcert)
                      && matchExtendedKeyUsage(xcert)
                      && matchSubjectKeyID(xcert)
                      && matchAuthorityKeyID(xcert)
                      && matchPrivateKeyValid(xcert)
                      && matchSubjectPublicKeyAlgID(xcert)
                      && matchPolicy(xcert)
                      && matchSubjectAlternativeNames(xcert)
                      && matchPathToNames(xcert)
                      && matchNameConstraints(xcert);

        if (result && (debug != null)) {
            debug.println("X509CertSelector.match returning: true");
        }
        return result;
    }

    /* match on subject key identifier extension value */
    private boolean matchSubjectKeyID(X509Certificate xcert) {
        if (subjectKeyID == null) {
            return true;
        }
        try {
            byte[] extVal = xcert.getExtensionValue("2.5.29.14");
            if (extVal == null) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "no subject key ID extension");
                }
                return false;
            }
            DerInputStream in = new DerInputStream(extVal);
            byte[] certSubjectKeyID = in.getOctetString();
            if (certSubjectKeyID == null ||
                    !Arrays.equals(subjectKeyID, certSubjectKeyID)) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "subject key IDs don't match");
                }
                return false;
            }
        } catch (IOException ex) {
            if (debug != null) {
                debug.println("X509CertSelector.match: "
                    + "exception in subject key ID check");
            }
            return false;
        }
        return true;
    }

    /* match on authority key identifier extension value */
    private boolean matchAuthorityKeyID(X509Certificate xcert) {
        if (authorityKeyID == null) {
            return true;
        }
        try {
            byte[] extVal = xcert.getExtensionValue("2.5.29.35");
            if (extVal == null) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "no authority key ID extension");
                }
                return false;
            }
            DerInputStream in = new DerInputStream(extVal);
            byte[] certAuthKeyID = in.getOctetString();
            if (certAuthKeyID == null ||
                    !Arrays.equals(authorityKeyID, certAuthKeyID)) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "authority key IDs don't match");
                }
                return false;
            }
        } catch (IOException ex) {
            if (debug != null) {
                debug.println("X509CertSelector.match: "
                    + "exception in authority key ID check");
            }
            return false;
        }
        return true;
    }

    /* match on private key usage range */
    private boolean matchPrivateKeyValid(X509Certificate xcert) {
        if (privateKeyValid == null) {
            return true;
        }
        PrivateKeyUsageExtension ext = null;
        try {
            ext = (PrivateKeyUsageExtension)
                getExtensionObject(xcert, PRIVATE_KEY_USAGE_ID);
            if (ext != null) {
                ext.valid(privateKeyValid);
            }
        } catch (CertificateExpiredException e1) {
            if (debug != null) {
                String time = "n/a";
                try {
                    Date notAfter = ext.get(PrivateKeyUsageExtension.NOT_AFTER);
                    time = notAfter.toString();
                } catch (CertificateException ex) {
                    // not able to retrieve notAfter value
                }
                debug.println("X509CertSelector.match: private key usage not "
                    + "within validity date; ext.NOT_After: "
                    + time + "; X509CertSelector: "
                    + this.toString());
                e1.printStackTrace();
            }
            return false;
        } catch (CertificateNotYetValidException e2) {
            if (debug != null) {
                String time = "n/a";
                try {
                    Date notBefore = ext.get(PrivateKeyUsageExtension.NOT_BEFORE);
                    time = notBefore.toString();
                } catch (CertificateException ex) {
                    // not able to retrieve notBefore value
                }
                debug.println("X509CertSelector.match: private key usage not "
                    + "within validity date; ext.NOT_BEFORE: "
                    + time + "; X509CertSelector: "
                    + this.toString());
                e2.printStackTrace();
            }
            return false;
        } catch (IOException e4) {
            if (debug != null) {
                debug.println("X509CertSelector.match: IOException in "
                    + "private key usage check; X509CertSelector: "
                    + this.toString());
                e4.printStackTrace();
            }
            return false;
        }
        return true;
    }

    /* match on subject public key algorithm OID */
    private boolean matchSubjectPublicKeyAlgID(X509Certificate xcert) {
        if (subjectPublicKeyAlgID == null) {
            return true;
        }
        try {
            byte[] encodedKey = xcert.getPublicKey().getEncoded();
            DerValue val = new DerValue(encodedKey);
            if (val.tag != DerValue.tag_Sequence) {
                throw new IOException("invalid key format");
            }

            AlgorithmId algID = AlgorithmId.parse(val.data.getDerValue());
            if (debug != null) {
                debug.println("X509CertSelector.match: subjectPublicKeyAlgID = "
                    + subjectPublicKeyAlgID + ", xcert subjectPublicKeyAlgID = "
                    + algID.getOID());
            }
            if (!subjectPublicKeyAlgID.equals((Object)algID.getOID())) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "subject public key alg IDs don't match");
                }
                return false;
            }
        } catch (IOException e5) {
            if (debug != null) {
                debug.println("X509CertSelector.match: IOException in subject "
                    + "public key algorithm OID check");
            }
            return false;
        }
        return true;
    }

    /* match on key usage extension value */
    private boolean matchKeyUsage(X509Certificate xcert) {
        if (keyUsage == null) {
            return true;
        }
        boolean[] certKeyUsage = xcert.getKeyUsage();
        if (certKeyUsage != null) {
            for (int keyBit = 0; keyBit < keyUsage.length; keyBit++) {
                if (keyUsage[keyBit] &&
                    ((keyBit >= certKeyUsage.length) || !certKeyUsage[keyBit])) {
                    if (debug != null) {
                        debug.println("X509CertSelector.match: "
                            + "key usage bits don't match");
                    }
                    return false;
                }
            }
        }
        return true;
    }

    /* match on extended key usage purpose OIDs */
    private boolean matchExtendedKeyUsage(X509Certificate xcert) {
        if ((keyPurposeSet == null) || keyPurposeSet.isEmpty()) {
            return true;
        }
        try {
            ExtendedKeyUsageExtension ext =
                (ExtendedKeyUsageExtension)getExtensionObject(xcert,
                                                EXTENDED_KEY_USAGE_ID);
            if (ext != null) {
                Vector<ObjectIdentifier> certKeyPurposeVector =
                    ext.get(ExtendedKeyUsageExtension.USAGES);
                if (!certKeyPurposeVector.contains(ANY_EXTENDED_KEY_USAGE)
                        && !certKeyPurposeVector.containsAll(keyPurposeOIDSet)) {
                    if (debug != null) {
                        debug.println("X509CertSelector.match: cert failed "
                            + "extendedKeyUsage criterion");
                    }
                    return false;
                }
            }
        } catch (IOException ex) {
            if (debug != null) {
                debug.println("X509CertSelector.match: "
                    + "IOException in extended key usage check");
            }
            return false;
        }
        return true;
    }

    /* match on subject alternative name extension names */
    private boolean matchSubjectAlternativeNames(X509Certificate xcert) {
        if ((subjectAlternativeNames == null) || subjectAlternativeNames.isEmpty()) {
            return true;
        }
        try {
            SubjectAlternativeNameExtension sanExt =
                (SubjectAlternativeNameExtension) getExtensionObject(xcert,
                                                      SUBJECT_ALT_NAME_ID);
            if (sanExt == null) {
                if (debug != null) {
                  debug.println("X509CertSelector.match: "
                      + "no subject alternative name extension");
                }
                return false;
            }
            GeneralNames certNames =
                    sanExt.get(SubjectAlternativeNameExtension.SUBJECT_NAME);
            Iterator<GeneralNameInterface> i =
                                subjectAlternativeGeneralNames.iterator();
            while (i.hasNext()) {
                GeneralNameInterface matchName = i.next();
                boolean found = false;
                for (Iterator<GeneralName> t = certNames.iterator();
                                                t.hasNext() && !found; ) {
                    GeneralNameInterface certName = (t.next()).getName();
                    found = certName.equals(matchName);
                }
                if (!found && (matchAllSubjectAltNames || !i.hasNext())) {
                    if (debug != null) {
                      debug.println("X509CertSelector.match: subject alternative "
                          + "name " + matchName + " not found");
                    }
                    return false;
                } else if (found && !matchAllSubjectAltNames) {
                    break;
                }
            }
        } catch (IOException ex) {
            if (debug != null)
                debug.println("X509CertSelector.match: IOException in subject "
                    + "alternative name check");
            return false;
        }
        return true;
    }

    /* match on name constraints */
    private boolean matchNameConstraints(X509Certificate xcert) {
        if (nc == null) {
            return true;
        }
        try {
            if (!nc.verify(xcert)) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: "
                        + "name constraints not satisfied");
                }
                return false;
            }
        } catch (IOException e) {
            if (debug != null) {
                debug.println("X509CertSelector.match: "
                    + "IOException in name constraints check");
            }
            return false;
        }
        return true;
    }

    /* match on policy OIDs */
    private boolean matchPolicy(X509Certificate xcert) {
        if (policy == null) {
            return true;
        }
        try {
            CertificatePoliciesExtension ext = (CertificatePoliciesExtension)
                getExtensionObject(xcert, CERT_POLICIES_ID);
            if (ext == null) {
                if (debug != null) {
                  debug.println("X509CertSelector.match: "
                      + "no certificate policy extension");
                }
                return false;
            }
            List<PolicyInformation> policies = ext.get(CertificatePoliciesExtension.POLICIES);
            /*
             * Convert the Vector of PolicyInformation to a Vector
             * of CertificatePolicyIds for easier comparison.
             */
            List<CertificatePolicyId> policyIDs = new ArrayList(policies.size());
            for (PolicyInformation info : policies) {
                policyIDs.add(info.getPolicyIdentifier());
            }
            if (policy != null) {
                boolean foundOne = false;
                /*
                 * if the user passes in an empty policy Set, then
                 * we just want to make sure that the candidate certificate
                 * has some policy OID in its CertPoliciesExtension
                 */
                if (policy.getCertPolicyIds().isEmpty()) {
                    if (policyIDs.isEmpty()) {
                        if (debug != null) {
                            debug.println("X509CertSelector.match: "
                                + "cert failed policyAny criterion");
                        }
                        return false;
                    }
                } else {
                    for (CertificatePolicyId id : policy.getCertPolicyIds()) {
                        if (policyIDs.contains(id)) {
                            foundOne = true;
                            break;
                        }
                    }
                    if (!foundOne) {
                        if (debug != null) {
                            debug.println("X509CertSelector.match: "
                                + "cert failed policyAny criterion");
                        }
                        return false;
                    }
                }
            }
        } catch (IOException ex) {
            if (debug != null) {
                debug.println("X509CertSelector.match: "
                    + "IOException in certificate policy ID check");
            }
            return false;
        }
        return true;
    }

    /* match on pathToNames */
    private boolean matchPathToNames(X509Certificate xcert) {
        if (pathToGeneralNames == null) {
            return true;
        }
        try {
            NameConstraintsExtension ext = (NameConstraintsExtension)
                getExtensionObject(xcert, NAME_CONSTRAINTS_ID);
            if (ext == null) {
                return true;
            }
            if ((debug != null) && Debug.isOn("certpath")) {
                debug.println("X509CertSelector.match pathToNames:\n");
                Iterator<GeneralNameInterface> i =
                                        pathToGeneralNames.iterator();
                while (i.hasNext()) {
                    debug.println("    " + i.next() + "\n");
                }
            }

            GeneralSubtrees permitted =
                    ext.get(NameConstraintsExtension.PERMITTED_SUBTREES);
            GeneralSubtrees excluded =
                    ext.get(NameConstraintsExtension.EXCLUDED_SUBTREES);
            if (excluded != null) {
                if (matchExcluded(excluded) == false) {
                    return false;
                }
            }
            if (permitted != null) {
                if (matchPermitted(permitted) == false) {
                    return false;
                }
            }
        } catch (IOException ex) {
            if (debug != null) {
                debug.println("X509CertSelector.match: "
                    + "IOException in name constraints check");
            }
            return false;
        }
        return true;
    }

    private boolean matchExcluded(GeneralSubtrees excluded) {
        /*
         * Enumerate through excluded and compare each entry
         * to all pathToNames. If any pathToName is within any of the
         * subtrees listed in excluded, return false.
         */
        for (Iterator<GeneralSubtree> t = excluded.iterator(); t.hasNext(); ) {
            GeneralSubtree tree = t.next();
            GeneralNameInterface excludedName = tree.getName().getName();
            Iterator<GeneralNameInterface> i = pathToGeneralNames.iterator();
            while (i.hasNext()) {
                GeneralNameInterface pathToName = i.next();
                if (excludedName.getType() == pathToName.getType()) {
                    switch (pathToName.constrains(excludedName)) {
                    case GeneralNameInterface.NAME_WIDENS:
                    case GeneralNameInterface.NAME_MATCH:
                        if (debug != null) {
                            debug.println("X509CertSelector.match: name constraints "
                                + "inhibit path to specified name");
                            debug.println("X509CertSelector.match: excluded name: " +
                                pathToName);
                        }
                        return false;
                    default:
                    }
                }
            }
        }
        return true;
    }

    private boolean matchPermitted(GeneralSubtrees permitted) {
        /*
         * Enumerate through pathToNames, checking that each pathToName
         * is in at least one of the subtrees listed in permitted.
         * If not, return false. However, if no subtrees of a given type
         * are listed, all names of that type are permitted.
         */
        Iterator<GeneralNameInterface> i = pathToGeneralNames.iterator();
        while (i.hasNext()) {
            GeneralNameInterface pathToName = i.next();
            Iterator<GeneralSubtree> t = permitted.iterator();
            boolean permittedNameFound = false;
            boolean nameTypeFound = false;
            String names = "";
            while (t.hasNext() && !permittedNameFound) {
                GeneralSubtree tree = t.next();
                GeneralNameInterface permittedName = tree.getName().getName();
                if (permittedName.getType() == pathToName.getType()) {
                    nameTypeFound = true;
                    names = names + "  " + permittedName;
                    switch (pathToName.constrains(permittedName)) {
                    case GeneralNameInterface.NAME_WIDENS:
                    case GeneralNameInterface.NAME_MATCH:
                        permittedNameFound = true;
                        break;
                    default:
                    }
                }
            }
            if (!permittedNameFound && nameTypeFound) {
                if (debug != null)
                  debug.println("X509CertSelector.match: " +
                            "name constraints inhibit path to specified name; " +
                            "permitted names of type " + pathToName.getType() +
                            ": " + names);
                return false;
            }
        }
        return true;
    }

    /* match on basic constraints */
    private boolean matchBasicConstraints(X509Certificate xcert) {
        if (basicConstraints == -1) {
            return true;
        }
        int maxPathLen = xcert.getBasicConstraints();
        if (basicConstraints == -2) {
            if (maxPathLen != -1) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: not an EE cert");
                }
                return false;
            }
        } else {
            if (maxPathLen < basicConstraints) {
                if (debug != null) {
                    debug.println("X509CertSelector.match: maxPathLen too small ("
                        + maxPathLen + " < " + basicConstraints + ")");
                }
                return false;
            }
        }
        return true;
    }

    @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
    private static <T> Set cloneSet(Set set) {
        if (set instanceof HashSet) {
            Object clone = ((HashSet<T>)set).clone();
            return (Set<T>)clone;
        } else {
            return new HashSet<T>(set);
        }
    }

    /**
     * Returns a copy of this object.
     *
     * @return the copy
     */
    public Object clone() {
        try {
            X509CertSelector copy = (X509CertSelector)super.clone();
            // Must clone these because addPathToName et al. modify them
            if (subjectAlternativeNames != null) {
                copy.subjectAlternativeNames =
                        cloneSet(subjectAlternativeNames);
                copy.subjectAlternativeGeneralNames =
                        cloneSet(subjectAlternativeGeneralNames);
            }
            if (pathToGeneralNames != null) {
                copy.pathToNames = cloneSet(pathToNames);
                copy.pathToGeneralNames = cloneSet(pathToGeneralNames);
            }
            return copy;
        } catch (CloneNotSupportedException e) {
            /* Cannot happen */
            throw new InternalError(e.toString(), e);
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java X509CertSelector.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.