package org.bouncycastle.jce.cert; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Set; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERGeneralizedTime; import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.asn1.util.ASN1Dump; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509Extensions; import org.bouncycastle.asn1.x509.X509Name; import org.bouncycastle.jce.PrincipalUtil; import org.bouncycastle.util.Integers; /** * A <code>CertSelector</code> that selects * <code>X509Certificates that match all * specified criteria. This class is particularly useful when * selecting certificates from a CertStore to build a PKIX-compliant * certification path.<br /> * <br /> * When first constructed, an <code>X509CertSelector</code> has no criteria enabled * and each of the get methods return a default value (<code>null</code>, or -1 for * the {@link #getBasicConstraints} method). Therefore, the {@link #match} method would * return true for any <code>X509Certificate</code>. Typically, several criteria * are enabled (by calling {@link #setIssuer} or {@link #setKeyUsage}, for instance) and * then the <code>X509CertSelector</code> is passed to {@link CertStore#getCertificates} or * some similar method.<br /> * <br /> * Several criteria can be enabled (by calling {@link #setIssuer} and * {@link #setSerialNumber}, for example) such that the match 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.<br /> * <br /> * Please refer to RFC 2459 for definitions of the X.509 certificate * extensions mentioned below.<br /> * <br /> * <b>Concurrent Access</b><br /> * <br /> * 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.<br /> * <br /> * <b>TODO: implement name constraints</b> * <b>TODO: implement match check for path to names</b><br /> * <br /> * Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream}, * {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence}, * {@link org.bouncycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier}, * {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream}, * {@link org.bouncycastle.asn1.ASN1Object ASN1Object}, * {@link org.bouncycastle.asn1.OIDTokenizer OIDTokenizer}, * {@link org.bouncycastle.asn1.x509.X509Name X509Name}, * {@link org.bouncycastle.asn1.x509.X509Extensions X509Extensions}, * {@link org.bouncycastle.asn1.x509.ExtendedKeyUsage ExtendedKeyUsage}, * {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId}, * {@link org.bouncycastle.asn1.x509.SubjectPublicKeyInfo SubjectPublicKeyInfo}, * {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier AlgorithmIdentifier} */ public class X509CertSelector implements CertSelector { private static final Hashtable keyPurposeIdMap = new Hashtable(); static { keyPurposeIdMap.put(KeyPurposeId.id_kp_serverAuth.getId(), KeyPurposeId.id_kp_serverAuth); keyPurposeIdMap.put(KeyPurposeId.id_kp_clientAuth.getId(), KeyPurposeId.id_kp_clientAuth); keyPurposeIdMap.put(KeyPurposeId.id_kp_codeSigning.getId(), KeyPurposeId.id_kp_codeSigning); keyPurposeIdMap.put(KeyPurposeId.id_kp_emailProtection.getId(), KeyPurposeId.id_kp_emailProtection); keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecEndSystem.getId(), KeyPurposeId.id_kp_ipsecEndSystem); keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecTunnel.getId(), KeyPurposeId.id_kp_ipsecTunnel); keyPurposeIdMap.put(KeyPurposeId.id_kp_ipsecUser.getId(), KeyPurposeId.id_kp_ipsecUser); keyPurposeIdMap.put(KeyPurposeId.id_kp_timeStamping.getId(), KeyPurposeId.id_kp_timeStamping); } private X509Certificate x509Cert = null; private BigInteger serialNumber = null; private Object issuerDN = null; private X509Name issuerDNX509 = null; private Object subjectDN = null; private X509Name subjectDNX509 = null; private byte[] subjectKeyID = null; private byte[] authorityKeyID = null; private Date certValid = null; private Date privateKeyValid = null; private ASN1ObjectIdentifier subjectKeyAlgID = null; private PublicKey subjectPublicKey = null; private byte[] subjectPublicKeyByte = null; private boolean[] keyUsage = null; private Set keyPurposeSet = null; private boolean matchAllSubjectAltNames = true; private Set subjectAltNames = null; private Set subjectAltNamesByte = null; private int minMaxPathLen = -1; private Set policy = null; private Set policyOID = null; private Set pathToNames = null; private Set pathToNamesByte = null; /** * Creates an <code>X509CertSelector</code>. Initially, no criteria are * set so any <code>X509Certificate</code> will match. */ public X509CertSelector() { } /** * Sets the certificateEquals criterion. The specified * <code>X509Certificate</code> must be equal to the * <code>X509Certificate</code> passed to the match method. If * <code>null</code>, then this check is not applied.<br /> * <br /> * 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 X509Certificate 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.<br /> * <br /> * If <code>issuerDN</code> is not <code>null</code>, it should contain * a distinguished name, in RFC 2253 format.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for parsing the * issuerDN. * * @param issuerDN * a distinguished name in RFC 2253 format (or <code>null</code>) * * @exception IOException * if a parsing error occurs (incorrect form for DN) */ public void setIssuer(String issuerDN) throws IOException { if (issuerDN == null) { this.issuerDN = null; this.issuerDNX509 = null; } else { X509Name nameX509; try { nameX509 = new X509Name(issuerDN); } catch (IllegalArgumentException ex) { throw new IOException(ex.getMessage()); } this.issuerDNX509 = nameX509; this.issuerDN = issuerDN; } } /** * Sets the issuer criterion. The specified distinguished name must match * the issuer distinguished name in the <code>X509Certificate</code>. If * null is specified, the issuer criterion is disabled and any issuer * distinguished name will do.<br /> * <br /> * 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.<br /> * <br /> * * <pre> * Name ::= CHOICE { * RDNSequence } * * RDNSequence ::= SEQUENCE OF RDN * * RDN ::= * 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> * * <br /> * <br /> * Note that the byte array specified here is cloned to protect against * subsequent modifications.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream}, * {@link org.bouncycastle.asn1.ASN1Object ASN1Object}, * {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence}, * {@link org.bouncycastle.asn1.x509.X509Name X509Name} * * @param issuerDN - * a byte array containing the distinguished name in ASN.1 DER * encoded form (or <code>null</code>) * * @exception IOException * if an encoding error occurs (incorrect form for DN) */ public void setIssuer(byte[] issuerDN) throws IOException { if (issuerDN == null) { this.issuerDN = null; this.issuerDNX509 = null; } else { ByteArrayInputStream inStream = new ByteArrayInputStream(issuerDN); ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Object obj = derInStream.readObject(); if (obj instanceof ASN1Sequence) { this.issuerDNX509 = new X509Name((ASN1Sequence)obj); } else { throw new IOException("parsing error"); } this.issuerDN = (byte[])issuerDN.clone(); } } /** * Sets the subject criterion. The specified distinguished name must match * the subject distinguished name in the <code>X509Certificate</code>. If * null, any subject distinguished name will do.<br /> * <br /> * If <code>subjectDN</code> is not <code>null</code>, it should * contain a distinguished name, in RFC 2253 format.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for parsing the * subjectDN. * * @param subjectDN * a distinguished name in RFC 2253 format (or <code>null</code>) * * @exception IOException * if a parsing error occurs (incorrect form for DN) */ public void setSubject(String subjectDN) throws IOException { if (subjectDN == null) { this.subjectDN = null; this.subjectDNX509 = null; } else { X509Name nameX509; try { nameX509 = new X509Name(subjectDN); } catch (IllegalArgumentException ex) { throw new IOException(ex.getMessage()); } this.subjectDNX509 = nameX509; this.subjectDN = subjectDN; } } /** * Sets the subject criterion. The specified distinguished name must match * the subject distinguished name in the <code>X509Certificate</code>. If * null, any subject distinguished name will do.<br /> * <br /> * 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 []) setIssuer(byte [] issuerDN)}.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream}, * {@link org.bouncycastle.asn1.ASN1Object ASN1Object}, * {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence}, * {@link org.bouncycastle.asn1.x509.X509Name X509Name} * * @param subjectDN * a byte array containing the distinguished name in ASN.1 DER * format (or <code>null</code>) * * @exception IOException * if an encoding error occurs (incorrect form for DN) */ public void setSubject(byte[] subjectDN) throws IOException { if (subjectDN == null) { this.subjectDN = null; this.subjectDNX509 = null; } else { ByteArrayInputStream inStream = new ByteArrayInputStream(subjectDN); ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Object obj = derInStream.readObject(); if (obj instanceof ASN1Sequence) { this.subjectDNX509 = new X509Name((ASN1Sequence)obj); } else { throw new IOException("parsing error"); } this.subjectDN = (byte[])subjectDN.clone(); } } /** * 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 null, no subjectKeyIdentifier check will be done.<br /> * <br /> * 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.<br /> * <br /> * * <pre> * SubjectKeyIdentifier ::= KeyIdentifier * * KeyIdentifier ::= OCTET STRING * </pre> * * <br /> * <br /> * 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.<br /> * <br /> * 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.<br /> * <br /> * 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.<br /> * <br /> * * <pre> * AuthorityKeyIdentifier ::= SEQUENCE { * keyIdentifier [0] KeyIdentifier OPTIONAL, * authorityCertIssuer [1] GeneralNames OPTIONAL, * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } * * KeyIdentifier ::= OCTET STRING * </pre> * * <br /> * <br /> * Authority key identifiers are not parsed by the * <code>X509CertSelector</code>. Instead, the values are compared using * a byte-by-byte comparison.<br /> * <br /> * When the <code>keyIdentifier</code> field of * <code>AuthorityKeyIdentifier</code> is populated, the value is usually * taken from the SubjectKeyIdentifier extension in the issuer's * certificate. Note, however, that the result of * X509Certificate.getExtensionValue(<SubjectKeyIdentifier Object * Identifier>) on the issuer's certificate may NOT be used directly as the * input to 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 * SubjectKeyIdentifier extension, it will be necessary to extract the value * of the embedded KeyIdentifier OCTET STRING, then DER encode this OCTET * STRING inside a SEQUENCE. For more details on SubjectKeyIdentifier, see * {@link #setSubjectKeyIdentifier(byte[]) setSubjectKeyIdentifier(byte[] subjectKeyID }).<br /> * <br /> * 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 X509Certificate. If * <code>null</code>, no certificateValid check will be done.<br /> * <br /> * Note that the Date supplied here is cloned to protect against subsequent * modifications. * * @param certValid * the Date to check (or <code>null</code>) * * @see #getCertificateValid() */ public void setCertificateValid(Date certValid) { if (certValid == null) { this.certValid = null; } else { this.certValid = new Date(certValid.getTime()); } } /** * Sets the privateKeyValid criterion. The specified date must fall within * the private key validity period for the X509Certificate. If * <code>null</code>, no privateKeyValid check will be done.<br /> * <br /> * Note that the Date supplied here is cloned to protect against subsequent * modifications. * * @param privateKeyValid * the Date to check (or <code>null</code>) * * @see #getPrivateKeyValid() */ public void setPrivateKeyValid(Date privateKeyValid) { if (privateKeyValid == null) { this.privateKeyValid = null; } else { this.privateKeyValid = new Date(privateKeyValid.getTime()); } } /** * Sets the subjectPublicKeyAlgID criterion. The X509Certificate 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. * * @exception 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) { CertUtil.parseOID(oid); subjectKeyAlgID = new ASN1ObjectIdentifier(oid); } else { subjectKeyAlgID = null; } } /** * Sets the subjectPublicKey criterion. The X509Certificate must contain the * specified subject public key. If null, no subjectPublicKey check will be * done. * * @param key * the subject public key to check for (or null) * * @see #getSubjectPublicKey() */ public void setSubjectPublicKey(PublicKey key) { if (key == null) { subjectPublicKey = null; subjectPublicKeyByte = null; } else { subjectPublicKey = key; subjectPublicKeyByte = 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.<br /> * <br /> * Because this method allows the public key to be specified as a byte * array, it may be used for unknown key types.<br /> * <br /> * If key 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.<br /> * <br /> * * <pre> * 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> * * <br /> * <br /> * 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>) * * @exception 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; subjectPublicKeyByte = null; } else { subjectPublicKey = null; subjectPublicKeyByte = (byte[])key.clone(); // TODO // try to generyte PublicKey Object from subjectPublicKeyByte } } /** * Sets the keyUsage criterion. The X509Certificate must allow the specified * keyUsage values. If null, no keyUsage check will be done. Note that an * X509Certificate that has no keyUsage extension implicitly allows all * keyUsage values.<br /> * <br /> * 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 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.<br /> * <br /> * Note that the Set is cloned to protect against subsequent modifications.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId} * * @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. * * @exception 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 keyPurposeSet) throws IOException { if (keyPurposeSet == null || keyPurposeSet.isEmpty()) { this.keyPurposeSet = keyPurposeSet; } else { this.keyPurposeSet = new HashSet(); Iterator iter = keyPurposeSet.iterator(); Object obj; KeyPurposeId purposeID; while (iter.hasNext()) { obj = iter.next(); if (obj instanceof String) { purposeID = (KeyPurposeId)keyPurposeIdMap.get((String)obj); if (purposeID == null) { throw new IOException("unknown purposeID " + (String)obj); } this.keyPurposeSet.add(purposeID); } } } } /** * 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 X509Certificate * must contain at least one of the specified subject alternative names.<br /> * <br /> * 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) { 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}).<br /> * <br /> * 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.<br /> * <br /> * 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.<br /> * <br /> * 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, String) addSubjectAlternativeName(int type, String name)} * and * {@link #addSubjectAlternativeName(int, byte[]) addSubjectAlternativeName(int type, byte [] name}).<br /> * <br /> * 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} method.<br /> * <br /> * Note that a deep copy is performed on the Collection to protect against * subsequent modifications. * * @param names - * a Collection of names (or null) * * @exception IOException * if a parsing error occurs * * @see #getSubjectAlternativeNames() */ public void setSubjectAlternativeNames(Collection names) throws IOException { try { if (names == null || names.isEmpty()) { subjectAltNames = null; subjectAltNamesByte = null; } else { subjectAltNames = new HashSet(); subjectAltNamesByte = new HashSet(); Iterator iter = names.iterator(); List item; int type; Object data; while (iter.hasNext()) { item = (List)iter.next(); type = ((Integer)item.get(0)).intValue(); data = item.get(1); if (data instanceof String) { addSubjectAlternativeName(type, (String)data); } else if (data instanceof byte[]) { addSubjectAlternativeName(type, (byte[])data); } else { throw new IOException( "parsing error: unknown data type"); } } } } catch (Exception ex) { throw new IOException("parsing exception:\n" + ex.toString()); } } /** * 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}).<br /> * <br /> * 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.<br /> * <br /> * 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 2459). 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, byte[]) addSubjectAlternativeName(int type, byte [] name)} * method. * * @param type * the name type (0-8, as specified in RFC 2459, section 4.2.1.7) * @param name - * the name in string form (not null) * * @exception IOException * if a parsing error occurs */ public void addSubjectAlternativeName(int type, String name) throws IOException { // TODO full implementation of CertUtil.parseGeneralName byte[] encoded = CertUtil.parseGeneralName(type, name); List tmpList = new ArrayList(); tmpList.add(Integers.valueOf(type)); tmpList.add(name); subjectAltNames.add(tmpList); tmpList.set(1, encoded); subjectAltNamesByte.add(tmpList); } /** * 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}).<br /> * <br /> * 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.<br /> * <br /> * 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 2459 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.<br /> * <br /> * * <pre> * 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> * * <br /> * <br /> * Note that the byte array supplied here is cloned to protect against * subsequent modifications.<br /> * <br /> * <b>TODO: check encoded format</b> * * @param type * the name type (0-8, as listed above) * @param name * a byte array containing the name in ASN.1 DER encoded form * * @exception IOException * if a parsing error occurs */ public void addSubjectAlternativeName(int type, byte[] name) throws IOException { // TODO check encoded format List tmpList = new ArrayList(); tmpList.add(Integers.valueOf(type)); tmpList.add(name.clone()); subjectAltNames.add(tmpList); subjectAltNamesByte.add(tmpList); } /** * Sets the name constraints criterion. The <code>X509Certificate</code> * must have subject and subject alternative names that meet the specified * name constraints.<br /> * <br /> * 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 2459 and * X.509. The ASN.1 definition of this structure appears below.<br /> * <br /> * * <pre> * 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> * * <br /> * <br /> * Note that the byte array supplied here is cloned to protect against * subsequent modifications.<br /> * <br /> * <b>TODO: implement this</b> * * @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 * * @exception IOException * if a parsing error occurs * @exception UnsupportedOperationException * because this method is not supported * @see #getNameConstraints() */ public void setNameConstraints(byte[] bytes) throws IOException { throw new UnsupportedOperationException(); } /** * 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.<br /> * <br /> * 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 * * @exception IllegalArgumentException * if the value is less than -2 * * @see #getBasicConstraints() */ public void setBasicConstraints(int minMaxPathLen) { if (minMaxPathLen < -2) { throw new IllegalArgumentException("minMaxPathLen must be >= -2"); } this.minMaxPathLen = minMaxPathLen; } /** * Sets the policy constraint. The X509Certificate must include at least one * of the specified policies in its certificate policies extension. If * certPolicySet is empty, then the X509Certificate must include at least * some specified policy in its certificate policies extension. If * certPolicySet is null, no policy check will be performed.<br /> * <br /> * Note that the Set is cloned to protect against subsequent modifications.<br /> * <br /> * <b>TODO: implement match check for this</b> * * @param certPolicySet * a Set of certificate policy OIDs in string format (or null). * Each OID is represented by a set of nonnegative integers * separated by periods. * * @exception 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 certPolicySet) throws IOException { if (certPolicySet == null) { policy = null; policyOID = null; } else { policyOID = new HashSet(); Iterator iter = certPolicySet.iterator(); Object item; while (iter.hasNext()) { item = iter.next(); if (item instanceof String) { CertUtil.parseOID((String)item); policyOID.add(new ASN1ObjectIdentifier((String)item)); } else { throw new IOException( "certPolicySet contains null values or non String objects"); } } policy = new HashSet(certPolicySet); } } /** * Sets the pathToNames criterion. The <code>X509Certificate</code> must * not include name constraints that would prohibit building a path to the * specified names.<br /> * <br /> * 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.<br /> * <br /> * 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.<br /> * <br /> * The names 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 Integer (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.<br /> * <br /> * Each name in the Collection may be specified either as a String or as an * ASN.1 encoded byte array. For more details about the formats used, see * {@link #addPathToName(int, String) addPathToName(int type, String name)} * and * {@link #addPathToName(int, byte[]) addPathToName(int type, byte [] name)}.<br /> * <br /> * Note that the names parameter can contain duplicate names (same name and * name type), but they may be removed from the Collection of names returned * by the {@link #getPathToNames} method.<br /> * <br /> * Note that a deep copy is performed on the Collection to protect against * subsequent modifications.<br /> * <br /> * <b>TODO: implement this match check for this</b> * * @param names * a Collection with one entry per name (or <code>null</code>) * * @exception IOException * if a parsing error occurs * @exception UnsupportedOperationException * because this method is not supported * * @see #getPathToNames() */ public void setPathToNames(Collection names) throws IOException { try { if (names == null || names.isEmpty()) { pathToNames = null; pathToNamesByte = null; } else { pathToNames = new HashSet(); pathToNamesByte = new HashSet(); Iterator iter = names.iterator(); List item; int type; Object data; while (iter.hasNext()) { item = (List)iter.next(); type = ((Integer)item.get(0)).intValue(); data = item.get(1); if (data instanceof String) { addPathToName(type, (String)data); } else if (data instanceof byte[]) { addPathToName(type, (byte[])data); } else { throw new IOException( "parsing error: unknown data type"); } } } } catch (Exception ex) { throw new IOException("parsing exception:\n" + ex.toString()); } } /** * 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.<br /> * <br /> * 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.<br /> * <br /> * 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 2459). 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, byte[]) addPathToName(int type, byte [] name)} * method.<br /> * <br /> * <b>TODO: implement this match check for this</b> * * @param type * the name type (0-8, as specified in RFC 2459, section 4.2.1.7) * @param name * the name in string form * * @exceptrion IOException if a parsing error occurs */ public void addPathToName(int type, String name) throws IOException { // TODO full implementation of CertUtil.parseGeneralName byte[] encoded = CertUtil.parseGeneralName(type, name); List tmpList = new ArrayList(); tmpList.add(Integers.valueOf(type)); tmpList.add(name); pathToNames.add(tmpList); tmpList.set(1, encoded); pathToNamesByte.add(tmpList); throw new UnsupportedOperationException(); } /** * 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.<br /> * <br /> * 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.<br /> * <br /> * 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 2459 and X.509. The ASN.1 definition of this structure appears in * the documentation for * {@link #addSubjectAlternativeName(int,byte[]) addSubjectAlternativeName(int type, byte[] name)}.<br /> * <br /> * Note that the byte array supplied here is cloned to protect against * subsequent modifications.<br /> * <br /> * <b>TODO: implement this match check for this</b> * * @param type * the name type (0-8, as specified in RFC 2459, section 4.2.1.7) * @param name * a byte array containing the name in ASN.1 DER encoded form * * @exception IOException * if a parsing error occurs */ public void addPathToName(int type, byte[] name) throws IOException { // TODO check encoded format List tmpList = new ArrayList(); tmpList.add(Integers.valueOf(type)); tmpList.add(name.clone()); pathToNames.add(tmpList); pathToNamesByte.add(tmpList); } /** * Returns the certificateEquals criterion. The specified * <code>X509Certificate</code> must be equal to the * <code>X509Certificate</code> passed to the match method. If * <code>null</code>, this check is not applied. * * @retrun the <code>X509Certificate</code> to match (or <code>null</code>) * * @see #setCertificate(java.security.cert.X509Certificate) */ 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(java.math.BigInteger) */ public BigInteger getSerialNumber() { return serialNumber; } /** * Returns the issuer criterion as a String. 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.<br /> * <br /> * If the value returned is not <code>null</code>, it is a distinguished * name, in RFC 2253 format.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for formatiing * byte[] issuerDN to String. * * @return the required issuer distinguished name in RFC 2253 format (or * <code>null</code>) */ public String getIssuerAsString() { if (issuerDN instanceof String) { return new String((String)issuerDN); } else if (issuerDNX509 != null) { return issuerDNX509.toString(); } return null; } /** * 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.<br /> * <br /> * 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[]) setIssuer(byte [] issuerDN)}.<br /> * <br /> * Note that the byte array returned is cloned to protect against subsequent * modifications.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream}, * {@link org.bouncycastle.asn1.x509.X509Name X509Name} to gnerate byte[] * output for String issuerDN. * * @return a byte array containing the required issuer distinguished name in * ASN.1 DER format (or <code>null</code>) * * @exception IOException * if an encoding error occurs */ public byte[] getIssuerAsBytes() throws IOException { if (issuerDN instanceof byte[]) { return (byte[])((byte[])issuerDN).clone(); } else if (issuerDNX509 != null) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); DEROutputStream derOutStream = new DEROutputStream(outStream); derOutStream.writeObject(issuerDNX509.toASN1Primitive()); derOutStream.close(); return outStream.toByteArray(); } return null; } /** * Returns the subject criterion as a String. 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.<br /> * <br /> * If the value returned is not <code>null</code>, it is a distinguished * name, in RFC 2253 format.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for formatiing * byte[] subjectDN to String. * * @return the required subject distinguished name in RFC 2253 format (or * <code>null</code>) */ public String getSubjectAsString() { if (subjectDN instanceof String) { return new String((String)subjectDN); } else if (subjectDNX509 != null) { return subjectDNX509.toString(); } return null; } /** * 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.<br /> * <br /> * 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)}.<br /> * <br /> * Note that the byte array returned is cloned to protect against subsequent * modifications.<br /> * <br /> * Uses {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream}, * {@link org.bouncycastle.asn1.x509.X509Name X509Name} to gnerate byte[] * output for String subjectDN. * * @return a byte array containing the required subject distinguished name * in ASN.1 DER format (or <code>null</code>) * * @exception IOException * if an encoding error occurs */ public byte[] getSubjectAsBytes() throws IOException { if (subjectDN instanceof byte[]) { return (byte[])((byte[])subjectDN).clone(); } else if (subjectDNX509 != null) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); DEROutputStream derOutStream = new DEROutputStream(outStream); derOutStream.writeObject(subjectDNX509.toASN1Primitive()); derOutStream.close(); return outStream.toByteArray(); } return null; } /** * 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.<br /> * <br /> * 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 (byte[])subjectKeyID.clone(); } return null; } /** * 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.<br /> * <br /> * 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 (byte[])authorityKeyID.clone(); } return null; } /** * 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.<br /> * <br /> * 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 (certValid != null) { return new Date(certValid.getTime()); } return null; } /** * 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.<br /> * <br /> * 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 new Date(privateKeyValid.getTime()); } return null; } /** * 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 (subjectKeyAlgID != null) { return subjectKeyAlgID.toString(); } return null; } /** * 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.<br /> * <br /> * 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 (boolean[])keyUsage.clone(); } return null; } /** * 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 getExtendedKeyUsage() { if (keyPurposeSet == null || keyPurposeSet.isEmpty()) { return keyPurposeSet; } Set returnSet = new HashSet(); Iterator iter = keyPurposeSet.iterator(); while (iter.hasNext()) { returnSet.add(iter.next().toString()); } return Collections.unmodifiableSet(returnSet); } /** * 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.<br /> * <br /> * 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).<br /> * <br /> * 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)}.<br /> * <br /> * 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 getSubjectAlternativeNames() { if (subjectAltNames != null) { return null; } Set returnAltNames = new HashSet(); List returnList; Iterator iter = subjectAltNames.iterator(); List obj; while (iter.hasNext()) { obj = (List)iter.next(); returnList = new ArrayList(); returnList.add(obj.get(0)); if (obj.get(1) instanceof byte[]) { returnList.add(((byte[])obj.get(1)).clone()); } else { returnList.add(obj.get(1)); } returnAltNames.add(returnList); } return returnAltNames; } /** * Returns the name constraints criterion. The <code>X509Certificate</code> * must have subject and subject alternative names that meet the specified * name constraints.<br /> * <br /> * 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 2459 and X.509. * The ASN.1 notation for this structure is supplied in the documentation * for * {@link #setNameConstraints(byte [] bytes) setNameConstraints(byte [] bytes)}.<br /> * <br /> * Note that the byte array returned is cloned to protect against subsequent * modifications.<br /> * <br /> * <b>TODO: implement this</b> * * @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. * * @exception UnsupportedOperationException * because this method is not supported * * @see #setNameConstraints */ public byte[] getNameConstraints() { throw new UnsupportedOperationException(); } /** * 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 minMaxPathLen; } /** * 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 getPolicy() { if (policy == null) { return null; } return Collections.unmodifiableSet(policy); } /** * 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.<br /> * <br /> * 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).<br /> * <br /> * 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)}.<br /> * <br /> * 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 getPathToNames() { if (pathToNames == null) { return null; } Set returnPathToNames = new HashSet(); List returnList; Iterator iter = pathToNames.iterator(); List obj; while (iter.hasNext()) { obj = (List)iter.next(); returnList = new ArrayList(); returnList.add(obj.get(0)); if (obj.get(1) instanceof byte[]) { returnList.add(((byte[])obj.get(1)).clone()); } else { returnList.add(obj.get(1)); } returnPathToNames.add(returnList); } return returnPathToNames; } /** * Return a printable representation of the <code>CertSelector</code>.<br /> * <br /> * <b>TODO: implement output for currently unsupported options(name * constraints)</b><br /> * <br /> * Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream}, * {@link org.bouncycastle.asn1.ASN1Object ASN1Object}, * {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId} * * @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: ").append(x509Cert).append('\n'); } if (serialNumber != null) { sb.append(" Serial Number: ").append(serialNumber).append('\n'); } if (issuerDN != null) { sb.append(" Issuer: ").append(getIssuerAsString()).append('\n'); } if (subjectDN != null) { sb.append(" Subject: ").append(getSubjectAsString()).append('\n'); } try { if (subjectKeyID != null) { ByteArrayInputStream inStream = new ByteArrayInputStream( subjectKeyID); ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Object derObject = derInStream.readObject(); sb.append(" Subject Key Identifier: ") .append(ASN1Dump.dumpAsString(derObject)).append('\n'); } if (authorityKeyID != null) { ByteArrayInputStream inStream = new ByteArrayInputStream( authorityKeyID); ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Object derObject = derInStream.readObject(); sb.append(" Authority Key Identifier: ") .append(ASN1Dump.dumpAsString(derObject)).append('\n'); } } catch (IOException ex) { sb.append(ex.getMessage()).append('\n'); } if (certValid != null) { sb.append(" Certificate Valid: ").append(certValid).append('\n'); } if (privateKeyValid != null) { sb.append(" Private Key Valid: ").append(privateKeyValid) .append('\n'); } if (subjectKeyAlgID != null) { sb.append(" Subject Public Key AlgID: ") .append(subjectKeyAlgID).append('\n'); } if (subjectPublicKey != null) { sb.append(" Subject Public Key: ").append(subjectPublicKey) .append('\n'); } if (keyUsage != null) { sb.append(" Key Usage: ").append(keyUsage).append('\n'); } if (keyPurposeSet != null) { sb.append(" Extended Key Usage: ").append(keyPurposeSet) .append('\n'); } if (policy != null) { sb.append(" Policy: ").append(policy).append('\n'); } sb.append(" matchAllSubjectAltNames flag: ") .append(matchAllSubjectAltNames).append('\n'); if (subjectAltNamesByte != null) { sb.append(" SubjectAlternativNames: \n["); Iterator iter = subjectAltNamesByte.iterator(); List obj; try { while (iter.hasNext()) { obj = (List)iter.next(); ByteArrayInputStream inStream = new ByteArrayInputStream( (byte[])obj.get(1)); ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Object derObject = derInStream.readObject(); sb.append(" Type: ").append(obj.get(0)).append(" Data: ") .append(ASN1Dump.dumpAsString(derObject)).append('\n'); } } catch (IOException ex) { sb.append(ex.getMessage()).append('\n'); } sb.append("]\n"); } if (pathToNamesByte != null) { sb.append(" PathToNamesNames: \n["); Iterator iter = pathToNamesByte.iterator(); List obj; try { while (iter.hasNext()) { obj = (List)iter.next(); ByteArrayInputStream inStream = new ByteArrayInputStream( (byte[])obj.get(1)); ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Object derObject = derInStream.readObject(); sb.append(" Type: ").append(obj.get(0)).append(" Data: ") .append(ASN1Dump.dumpAsString(derObject)).append('\n'); } } catch (IOException ex) { sb.append(ex.getMessage()).append('\n'); } sb.append("]\n"); } sb.append(']'); return sb.toString(); } /** * Decides whether a <code>Certificate</code> should be selected.<br /> * <br /> * <b>TODO: implement missing tests (name constraints and path to names)</b><br /> * <br /> * Uses {@link org.bouncycastle.asn1.ASN1InputStream ASN1InputStream}, * {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence}, * {@link org.bouncycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier}, * {@link org.bouncycastle.asn1.ASN1Object ASN1Object}, * {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime}, * {@link org.bouncycastle.asn1.x509.X509Name X509Name}, * {@link org.bouncycastle.asn1.x509.X509Extensions X509Extensions}, * {@link org.bouncycastle.asn1.x509.ExtendedKeyUsage ExtendedKeyUsage}, * {@link org.bouncycastle.asn1.x509.KeyPurposeId KeyPurposeId}, * {@link org.bouncycastle.asn1.x509.SubjectPublicKeyInfo SubjectPublicKeyInfo}, * {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier AlgorithmIdentifier} * to access X509 extensions * * @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) { boolean[] booleanArray; List tempList; Iterator tempIter; if (!(cert instanceof X509Certificate)) { return false; } X509Certificate certX509 = (X509Certificate)cert; if (x509Cert != null && !x509Cert.equals(certX509)) { return false; } if (serialNumber != null && !serialNumber.equals(certX509.getSerialNumber())) { return false; } try { if (issuerDNX509 != null) { if (!issuerDNX509.equals(PrincipalUtil .getIssuerX509Principal(certX509), true)) { return false; } } if (subjectDNX509 != null) { if (!subjectDNX509.equals(PrincipalUtil .getSubjectX509Principal(certX509), true)) { return false; } } } catch (Exception ex) { return false; } if (subjectKeyID != null) { byte[] data = certX509 .getExtensionValue(X509Extensions.SubjectKeyIdentifier .getId()); if (data == null) { return false; } try { ByteArrayInputStream inStream = new ByteArrayInputStream(data); ASN1InputStream derInputStream = new ASN1InputStream(inStream); byte[] testData = ((ASN1OctetString)derInputStream.readObject()) .getOctets(); if (!Arrays.equals(subjectKeyID, testData)) { return false; } } catch (IOException ex) { return false; } } if (authorityKeyID != null) { byte[] data = certX509 .getExtensionValue(X509Extensions.AuthorityKeyIdentifier .getId()); if (data == null) { return false; } try { ByteArrayInputStream inStream = new ByteArrayInputStream(data); ASN1InputStream derInputStream = new ASN1InputStream(inStream); byte[] testData = ((ASN1OctetString)derInputStream.readObject()) .getOctets(); if (!Arrays.equals(authorityKeyID, testData)) { return false; } } catch (IOException ex) { return false; } } if (certValid != null) { if (certX509.getNotAfter() != null && certValid.after(certX509.getNotAfter())) { return false; } if (certX509.getNotBefore() != null && certValid.before(certX509.getNotBefore())) { return false; } } if (privateKeyValid != null) { try { byte[] data = certX509 .getExtensionValue(X509Extensions.PrivateKeyUsagePeriod .getId()); if (data != null) { ByteArrayInputStream inStream = new ByteArrayInputStream( data); ASN1InputStream derInputStream = new ASN1InputStream(inStream); inStream = new ByteArrayInputStream( ((ASN1OctetString)derInputStream.readObject()) .getOctets()); derInputStream = new ASN1InputStream(inStream); // TODO fix this, Sequence contains tagged objects ASN1Sequence derObject = (ASN1Sequence)derInputStream .readObject(); DERGeneralizedTime derDate = DERGeneralizedTime .getInstance(derObject.getObjectAt(0)); SimpleDateFormat dateF = new SimpleDateFormat( "yyyyMMddHHmmssZ"); if (privateKeyValid.before(dateF.parse(derDate.getTime()))) { return false; } derDate = DERGeneralizedTime.getInstance(derObject .getObjectAt(1)); if (privateKeyValid.after(dateF.parse(derDate.getTime()))) { return false; } } } catch (Exception ex) { return false; } } if (subjectKeyAlgID != null) { try { ByteArrayInputStream inStream = new ByteArrayInputStream( certX509.getPublicKey().getEncoded()); ASN1InputStream derInputStream = new ASN1InputStream(inStream); SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo( (ASN1Sequence)derInputStream.readObject()); AlgorithmIdentifier algInfo = publicKeyInfo.getAlgorithmId(); if (!algInfo.getObjectId().equals(subjectKeyAlgID)) { return false; } } catch (Exception ex) { return false; } } if (subjectPublicKeyByte != null) { if (!Arrays.equals(subjectPublicKeyByte, certX509.getPublicKey() .getEncoded())) { return false; } } if (subjectPublicKey != null) { if (!subjectPublicKey.equals(certX509.getPublicKey())) { return false; } } if (keyUsage != null) { booleanArray = certX509.getKeyUsage(); if (booleanArray != null) { for (int i = 0; i < keyUsage.length; i++) { if (keyUsage[i] && (booleanArray.length <= i || !booleanArray[i])) { return false; } } } } if (keyPurposeSet != null && !keyPurposeSet.isEmpty()) { try { byte[] data = certX509 .getExtensionValue(X509Extensions.ExtendedKeyUsage .getId()); if (data != null) { ByteArrayInputStream inStream = new ByteArrayInputStream( data); ASN1InputStream derInputStream = new ASN1InputStream(inStream); ExtendedKeyUsage extendedKeyUsage = ExtendedKeyUsage.getInstance( derInputStream.readObject()); tempIter = keyPurposeSet.iterator(); while (tempIter.hasNext()) { if (!extendedKeyUsage .hasKeyPurposeId((KeyPurposeId)tempIter.next())) { return false; } } } } catch (Exception ex) { return false; } } if (minMaxPathLen != -1) { if (minMaxPathLen == -2 && certX509.getBasicConstraints() != -1) { return false; } if (minMaxPathLen >= 0 && certX509.getBasicConstraints() < minMaxPathLen) { return false; } } if (policyOID != null) { try { byte[] data = certX509 .getExtensionValue(X509Extensions.CertificatePolicies .getId()); if (data == null) { return false; } if (!policyOID.isEmpty()) { ByteArrayInputStream inStream = new ByteArrayInputStream( data); ASN1InputStream derInputStream = new ASN1InputStream(inStream); inStream = new ByteArrayInputStream( ((ASN1OctetString)derInputStream.readObject()) .getOctets()); derInputStream = new ASN1InputStream(inStream); Enumeration policySequence = ((ASN1Sequence)derInputStream .readObject()).getObjects(); ASN1Sequence policyObject; boolean test = false; while (policySequence.hasMoreElements() && !test) { policyObject = (ASN1Sequence)policySequence .nextElement(); if (policyOID.contains(policyObject.getObjectAt(0))) { test = true; } } if (!test) { return false; } } } catch (Exception ex) { ex.printStackTrace(); return false; } } if (subjectAltNamesByte != null) { try { byte[] data = certX509 .getExtensionValue(X509Extensions.SubjectAlternativeName .getId()); if (data == null) { return false; } ByteArrayInputStream inStream = new ByteArrayInputStream(data); ASN1InputStream derInputStream = new ASN1InputStream(inStream); inStream = new ByteArrayInputStream( ((ASN1OctetString)derInputStream.readObject()) .getOctets()); derInputStream = new ASN1InputStream(inStream); Enumeration altNamesSequence = ((ASN1Sequence)derInputStream .readObject()).getObjects(); ASN1TaggedObject altNameObject; boolean test = false; Set testSet = new HashSet(subjectAltNamesByte); List testList; ASN1Object derData; ByteArrayOutputStream outStream; DEROutputStream derOutStream; while (altNamesSequence.hasMoreElements() && !test) { altNameObject = (ASN1TaggedObject)altNamesSequence .nextElement(); testList = new ArrayList(2); testList.add(Integers.valueOf(altNameObject.getTagNo())); derData = altNameObject.getObject(); outStream = new ByteArrayOutputStream(); derOutStream = new DEROutputStream(outStream); derOutStream.writeObject(derData); derOutStream.close(); testList.add(outStream.toByteArray()); if (testSet.remove(testList)) { test = true; } if (matchAllSubjectAltNames && !testSet.isEmpty()) { test = false; } } if (!test) { return false; } } catch (Exception ex) { ex.printStackTrace(); return false; } } return true; } /** * Returns a copy of this object. * * @return the copy */ public Object clone() { try { X509CertSelector copy = (X509CertSelector)super.clone(); if (issuerDN instanceof byte[]) { copy.issuerDN = ((byte[])issuerDN).clone(); } if (subjectDN instanceof byte[]) { copy.subjectDN = ((byte[])subjectDN).clone(); } if (subjectKeyID != null) { copy.subjectKeyID = (byte[])subjectKeyID.clone(); } if (authorityKeyID != null) { copy.authorityKeyID = (byte[])authorityKeyID.clone(); } if (subjectPublicKeyByte != null) { copy.subjectPublicKeyByte = (byte[])subjectPublicKeyByte .clone(); } if (keyUsage != null) { copy.keyUsage = (boolean[])keyUsage.clone(); } if (keyPurposeSet != null) { copy.keyPurposeSet = new HashSet(keyPurposeSet); } if (policy != null) { copy.policy = new HashSet(policy); copy.policyOID = new HashSet(); Iterator iter = policyOID.iterator(); while (iter.hasNext()) { copy.policyOID.add(new ASN1ObjectIdentifier( ((ASN1ObjectIdentifier)iter.next()).getId())); } } if (subjectAltNames != null) { copy.subjectAltNames = new HashSet(getSubjectAlternativeNames()); Iterator iter = subjectAltNamesByte.iterator(); List obj; List cloneObj; while (iter.hasNext()) { obj = (List)iter.next(); cloneObj = new ArrayList(); cloneObj.add(obj.get(0)); cloneObj.add(((byte[])obj.get(1)).clone()); copy.subjectAltNamesByte.add(cloneObj); } } if (pathToNames != null) { copy.pathToNames = new HashSet(getPathToNames()); Iterator iter = pathToNamesByte.iterator(); List obj; List cloneObj; while (iter.hasNext()) { obj = (List)iter.next(); cloneObj = new ArrayList(); cloneObj.add(obj.get(0)); cloneObj.add(((byte[])obj.get(1)).clone()); copy.pathToNamesByte.add(cloneObj); } } return copy; } catch (CloneNotSupportedException e) { /* Cannot happen */ throw new InternalError(e.toString()); } } }