package java.security.cert;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.cert.CRL;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.ASN1Object;
import org.spongycastle.asn1.ASN1OctetString;
import org.spongycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.ASN1Integer;
import org.spongycastle.asn1.x509.X509Extensions;
import org.spongycastle.asn1.x509.X509Name;
import org.spongycastle.jce.PrincipalUtil;
/**
* A CRLSelector
that selects X509CRLs
that match
* all specified criteria. This class is particularly useful when selecting CRLs
* from a CertStore
to check revocation status of a particular
* certificate.
*
* When first constructed, an X509CRLSelector
has no criteria
* enabled and each of the get
methods return a default value (null
).
* Therefore, the {@link #match match} method would return true
* for any X509CRL
. Typically, several criteria are enabled (by
* calling {@link #setIssuerNames setIssuerNames} or
* {@link #setDateAndTime setDateAndTime}, for instance) and then the
* X509CRLSelector
is passed to
* {@link CertStore#getCRLs CertStore.getCRLs} or some similar method.
*
* Please refer to RFC 2459 for definitions of the X.509 CRL fields and
* extensions mentioned below.
*
* Concurrent Access
*
* 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.
*
* Uses {@link org.spongycastle.asn1.ASN1InputStream ASN1InputStream},
* {@link org.spongycastle.asn1.ASN1Sequence ASN1Sequence},
* {@link org.spongycastle.asn1.ASN1ObjectIdentifier ASN1ObjectIdentifier},
* {@link org.spongycastle.asn1.DEROutputStream DEROutputStream},
* {@link org.spongycastle.asn1.ASN1Object ASN1Object},
* {@link org.spongycastle.asn1.x509.X509Name X509Name}
*
* @see CRLSelector
* @see X509CRL
*/
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 X509CRLSelector
. Initially, no criteria are
* set so any X509CRL
will match.
*/
public X509CRLSelector()
{
}
/**
* Sets the issuerNames criterion. The issuer distinguished name in the
* X509CRL
must match at least one of the specified
* distinguished names. If null
, any issuer distinguished
* name will do.
*
* This method allows the caller to specify, with a single method call, the
* complete set of issuer names which X509CRLs
may contain.
* The specified value replaces the previous value for the issuerNames
* criterion.
*
* The names
parameter (if not null
) is a
* Collection
of names. Each name is a String
* or a byte array representing a distinguished name (in RFC 2253 or ASN.1
* DER encoded form, respectively). If null
is supplied as
* the value for this argument, no issuerNames check will be performed.
*
* Note that the names
parameter can contain duplicate
* distinguished names, but they may be removed from the
* Collection
of names returned by the
* {@link #getIssuerNames getIssuerNames} method.
*
* 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.
*
*
* 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)) }
*
*
* Collection
to
* protect against subsequent modifications.
*
* @param names
* a Collection
of names (or null
)
*
* @exception IOException
* if a parsing error occurs
*
* @see #getIssuerNames
*/
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 X509CRL
must match at least one of the specified
* distinguished names.X509CRLs
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.X509CRL
must match at least one of the specified
* distinguished names.X509CRLs
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.X509CRL
must have a
* CRL number extension whose value is greater than or equal to the
* specified value. If null
, no minCRLNumber check will be
* done.
*
* @param minCRL
* the minimum CRL number accepted (or null
)
*/
public void setMinCRLNumber(BigInteger minCRL)
{
this.minCRL = minCRL;
}
/**
* Sets the maxCRLNumber criterion. The X509CRL
must have a
* CRL number extension whose value is less than or equal to the specified
* value. If null
, no maxCRLNumber check will be done.
*
* @param maxCRL
* the maximum CRL number accepted (or null
)
*/
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
* X509CRL
and earlier than the value of the nextUpdate
* component. There is no match if the X509CRL
does not
* contain a nextUpdate component. If null
, no dateAndTime
* check will be done.Date
supplied here is cloned to protect
* against subsequent modifications.
*
* @param dateAndTime
* the Date
to match against (or null
)
*
* @see #getDateAndTime
*/
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 CertStore
find
* CRLs that would be relevant when checking revocation for the specified
* certificate. If null
is specified, then no such optional
* information is provided.
*
* @param cert
* the X509Certificate
being checked (or
* null
)
*
* @see #getCertificateChecking
*/
public void setCertificateChecking(X509Certificate cert)
{
certChecking = cert;
}
/**
* Returns a copy of the issuerNames criterion. The issuer distinguished
* name in the X509CRL
must match at least one of the
* specified distinguished names. If the value returned is null
,
* any issuer distinguished name will do.null
, it is a
* Collection
of names. Each name is a String
* or a byte array representing a distinguished name (in RFC 2253 or ASN.1
* DER encoded form, respectively). Note that the Collection
* returned may contain duplicate names.Collection
to
* protect against subsequent modifications.
*
* @return a Collection
of names (or null
)
* @see #setIssuerNames
*/
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 X509CRL
must have
* a CRL number extension whose value is greater than or equal to the
* specified value. If null
, no minCRLNumber check will be
* done.
*
* @return the minimum CRL number accepted (or null
)
*/
public BigInteger getMinCRL()
{
return minCRL;
}
/**
* Returns the maxCRLNumber criterion. The X509CRL
must have
* a CRL number extension whose value is less than or equal to the specified
* value. If null
, no maxCRLNumber check will be done.
*
* @return the maximum CRL number accepted (or null
)
*/
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
* X509CRL
and earlier than the value of the nextUpdate
* component. There is no match if the X509CRL
does not
* contain a nextUpdate component. If null
, no dateAndTime
* check will be done.Date
returned is cloned to protect against
* subsequent modifications.
*
* @return the Date
to match against (or null
)
*
* @see #setDateAndTime
*/
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 CertStore
find
* CRLs that would be relevant when checking revocation for the specified
* certificate. If the value returned is null
, then no such
* optional information is provided.
*
* @return the certificate being checked (or null
)
*
* @see #setCertificateChecking
*/
public X509Certificate getCertificateChecking()
{
return certChecking;
}
/**
* Returns a printable representation of the X509CRLSelector
.String
describing the contents of the
* X509CRLSelector
.
*/
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 CRL
should be selected.CRL
to be checked
*
* @return true
if the CRL
should be selected,
* false
otherwise
*/
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);
ASN1InputStream derInputStream = new ASN1InputStream(inStream);
inStream = new ByteArrayInputStream(
((ASN1OctetString)derInputStream.readObject())
.getOctets());
derInputStream = new ASN1InputStream(inStream);
BigInteger crlNumber = ((ASN1Integer)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
*/
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 CRL
should be selected.
*
* @param crl
* the CRL
to be checked
*
* @return true
if the CRL
should be selected,
* false
otherwise
*/
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 true
if two Objects are unequal.
* This means that one is null
and the other is
* not or obj1.equals(obj2)
returns
* false
.
**/
private boolean equals(Object obj1, Object obj2)
{
if (obj1 == null)
{
if (obj2 != null)
{
return true;
}
}
else if (!obj1.equals(obj2))
{
return true;
}
return false;
}
}