/* * Copyright (c) 2000, 2006, 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</code> that selects <code>X509Certificates</code> that * match all specified criteria. This class is particularly useful when * selecting certificates from a <code>CertStore</code> to build a * PKIX-compliant certification path. * <p> * When first constructed, an <code>X509CertSelector</code> has no criteria * enabled and each of the <code>get</code> methods return a default value * (<code>null</code>, or <code>-1</code> for the {@link #getBasicConstraints * getBasicConstraints} method). Therefore, the {@link #match match} * method would return <code>true</code> for any <code>X509Certificate</code>. * Typically, several criteria are enabled (by calling * {@link #setIssuer setIssuer} or * {@link #setKeyUsage setKeyUsage}, for instance) and then the * <code>X509CertSelector</code> 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</code> method * usually uniquely matches a single <code>X509Certificate</code>. 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</b> * <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</code>. Initially, no criteria are set * so any <code>X509Certificate</code> will match. */ public X509CertSelector() { // empty } /** * Sets the certificateEquals criterion. The specified * <code>X509Certificate</code> must be equal to the * <code>X509Certificate</code> passed to the <code>match</code> method. * If <code>null</code>, 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</code> to match (or * <code>null</code>) * @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</code>. If <code>null</code>, any certificate * serial number will do. * * @param serial the certificate serial number to match * (or <code>null</code>) * @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</code>. If <code>null</code>, any issuer * distinguished name will do. * * @param issuer a distinguished name as X500Principal * (or <code>null</code>) * @since 1.5 */ public void setIssuer(X500Principal issuer) { this.issuer = issuer; } /** * <strong>Denigrated</strong>, 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</a> 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</code>. If <code>null</code>, any issuer * distinguished name will do. * <p> * If <code>issuerDN</code> is not <code>null</code>, it should contain a * distinguished name, in RFC 2253 format. * * @param issuerDN a distinguished name in RFC 2253 format * (or <code>null</code>) * @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</code>. If <code>null</code> is specified, * the issuer criterion is disabled and any issuer distinguished name will * do. * <p> * If <code>issuerDN</code> is not <code>null</code>, 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)) } * </code></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</code>) * @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 (IOException)new IOException("Invalid name").initCause(e); } } /** * Sets the subject criterion. The specified distinguished name * must match the subject distinguished name in the * <code>X509Certificate</code>. If <code>null</code>, any subject * distinguished name will do. * * @param subject a distinguished name as X500Principal * (or <code>null</code>) * @since 1.5 */ public void setSubject(X500Principal subject) { this.subject = subject; } /** * <strong>Denigrated</strong>, 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</code>. If <code>null</code>, any subject * distinguished name will do. * <p> * If <code>subjectDN</code> is not <code>null</code>, it should contain a * distinguished name, in RFC 2253 format. * * @param subjectDN a distinguished name in RFC 2253 format * (or <code>null</code>) * @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</code>. If <code>null</code>, any subject * distinguished name will do. * <p> * If <code>subjectDN</code> is not <code>null</code>, 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</code>) * @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 (IOException)new IOException("Invalid name").initCause(e); } } /** * Sets the subjectKeyIdentifier criterion. The * <code>X509Certificate</code> must contain a SubjectKeyIdentifier * extension for which the contents of the extension * matches the specified criterion value. * If the criterion value is <code>null</code>, no * subjectKeyIdentifier check will be done. * <p> * If <code>subjectKeyID</code> is not <code>null</code>, 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. * <p> * <pre><code> * SubjectKeyIdentifier ::= KeyIdentifier * * KeyIdentifier ::= OCTET STRING * </code></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</code>. 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</code>) * @see #getSubjectKeyIdentifier */ public void setSubjectKeyIdentifier(byte[] subjectKeyID) { if (subjectKeyID == null) { this.subjectKeyID = null; } else { this.subjectKeyID = (byte[])subjectKeyID.clone(); } } /** * Sets the authorityKeyIdentifier criterion. The * <code>X509Certificate</code> 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</code>, no * authorityKeyIdentifier check will be done. * <p> * If <code>authorityKeyID</code> is not <code>null</code>, 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. * <p> * <pre><code> * AuthorityKeyIdentifier ::= SEQUENCE { * keyIdentifier [0] KeyIdentifier OPTIONAL, * authorityCertIssuer [1] GeneralNames OPTIONAL, * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } * * KeyIdentifier ::= OCTET STRING * </code></pre> * <p> * Authority key identifiers are not parsed by the * <code>X509CertSelector</code>. Instead, the values are * compared using a byte-by-byte comparison. * <p> * When the <code>keyIdentifier</code> field of * <code>AuthorityKeyIdentifier</code> is populated, the value is * usually taken from the <code>SubjectKeyIdentifier</code> extension * in the issuer's certificate. Note, however, that the result of * <code>X509Certificate.getExtensionValue(<SubjectKeyIdentifier Object * Identifier>)</code> on the issuer's certificate may NOT be used * directly as the input to <code>setAuthorityKeyIdentifier</code>. * 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</code> * extension, it will be necessary to extract the value of the embedded * <code>KeyIdentifier</code> 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</code>) * @see #getAuthorityKeyIdentifier */ public void setAuthorityKeyIdentifier(byte[] authorityKeyID) { if (authorityKeyID == null) { this.authorityKeyID = null; } else { this.authorityKeyID = (byte[])authorityKeyID.clone(); } } /** * Sets the certificateValid criterion. The specified date must fall * within the certificate validity period for the * <code>X509Certificate</code>. If <code>null</code>, no certificateValid * check will be done. * <p> * Note that the <code>Date</code> supplied here is cloned to protect * against subsequent modifications. * * @param certValid the <code>Date</code> to check (or <code>null</code>) * @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</code>. If <code>null</code>, no privateKeyValid * check will be done. * <p> * Note that the <code>Date</code> supplied here is cloned to protect * against subsequent modifications. * * @param privateKeyValid the <code>Date</code> to check (or * <code>null</code>) * @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</code> must contain a subject public key * with the specified algorithm. If <code>null</code>, no * subjectPublicKeyAlgID check will be done. * * @param oid The object identifier (OID) of the algorithm to check * for (or <code>null</code>). 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</code> must contain the specified subject public * key. If <code>null</code>, no subjectPublicKey check will be done. * * @param key the subject public key to check for (or <code>null</code>) * @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</code> * must contain the specified subject public key. If <code>null</code>, * 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</code> is not <code>null</code>, 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 * </code></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</code>) * @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 = (byte[])key.clone(); subjectPublicKey = X509Key.parse(new DerValue(subjectPublicKeyBytes)); } } /** * Sets the keyUsage criterion. The <code>X509Certificate</code> * must allow the specified keyUsage values. If <code>null</code>, no * keyUsage check will be done. Note that an <code>X509Certificate</code> * 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</code>. * @see #getKeyUsage */ public void setKeyUsage(boolean[] keyUsage) { if (keyUsage == null) { this.keyUsage = null; } else { this.keyUsage = (boolean[])keyUsage.clone(); } } /** * Sets the extendedKeyUsage criterion. The <code>X509Certificate</code> * must allow the specified key purposes in its extended key usage * extension. If <code>keyPurposeSet</code> is empty or <code>null</code>, * no extendedKeyUsage check will be done. Note that an * <code>X509Certificate</code> that has no extendedKeyUsage extension * implicitly allows all key purposes. * <p> * Note that the <code>Set</code> is cloned to protect against * subsequent modifications. * * @param keyPurposeSet a <code>Set</code> of key purpose OIDs in string * format (or <code>null</code>). 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</code> must contain all of the * specified subject alternative names. If disabled, the * <code>X509Certificate</code> must contain at least one of the * specified subject alternative names. * * <p>The matchAllNames flag is <code>true</code> by default. * * @param matchAllNames if <code>true</code>, the flag is enabled; * if <code>false</code>, the flag is disabled. * @see #getMatchAllSubjectAltNames */ public void setMatchAllSubjectAltNames(boolean matchAllNames) { this.matchAllSubjectAltNames = matchAllNames; } /** * Sets the subjectAlternativeNames criterion. The * <code>X509Certificate</code> 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</code> parameter (if not <code>null</code>) is a * <code>Collection</code> with one * entry for each name to be included in the subject alternative name * criterion. Each entry is a <code>List</code> whose first entry is an * <code>Integer</code> (the name type, 0-8) and whose second * entry is a <code>String</code> 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</code> * is supplied as the value for this argument, no * subjectAlternativeNames check will be performed. * <p> * Each subject alternative name in the <code>Collection</code> * may be specified either as a <code>String</code> 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:</strong> 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</code> parameter can contain duplicate * names (same name and name type), but they may be removed from the * <code>Collection</code> of names returned by the * {@link #getSubjectAlternativeNames getSubjectAlternativeNames} method. * <p> * Note that a deep copy is performed on the <code>Collection</code> to * protect against subsequent modifications. * * @param names a <code>Collection</code> of names (or <code>null</code>) * @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</code> 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</a>, 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:</strong> 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</code>) * @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</code> 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} * </code></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</code> * 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<Object>(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</code> of * <code>GeneralNameInterface</code>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</code> 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</code>s * @throws IOException if a parsing error occurs */ private static Set<GeneralNameInterface> parseNames(Collection<List<?>> names) throws IOException { Set<GeneralNameInterface> genNames = new HashSet<GeneralNameInterface>(); 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</code> or a * <code>ClassCastException</code> 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</code> 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</code> * 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} * </code></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</code>, * 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 = (byte[])bytes.clone(); nc = new NameConstraintsExtension(FALSE, bytes); } } /** * Sets the basic constraints constraint. If the value is greater than or * equal to zero, <code>X509Certificates</code> 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</code> must * include at least one of the specified policies in its certificate * policies extension. If <code>certPolicySet</code> is empty, then the * <code>X509Certificate</code> must include at least some specified policy * in its certificate policies extension. If <code>certPolicySet</code> is * <code>null</code>, no policy check will be performed. * <p> * Note that the <code>Set</code> is cloned to protect against * subsequent modifications. * * @param certPolicySet a <code>Set</code> of certificate policy OIDs in * string format (or <code>null</code>). 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<CertificatePolicyId>(); 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</code> 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</code>'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</code> parameter (if not <code>null</code>) is a * <code>Collection</code> with one * entry for each name to be included in the pathToNames * criterion. Each entry is a <code>List</code> whose first entry is an * <code>Integer</code> (the name type, 0-8) and whose second * entry is a <code>String</code> 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</code> * is supplied as the value for this argument, no * pathToNames check will be performed. * <p> * Each name in the <code>Collection</code> * may be specified either as a <code>String</code> 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:</strong> 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</code> parameter can contain duplicate * names (same name and name type), but they may be removed from the * <code>Collection</code> of names returned by the * {@link #getPathToNames getPathToNames} method. * <p> * Note that a deep copy is performed on the <code>Collection</code> to * protect against subsequent modifications. * * @param names a <code>Collection</code> with one entry per name * (or <code>null</code>) * @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.<List<?>>emptySet(); pathToGeneralNames = names; } /** * Adds a name to the pathToNames criterion. The <code>X509Certificate</code> * 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</code>'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:</strong> 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</code> * 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</code>'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</code> 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<Object>(2); list.add(Integer.valueOf(type)); list.add(name); pathToNames.add(list); pathToGeneralNames.add(tempName); } /** * Returns the certificateEquals criterion. The specified * <code>X509Certificate</code> must be equal to the * <code>X509Certificate</code> passed to the <code>match</code> method. * If <code>null</code>, this check is not applied. * * @return the <code>X509Certificate</code> to match (or <code>null</code>) * @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</code>. If <code>null</code>, any certificate * serial number will do. * * @return the certificate serial number to match * (or <code>null</code>) * @see #setSerialNumber */ public BigInteger getSerialNumber() { return serialNumber; } /** * Returns the issuer criterion as an <code>X500Principal</code>. This * distinguished name must match the issuer distinguished name in the * <code>X509Certificate</code>. If <code>null</code>, the issuer criterion * is disabled and any issuer distinguished name will do. * * @return the required issuer distinguished name as X500Principal * (or <code>null</code>) * @since 1.5 */ public X500Principal getIssuer() { return issuer; } /** * <strong>Denigrated</strong>, 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</code>. This * distinguished name must match the issuer distinguished name in the * <code>X509Certificate</code>. If <code>null</code>, the issuer criterion * is disabled and any issuer distinguished name will do. * <p> * If the value returned is not <code>null</code>, it is a * distinguished name, in RFC 2253 format. * * @return the required issuer distinguished name in RFC 2253 format * (or <code>null</code>) */ 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</code>. If <code>null</code>, the issuer criterion * is disabled and any issuer distinguished name will do. * <p> * If the value returned is not <code>null</code>, 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</code>) * @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</code>. This * distinguished name must match the subject distinguished name in the * <code>X509Certificate</code>. If <code>null</code>, the subject criterion * is disabled and any subject distinguished name will do. * * @return the required subject distinguished name as X500Principal * (or <code>null</code>) * @since 1.5 */ public X500Principal getSubject() { return subject; } /** * <strong>Denigrated</strong>, 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</code>. This * distinguished name must match the subject distinguished name in the * <code>X509Certificate</code>. If <code>null</code>, the subject criterion * is disabled and any subject distinguished name will do. * <p> * If the value returned is not <code>null</code>, it is a * distinguished name, in RFC 2253 format. * * @return the required subject distinguished name in RFC 2253 format * (or <code>null</code>) */ 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</code>. If <code>null</code>, the subject criterion * is disabled and any subject distinguished name will do. * <p> * If the value returned is not <code>null</code>, 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</code>) * @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</code> must contain a SubjectKeyIdentifier * extension with the specified value. If <code>null</code>, 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</code>) * @see #setSubjectKeyIdentifier */ public byte[] getSubjectKeyIdentifier() { if (subjectKeyID == null) { return null; } return (byte[])subjectKeyID.clone(); } /** * Returns the authorityKeyIdentifier criterion. The * <code>X509Certificate</code> must contain a AuthorityKeyIdentifier * extension with the specified value. If <code>null</code>, 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</code>) * @see #setAuthorityKeyIdentifier */ public byte[] getAuthorityKeyIdentifier() { if (authorityKeyID == null) { return null; } return (byte[])authorityKeyID.clone(); } /** * Returns the certificateValid criterion. The specified date must fall * within the certificate validity period for the * <code>X509Certificate</code>. If <code>null</code>, no certificateValid * check will be done. * <p> * Note that the <code>Date</code> returned is cloned to protect against * subsequent modifications. * * @return the <code>Date</code> to check (or <code>null</code>) * @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</code>. If <code>null</code>, no privateKeyValid * check will be done. * <p> * Note that the <code>Date</code> returned is cloned to protect against * subsequent modifications. * * @return the <code>Date</code> to check (or <code>null</code>) * @see #setPrivateKeyValid */ public Date getPrivateKeyValid() { if (privateKeyValid == null) { return null; } return (Date)privateKeyValid.clone(); } /** * Returns the subjectPublicKeyAlgID criterion. The * <code>X509Certificate</code> must contain a subject public key * with the specified algorithm. If <code>null</code>, no * subjectPublicKeyAlgID check will be done. * * @return the object identifier (OID) of the signature algorithm to check * for (or <code>null</code>). 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</code> must contain the specified subject * public key. If <code>null</code>, no subjectPublicKey check will be done. * * @return the subject public key to check for (or <code>null</code>) * @see #setSubjectPublicKey */ public PublicKey getSubjectPublicKey() { return subjectPublicKey; } /** * Returns the keyUsage criterion. The <code>X509Certificate</code> * 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</code>. * @see #setKeyUsage */ public boolean[] getKeyUsage() { if (keyUsage == null) { return null; } return (boolean[])keyUsage.clone(); } /** * Returns the extendedKeyUsage criterion. The <code>X509Certificate</code> * must allow the specified key purposes in its extended key usage * extension. If the <code>keyPurposeSet</code> returned is empty or * <code>null</code>, no extendedKeyUsage check will be done. Note that an * <code>X509Certificate</code> that has no extendedKeyUsage extension * implicitly allows all key purposes. * * @return an immutable <code>Set</code> of key purpose OIDs in string * format (or <code>null</code>) * @see #setExtendedKeyUsage */ public Set<String> getExtendedKeyUsage() { return keyPurposeSet; } /** * Indicates if the <code>X509Certificate</code> must contain all * or at least one of the subjectAlternativeNames * specified in the {@link #setSubjectAlternativeNames * setSubjectAlternativeNames} or {@link #addSubjectAlternativeName * addSubjectAlternativeName} methods. If <code>true</code>, * the <code>X509Certificate</code> must contain all of the * specified subject alternative names. If <code>false</code>, the * <code>X509Certificate</code> must contain at least one of the * specified subject alternative names. * * @return <code>true</code> if the flag is enabled; * <code>false</code> if the flag is disabled. The flag is * <code>true</code> by default. * @see #setMatchAllSubjectAltNames */ public boolean getMatchAllSubjectAltNames() { return matchAllSubjectAltNames; } /** * Returns a copy of the subjectAlternativeNames criterion. * The <code>X509Certificate</code> 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</code>, no subjectAlternativeNames check will be performed. * <p> * If the value returned is not <code>null</code>, it is a * <code>Collection</code> with * one entry for each name to be included in the subject alternative name * criterion. Each entry is a <code>List</code> whose first entry is an * <code>Integer</code> (the name type, 0-8) and whose second * entry is a <code>String</code> 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</code> returned may contain duplicate names (same name * and name type). * <p> * Each subject alternative name in the <code>Collection</code> * may be specified either as a <code>String</code> 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</code> to * protect against subsequent modifications. * * @return a <code>Collection</code> of names (or <code>null</code>) * @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</code> if the argument is malformed. * <p> * This method wraps cloneAndCheckNames, changing any * <code>IOException</code> into a <code>RuntimeException</code>. 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</code> with one entry per name. * Each entry is a <code>List</code> 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</code> * @throws RuntimeException if a parsing error occurs */ private static Set<List<?>> cloneNames(Collection<List<?>> 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</code> if the argument is malformed. * * @param names a <code>Collection</code> with one entry per name. * Each entry is a <code>List</code> 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</code> is not an acceptable value. * @return a deep copy of the specified <code>Collection</code> * @throws IOException if a parsing error occurs */ private static Set<List<?>> cloneAndCheckNames(Collection<List<?>> names) throws IOException { // Copy the Lists and Collection Set<List<?>> namesCopy = new HashSet<List<?>>(); Iterator<List<?>> i = names.iterator(); while (i.hasNext()) { Object o = i.next(); if (!(o instanceof List)) { throw new IOException("expected a List"); } namesCopy.add(new ArrayList<Object>((List<?>)o)); } // Check the contents of the Lists and clone any byte arrays i = namesCopy.iterator(); while (i.hasNext()) { List<Object> nameList = (List<Object>)i.next(); 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</code> * 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</code> if no name constraints check will be performed. * @see #setNameConstraints */ public byte[] getNameConstraints() { if (ncBytes == null) { return null; } else { return (byte[]) ncBytes.clone(); } } /** * Returns the basic constraints constraint. If the value is greater than * or equal to zero, the <code>X509Certificates</code> 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</code> must * include at least one of the specified policies in its certificate policies * extension. If the <code>Set</code> returned is empty, then the * <code>X509Certificate</code> must include at least some specified policy * in its certificate policies extension. If the <code>Set</code> returned is * <code>null</code>, no policy check will be performed. * * @return an immutable <code>Set</code> of certificate policy OIDs in * string format (or <code>null</code>) * @see #setPolicy */ public Set<String> getPolicy() { return policySet; } /** * Returns a copy of the pathToNames criterion. The * <code>X509Certificate</code> must not include name constraints that would * prohibit building a path to the specified names. If the value * returned is <code>null</code>, no pathToNames check will be performed. * <p> * If the value returned is not <code>null</code>, it is a * <code>Collection</code> with one * entry for each name to be included in the pathToNames * criterion. Each entry is a <code>List</code> whose first entry is an * <code>Integer</code> (the name type, 0-8) and whose second * entry is a <code>String</code> 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</code> returned may contain duplicate names (same * name and name type). * <p> * Each name in the <code>Collection</code> * may be specified either as a <code>String</code> 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</code> to * protect against subsequent modifications. * * @return a <code>Collection</code> of names (or <code>null</code>) * @see #setPathToNames */ public Collection<List<?>> getPathToNames() { if (pathToNames == null) { return null; } return cloneNames(pathToNames); } /** * Return a printable representation of the <code>CertSelector</code>. * * @return a <code>String</code> describing the contents of the * <code>CertSelector</code> */ 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</code> if the extension byte value is * malformed. * * @param cert a <code>X509Certificate</code> * @param extId an <code>integer</code> 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</code> object whose real type is as specified * by the extension oid. * @throws IOException if cannot construct the <code>Extension</code> * object with the extension encoding retrieved from the passed in * <code>X509Certificate</code>. */ 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</code> should be selected. * * @param cert the <code>Certificate</code> to be checked * @return <code>true</code> if the <code>Certificate</code> should be * selected, <code>false</code> otherwise */ 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 = (Date)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 = (Date) 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 (CertificateException e3) { if (debug != null) { debug.println("X509CertSelector.match: CertificateException " + "in private key usage check; X509CertSelector: " + this.toString()); e3.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(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 = (Vector<ObjectIdentifier>)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 = (GeneralNames) 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 = (List<PolicyInformation>)ext.get(CertificatePoliciesExtension.POLICIES); /* * Convert the Vector of PolicyInformation to a Vector * of CertificatePolicyIds for easier comparison. */ List<CertificatePolicyId> policyIDs = new ArrayList<CertificatePolicyId>(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 = (GeneralSubtrees) ext.get(NameConstraintsExtension.PERMITTED_SUBTREES); GeneralSubtrees excluded = (GeneralSubtrees) 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; } private static Set<?> cloneSet(Set<?> set) { if (set instanceof HashSet) { Object clone = ((HashSet<?>)set).clone(); return (Set<?>)clone; } else { return new HashSet<Object>(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 = (Set<List<?>>)cloneSet(subjectAlternativeNames); copy.subjectAlternativeGeneralNames = (Set<GeneralNameInterface>)cloneSet (subjectAlternativeGeneralNames); } if (pathToGeneralNames != null) { copy.pathToNames = (Set<List<?>>)cloneSet(pathToNames); copy.pathToGeneralNames = (Set<GeneralNameInterface>)cloneSet (pathToGeneralNames); } return copy; } catch (CloneNotSupportedException e) { /* Cannot happen */ throw new InternalError(e.toString()); } } }