Logo Search packages:      
Sourcecode: bouncycastle version File versions  Download package

X509CRLSelector.java

package java.security.cert;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERInputStream;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;

import org.bouncycastle.jce.PrincipalUtil;

/**
 * A <code>CRLSelector</code> that selects <code>X509CRLs</code> that
 * match all specified criteria. This class is particularly useful when
 * selecting CRLs from a <code>CertStore</code> to check revocation status
 * of a particular certificate.<br />
 * <br />
 * When first constructed, an <code>X509CRLSelector</code> has no criteria
 * enabled and each of the <code>get</code> methods return a default
 * value (<code>null</code>). Therefore, the {@link #match match} method 
 * would return <code>true</code> for any <code>X509CRL</code>. Typically, 
 * several criteria are enabled (by calling {@link #setIssuerNames setIssuerNames} 
 * or {@link #setDateAndTime setDateAndTime}, for instance) and then the
 * <code>X509CRLSelector</code> is passed to
 * {@link CertStore#getCRLs CertStore.getCRLs} or some similar
 * method.<br />
 * <br />
 * Please refer to RFC 2459 for definitions of the X.509 CRL fields and
 * 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 />
 * Uses {@link org.bouncycastle.asn1.DERInputStream DERInputStream},
 * {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence},
 * {@link org.bouncycastle.asn1.DERObjectIdentifier DERObjectIdentifier},
 * {@link org.bouncycastle.asn1.DEROutputStream DEROutputStream},
 * {@link org.bouncycastle.asn1.DERObject DERObject},
 * {@link org.bouncycastle.asn1.x509.X509Name X509Name} and
 * {@link org.bouncycastle.asn1.util.ASN1Dump#_dumpAsString _dumpAsString}
 *
 * @see CRLSelector
 * @see X509CRL
 **/
00060 public class X509CRLSelector implements CRLSelector
{
    private Set issuerNames = null;
    private Set issuerNamesX509 = null;
    private BigInteger minCRL = null;
    private BigInteger maxCRL = null;
    private Date dateAndTime = null;
    private X509Certificate certChecking = null;

    /**
     * Creates an <code>X509CRLSelector</code>. Initially, no criteria are set
     * so any <code>X509CRL</code> will match.
     */
00073     public X509CRLSelector() {}

    /**
     * Sets the issuerNames criterion. The issuer distinguished name in the
     * <code>X509CRL</code> must match at least one of the specified
     * distinguished names. If <code>null</code>, any issuer distinguished name
     * will do.<br />
     * <br />
     * This method allows the caller to specify, with a single method call,
     * the complete set of issuer names which <code>X509CRLs</code> may contain.
     * The specified value replaces the previous value for the issuerNames
     * criterion.<br />
     * <br />
     * The <code>names</code> parameter (if not <code>null</code>) is a
     * <code>Collection</code> of names. Each name is a <code>String</code>
     * or a byte array representing a distinguished name (in RFC 2253 or
     * ASN.1 DER encoded form, respectively). If <code>null</code> is supplied
     * as the value for this argument, no issuerNames check will be performed.<br />
     * <br />
     * Note that the <code>names</code> parameter can contain duplicate
     * distinguished names, but they may be removed from the 
     * <code>Collection</code> of names returned by the
     * {@link #getIssuerNames getIssuerNames} method.<br />
     * <br />
     * If a name is specified as a byte array, 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><br />
     * <br />
     * 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>)
     *
     * @exception IOException if a parsing error occurs
     *
     * @see #getIssuerNames
     */
00134     public void setIssuerNames(Collection names)
    throws IOException
    {
    if ( names == null || names.isEmpty() )
    {
        issuerNames = null;
        issuerNamesX509 = null;
    }
    else
    {
        Object item;
        Iterator iter = names.iterator();
        while ( iter.hasNext() )
        {
        item = iter.next();
        if ( item instanceof String )
            addIssuerName( (String)item );
        else if ( item instanceof byte[] )
            addIssuerName( (byte[])item );
        else
            throw new IOException( "name not byte[]or String: " + item.toString() );
        }
    }
    }

    /**
     * Adds a name to the issuerNames criterion. The issuer distinguished
     * name in the <code>X509CRL</code> must match at least one of the specified
     * distinguished names.<br />
     * <br />
     * This method allows the caller to add a name to the set of issuer names
     * which <code>X509CRLs</code> may contain. The specified name is added to
     * any previous value for the issuerNames criterion.
     * If the specified name is a duplicate, it may be ignored.<br />
     * <br />
     * Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for parsing the name
     *
     * @param name the name in RFC 2253 form
     *
     * @exception IOException if a parsing error occurs
     */
00175     public void addIssuerName(String name)
    throws IOException
    {
    if ( issuerNames == null )
    {
        issuerNames = new HashSet();
        issuerNamesX509 = new HashSet();
    }
    X509Name nameX509;
    try {
        nameX509 = new X509Name(name);
    } catch ( IllegalArgumentException ex ) {
        throw new IOException(ex.getMessage());
    }
    issuerNamesX509.add(nameX509);
    issuerNames.add(name);
    }

    /**
     * Adds a name to the issuerNames criterion. The issuer distinguished
     * name in the <code>X509CRL</code> must match at least one of the specified
     * distinguished names.<br />
     * <br />
     * This method allows the caller to add a name to the set of issuer names
     * which <code>X509CRLs</code> may contain. The specified name is added to
     * any previous value for the issuerNames criterion. If the specified name
     * is a duplicate, it may be ignored.
     * If a name is specified as a byte array, 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 />
     * The name is provided as a byte array. This byte array should contain
     * a single DER encoded distinguished name, as defined in X.501. The ASN.1
     * notation for this structure appears in the documentation for
     * {@link #setIssuerNames setIssuerNames(Collection names)}.<br />
     * <br />
     * Note that the byte array supplied here is cloned to protect against
     * subsequent modifications.<br />
     * <br />
     * Uses {@link org.bouncycastle.asn1.x509.X509Name X509Name} for parsing the name,
     * {@link org.bouncycastle.asn1.DERInputStream DERInputStream},
     * {@link org.bouncycastle.asn1.DERObject DERObject} and
     * {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence}
     *
     * @param name a byte array containing the name in ASN.1 DER encoded form
     *
     * @exception IOException if a parsing error occurs
     */
00223     public void addIssuerName(byte[] name)
    throws IOException
    {
    if ( issuerNames == null ) {
        issuerNames = new HashSet();
        issuerNamesX509 = new HashSet();
    }

    ByteArrayInputStream inStream = new ByteArrayInputStream(name);
    DERInputStream derInStream = new DERInputStream(inStream);
    DERObject obj = derInStream.readObject();
    if ( obj instanceof ASN1Sequence )
    {
        issuerNamesX509.add(new X509Name((ASN1Sequence)obj));
    }
    else
    {
        throw new IOException("parsing error");
    }
    issuerNames.add(name.clone());
    }

    /**
     * Sets the minCRLNumber criterion. The <code>X509CRL</code> must have a
     * CRL number extension whose value is greater than or equal to the
     * specified value. If <code>null</code>, no minCRLNumber check will be 
     * done.
     *
     * @param minCRL the minimum CRL number accepted (or <code>null</code>)
     */
00253     public void setMinCRLNumber(BigInteger minCRL)
    {
    this.minCRL = minCRL;
    }

    /**
     * Sets the maxCRLNumber criterion. The <code>X509CRL</code> must have a
     * CRL number extension whose value is less than or equal to the
     * specified value. If <code>null</code>, no maxCRLNumber check will be 
     * done.
     *
     * @param maxCRL the maximum CRL number accepted (or <code>null</code>)
     */
00266     public void setMaxCRLNumber(BigInteger maxCRL)
    {
    this.maxCRL = maxCRL;
    }

    /**
     * Sets the dateAndTime criterion. The specified date must be
     * equal to or later than the value of the thisUpdate component
     * of the <code>X509CRL</code> and earlier than the value of the
     * nextUpdate component. There is no match if the <code>X509CRL</code>
     * does not contain a nextUpdate component.
     * If <code>null</code>, no dateAndTime check will be done.<br />
     * <br />
     * Note that the <code>Date</code> supplied here is cloned to protect 
     * against subsequent modifications.
     *
     * @param dateAndTime the <code>Date</code> to match against
     *                    (or <code>null</code>)
     *
     * @see #getDateAndTime
     */
00287     public void setDateAndTime(Date dateAndTime)
    {
    if ( dateAndTime == null )
        this.dateAndTime = null;
    else
        this.dateAndTime = new Date( dateAndTime.getTime() );
    }

    /**
     * Sets the certificate being checked. This is not a criterion. Rather,
     * it is optional information that may help a <code>CertStore</code>
     * find CRLs that would be relevant when checking revocation for the
     * specified certificate. If <code>null</code> is specified, then no
     * such optional information is provided.
     *
     * @param cert the <code>X509Certificate</code> being checked
     *             (or <code>null</code>)
     *
     * @see #getCertificateChecking
     */
00307     public void setCertificateChecking(X509Certificate cert)
    {
    certChecking = cert;
    }

    /**
     * Returns a copy of the issuerNames criterion. The issuer distinguished
     * name in the <code>X509CRL</code> must match at least one of the specified
     * distinguished names. If the value returned is <code>null</code>, any
     * issuer distinguished name will do.<br />
     * <br />
     * If the value returned is not <code>null</code>, it is a
     * <code>Collection</code> of names. Each name is a <code>String</code>
     * or a byte array representing a distinguished name (in RFC 2253 or
     * ASN.1 DER encoded form, respectively).  Note that the 
     * <code>Collection</code> returned may contain duplicate names.<br />
     * <br />
     * If a name is specified as a byte array, it should contain a single DER
     * encoded distinguished name, as defined in X.501. The ASN.1 notation for
     * this structure is given in the documentation for
     * {@link #setIssuerNames setIssuerNames(Collection names)}.<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 #setIssuerNames
     */
00335     public Collection getIssuerNames()
    {
    if ( issuerNames == null )
        return null;
    
    Collection set = new HashSet();
    Iterator iter = issuerNames.iterator();
    Object item;
    while ( iter.hasNext() )
    {
        item = iter.next();
        if ( item instanceof String )
        set.add(new String((String)item));
        else if ( item instanceof byte[] )
        set.add(((byte[])item).clone());
    }
    return set;
    }

    /**
     * Returns the minCRLNumber criterion. The <code>X509CRL</code> must have a
     * CRL number extension whose value is greater than or equal to the
     * specified value. If <code>null</code>, no minCRLNumber check will be done.
     *
     * @return the minimum CRL number accepted (or <code>null</code>)
     */
00361     public BigInteger getMinCRL()
    {
    return minCRL;
    }

    /**
     * Returns the maxCRLNumber criterion. The <code>X509CRL</code> must have a
     * CRL number extension whose value is less than or equal to the
     * specified value. If <code>null</code>, no maxCRLNumber check will be 
     * done.
     *
     * @return the maximum CRL number accepted (or <code>null</code>)
     */
00374     public BigInteger getMaxCRL()
    {
    return maxCRL;
    }

    /**
     * Returns the dateAndTime criterion. The specified date must be
     * equal to or later than the value of the thisUpdate component
     * of the <code>X509CRL</code> and earlier than the value of the
     * nextUpdate component. There is no match if the
     * <code>X509CRL</code> does not contain a nextUpdate component.
     * If <code>null</code>, no dateAndTime 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 match against (or <code>null</code>)
     *
     * @see #setDateAndTime
     */
00394     public Date getDateAndTime()
    {
    if ( dateAndTime == null )
        return null;

    return new Date(dateAndTime.getTime());
    }

    /**
     * Returns the certificate being checked. This is not a criterion. Rather,
     * it is optional information that may help a <code>CertStore</code>
     * find CRLs that would be relevant when checking revocation for the
     * specified certificate. If the value returned is <code>null</code>, then 
     * no such optional information is provided.
     *
     * @return the certificate being checked (or <code>null</code>)
     *
     * @see #setCertificateChecking
     */
00413     public X509Certificate getCertificateChecking()
    {
    return certChecking;
    }

    /**
     * Returns a printable representation of the <code>X509CRLSelector</code>.<br />
     * <br />
     * Uses {@link org.bouncycastle.asn1.x509.X509Name#toString X509Name.toString} to format the output
     *
     * @return a <code>String</code> describing the contents of the
     *         <code>X509CRLSelector</code>.
     */
00426     public String toString()
    {
    StringBuffer s = new StringBuffer();
    s.append( "X509CRLSelector: [\n" );
    if ( issuerNamesX509 != null ) {
            s.append("  IssuerNames:\n");
        Iterator iter = issuerNamesX509.iterator();
        while ( iter.hasNext() ) {
        s.append( "    ").append(iter.next()).append('\n' );
        }
    }
        if (minCRL != null)
            s.append("  minCRLNumber: ").append(minCRL).append('\n');
        if (maxCRL != null)
            s.append("  maxCRLNumber: ").append(maxCRL).append('\n');
        if (dateAndTime != null)
            s.append("  dateAndTime: ").append(dateAndTime).append('\n');
        if (certChecking != null)
            s.append("  Certificate being checked: ").append(certChecking).append('\n');
        s.append(']');
    return s.toString();
    }

    /**
     * Decides whether a <code>CRL</code> should be selected.<br />
     * <br />
     * Uses {@link org.bouncycastle.asn1.x509.X509Name#toString X509Name.toString}
     * to parse and to compare the crl parameter issuer and
     * {@link org.bouncycastle.asn1.x509.X509Extensions#CRLNumber CRLNumber} to access
     * the CRL number extension.
     *
     * @param crl the <code>CRL</code> to be checked
     *
     * @return <code>true</code> if the <code>CRL</code> should be selected,
     *         <code>false</code> otherwise
     */
00462     public boolean match(CRL crl)
    {
    if ( ! ( crl instanceof X509CRL ) )
        return false;
    
    X509CRL crlX509 = (X509CRL)crl;
    boolean test;

    if ( issuerNamesX509 != null )
    {
        Iterator iter = issuerNamesX509.iterator();
        test = false;
        X509Name crlIssuer = null;
        try {
            crlIssuer = PrincipalUtil.getIssuerX509Principal(crlX509);
        } catch ( Exception ex ) {
        return false;
        }
        
        while ( iter.hasNext() )
        {
            if ( crlIssuer.equals(iter.next(), true) )
            {
                test = true;
                break;
            }
        }
        if ( ! test )
        {
            return false;
        }
    }

    byte[] data = crlX509.getExtensionValue( X509Extensions.CRLNumber.getId() );
    if ( data != null )
    {
        try {
        ByteArrayInputStream inStream = new ByteArrayInputStream(data);
        DERInputStream derInputStream = new DERInputStream(inStream);
        inStream = new ByteArrayInputStream(((ASN1OctetString)derInputStream.readObject()).getOctets());
        derInputStream = new DERInputStream(inStream);
        BigInteger crlNumber = ((DERInteger)derInputStream.readObject()).getPositiveValue();
        if ( minCRL != null && minCRL.compareTo( crlNumber ) > 0 )
            return false;
        if ( maxCRL != null && maxCRL.compareTo( crlNumber ) < 0 )
            return false;
        } catch ( IOException ex ) {
        return false;
        }
    } else if ( minCRL != null || maxCRL != null )
    {
        return false;
    }

    if ( dateAndTime != null )
    {
        Date check = crlX509.getThisUpdate();
        if ( check == null )
        {
        return false;
        }
        else if ( dateAndTime.before( check ) )
        {
        return false;
        }

        check = crlX509.getNextUpdate();
        if ( check == null )
        {
        return false;
        }
        else if ( ! dateAndTime.before( check ) )
        {
        return false;
        }
    }

    return true;
    }

    /**
     * Returns a copy of this object.
     *
     * @return the copy
     */
00547     public Object clone()
    {
    try {
        X509CRLSelector copy = (X509CRLSelector)super.clone();
        if ( issuerNames != null )
        {
        copy.issuerNames = new HashSet();
        Iterator iter = issuerNames.iterator();
        Object obj;
        while ( iter.hasNext() )
        {
            obj = iter.next();
            if ( obj instanceof byte[] )
            copy.issuerNames.add( ((byte[])obj).clone() );
             else
             copy.issuerNames.add( obj );
        }
        copy.issuerNamesX509 = new HashSet( issuerNamesX509 );
        }
        return copy;
    } catch (CloneNotSupportedException e) {
        /* Cannot happen */
        throw new InternalError(e.toString());
    }
    }

    /**
     * Decides whether a <code>CRL</code> should be selected.
     *
     * @param crl the <code>CRL</code> to be checked
     *
     * @return <code>true</code> if the <code>CRL</code> should be selected,
     *         <code>false</code> otherwise
     */
00581     public boolean equals( Object obj )
    {
    if ( ! ( obj instanceof X509CRLSelector ) )
        return false;

    X509CRLSelector equalsCRL = (X509CRLSelector)obj;

    if ( ! equals(dateAndTime, equalsCRL.dateAndTime) )
         return false;

    if ( ! equals(minCRL, equalsCRL.minCRL) )
        return false;

    if ( ! equals(maxCRL, equalsCRL.maxCRL) )
        return false;

    if ( ! equals(issuerNamesX509, equalsCRL.issuerNamesX509) )
        return false;

    if ( ! equals( certChecking, equalsCRL.certChecking) )
        return false;

    return true;
    }

    /**
     * Return <code>true</code> if two Objects are unequal.
     * This means that one is <code>null</code> and the other is
     * not or <code>obj1.equals(obj2)</code> returns
     * <code>false</code>.
     **/
00612     private boolean equals( Object obj1, Object obj2 )
    {
    if ( obj1 == null ) {
        if ( obj2 != null )
        return true;
    }
    else if ( ! obj1.equals( obj2 ) )
        return true;
    return false;
    }    
}

Generated by  Doxygen 1.6.0   Back to index