/* * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.security.cert; import java.io.IOException; import java.math.BigInteger; import java.util.*; import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; import sun.security.util.DerInputStream; import sun.security.x509.CRLNumberExtension; import sun.security.x509.X500Name; /** * 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. * <p> * 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 #setIssuers setIssuers} * or {@link #setDateAndTime setDateAndTime}, for instance) and then the * <code>X509CRLSelector</code> is passed to * {@link CertStore#getCRLs CertStore.getCRLs} or some similar * method. * <p> * Please refer to <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: * Internet X.509 Public Key Infrastructure Certificate and CRL Profile</a> * for definitions of the X.509 CRL fields and extensions mentioned below. * <p> * <b>Concurrent Access</b> * <p> * Unless otherwise specified, the methods defined in this class are not * thread-safe. Multiple threads that need to access a single * object concurrently should synchronize amongst themselves and * provide the necessary locking. Multiple threads each manipulating * separate objects need not synchronize. * * @see CRLSelector * @see X509CRL * * @since 1.4 * @author Steve Hanna */ public class X509CRLSelector implements CRLSelector { private static final Debug debug = Debug.getInstance("certpath"); private HashSet<Object> issuerNames; private HashSet<X500Principal> issuerX500Principals; private BigInteger minCRL; private BigInteger maxCRL; private Date dateAndTime; private X509Certificate certChecking; /** * Creates an <code>X509CRLSelector</code>. Initially, no criteria are set * so any <code>X509CRL</code> will match. */ 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. * <p> * 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. * <p> * The <code>names</code> parameter (if not <code>null</code>) is a * <code>Collection</code> of <code>X500Principal</code>s. * <p> * 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 #getIssuers getIssuers} method. * <p> * Note that a copy is performed on the <code>Collection</code> to * protect against subsequent modifications. * * @param issuers a <code>Collection</code> of X500Principals * (or <code>null</code>) * @see #getIssuers * @since 1.5 */ public void setIssuers(Collection<X500Principal> issuers) { if ((issuers == null) || issuers.isEmpty()) { issuerNames = null; issuerX500Principals = null; } else { // clone issuerX500Principals = new HashSet<X500Principal>(issuers); issuerNames = new HashSet<Object>(); for (X500Principal p : issuerX500Principals) { issuerNames.add(p.getEncoded()); } } } /** * <strong>Note:</strong> use {@linkplain #setIssuers(Collection)} instead * or only specify the byte array form of distinguished names when using * this method. See {@link #addIssuerName(String)} for more information. * <p> * 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. * <p> * 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. * <p> * 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 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a> 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. * <p> * 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. * <p> * 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> * <p> * Note that a deep copy is performed on the <code>Collection</code> to * protect against subsequent modifications. * * @param names a <code>Collection</code> of names (or <code>null</code>) * @throws IOException if a parsing error occurs * @see #getIssuerNames */ public void setIssuerNames(Collection<?> names) throws IOException { if (names == null || names.size() == 0) { issuerNames = null; issuerX500Principals = null; } else { HashSet<Object> tempNames = cloneAndCheckIssuerNames(names); // Ensure that we either set both of these or neither issuerX500Principals = parseIssuerNames(tempNames); issuerNames = tempNames; } } /** * 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. * <p> * 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. * * @param issuer the issuer as X500Principal * @since 1.5 */ public void addIssuer(X500Principal issuer) { addIssuerNameInternal(issuer.getEncoded(), issuer); } /** * <strong>Denigrated</strong>, use * {@linkplain #addIssuer(X500Principal)} or * {@linkplain #addIssuerName(byte[])} instead. This method should not be * relied on as it can fail to match some CRLs because of a loss of * encoding information in the RFC 2253 String form of some distinguished * names. * <p> * 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. * <p> * 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. * * @param name the name in RFC 2253 form * @throws IOException if a parsing error occurs */ public void addIssuerName(String name) throws IOException { addIssuerNameInternal(name, new X500Name(name).asX500Principal()); } /** * 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. * <p> * 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. * <p> * 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)}. * <p> * Note that the byte array supplied here is cloned to protect against * subsequent modifications. * * @param name a byte array containing the name in ASN.1 DER encoded form * @throws IOException if a parsing error occurs */ public void addIssuerName(byte[] name) throws IOException { // clone because byte arrays are modifiable addIssuerNameInternal(name.clone(), new X500Name(name).asX500Principal()); } /** * A private method that adds a name (String or byte array) to the * issuerNames criterion. The issuer distinguished * name in the <code>X509CRL</code> must match at least one of the specified * distinguished names. * * @param name the name in string or byte array form * @param principal the name in X500Principal form * @throws IOException if a parsing error occurs */ private void addIssuerNameInternal(Object name, X500Principal principal) { if (issuerNames == null) { issuerNames = new HashSet<Object>(); } if (issuerX500Principals == null) { issuerX500Principals = new HashSet<X500Principal>(); } issuerNames.add(name); issuerX500Principals.add(principal); } /** * Clone and check an argument of the form passed to * setIssuerNames. Throw an IOException if the argument is malformed. * * @param names a <code>Collection</code> of names. Each entry is a * String or a byte array (the name, in string or ASN.1 * DER encoded form, respectively). <code>null</code> is * not an acceptable value. * @return a deep copy of the specified <code>Collection</code> * @throws IOException if a parsing error occurs */ private static HashSet<Object> cloneAndCheckIssuerNames(Collection<?> names) throws IOException { HashSet<Object> namesCopy = new HashSet<Object>(); Iterator<?> i = names.iterator(); while (i.hasNext()) { Object nameObject = i.next(); if (!(nameObject instanceof byte []) && !(nameObject instanceof String)) throw new IOException("name not byte array or String"); if (nameObject instanceof byte []) namesCopy.add(((byte []) nameObject).clone()); else namesCopy.add(nameObject); } return(namesCopy); } /** * Clone an argument of the form passed to setIssuerNames. * Throw a RuntimeException if the argument is malformed. * <p> * This method wraps cloneAndCheckIssuerNames, changing any IOException * into a RuntimeException. This method should be used when the object being * cloned has already been checked, so there should never be any exceptions. * * @param names a <code>Collection</code> of names. Each entry is a * String or a byte array (the name, in string or ASN.1 * DER encoded form, respectively). <code>null</code> is * not an acceptable value. * @return a deep copy of the specified <code>Collection</code> * @throws RuntimeException if a parsing error occurs */ private static HashSet<Object> cloneIssuerNames(Collection<Object> names) { try { return cloneAndCheckIssuerNames(names); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Parse an argument of the form passed to setIssuerNames, * returning a Collection of issuerX500Principals. * Throw an IOException if the argument is malformed. * * @param names a <code>Collection</code> of names. Each entry is a * String or a byte array (the name, in string or ASN.1 * DER encoded form, respectively). <Code>Null</Code> is * not an acceptable value. * @return a HashSet of issuerX500Principals * @throws IOException if a parsing error occurs */ private static HashSet<X500Principal> parseIssuerNames(Collection<Object> names) throws IOException { HashSet<X500Principal> x500Principals = new HashSet<X500Principal>(); for (Iterator<Object> t = names.iterator(); t.hasNext(); ) { Object nameObject = t.next(); if (nameObject instanceof String) { x500Principals.add(new X500Name((String)nameObject).asX500Principal()); } else { try { x500Principals.add(new X500Principal((byte[])nameObject)); } catch (IllegalArgumentException e) { throw (IOException)new IOException("Invalid name").initCause(e); } } } return x500Principals; } /** * 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>) */ 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>) */ 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. * <p> * 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 */ public void setDateAndTime(Date dateAndTime) { if (dateAndTime == null) this.dateAndTime = null; else this.dateAndTime = (Date) dateAndTime.clone(); } /** * 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 */ public void setCertificateChecking(X509Certificate cert) { certChecking = cert; } /** * Returns 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. * <p> * If the value returned is not <code>null</code>, it is a * unmodifiable <code>Collection</code> of <code>X500Principal</code>s. * * @return an unmodifiable <code>Collection</code> of names * (or <code>null</code>) * @see #setIssuers * @since 1.5 */ public Collection<X500Principal> getIssuers() { if (issuerX500Principals == null) { return null; } return Collections.unmodifiableCollection(issuerX500Principals); } /** * 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. * <p> * 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. * <p> * 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)}. * <p> * Note that a deep copy is performed on the <code>Collection</code> to * protect against subsequent modifications. * * @return a <code>Collection</code> of names (or <code>null</code>) * @see #setIssuerNames */ public Collection<Object> getIssuerNames() { if (issuerNames == null) { return null; } return cloneIssuerNames(issuerNames); } /** * 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>) */ 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>) */ 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. * <p> * 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 */ public Date getDateAndTime() { if (dateAndTime == null) return null; return (Date) dateAndTime.clone(); } /** * 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 */ public X509Certificate getCertificateChecking() { return certChecking; } /** * Returns a printable representation of the <code>X509CRLSelector</code>. * * @return a <code>String</code> describing the contents of the * <code>X509CRLSelector</code>. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("X509CRLSelector: [\n"); if (issuerNames != null) { sb.append(" IssuerNames:\n"); Iterator<Object> i = issuerNames.iterator(); while (i.hasNext()) sb.append(" " + i.next() + "\n"); } if (minCRL != null) sb.append(" minCRLNumber: " + minCRL + "\n"); if (maxCRL != null) sb.append(" maxCRLNumber: " + maxCRL + "\n"); if (dateAndTime != null) sb.append(" dateAndTime: " + dateAndTime + "\n"); if (certChecking != null) sb.append(" Certificate being checked: " + certChecking + "\n"); sb.append("]"); return sb.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 */ public boolean match(CRL crl) { if (!(crl instanceof X509CRL)) { return false; } X509CRL xcrl = (X509CRL)crl; /* match on issuer name */ if (issuerNames != null) { X500Principal issuer = xcrl.getIssuerX500Principal(); Iterator<X500Principal> i = issuerX500Principals.iterator(); boolean found = false; while (!found && i.hasNext()) { if (i.next().equals(issuer)) { found = true; } } if (!found) { if (debug != null) { debug.println("X509CRLSelector.match: issuer DNs " + "don't match"); } return false; } } if ((minCRL != null) || (maxCRL != null)) { /* Get CRL number extension from CRL */ byte[] crlNumExtVal = xcrl.getExtensionValue("2.5.29.20"); if (crlNumExtVal == null) { if (debug != null) { debug.println("X509CRLSelector.match: no CRLNumber"); } } BigInteger crlNum; try { DerInputStream in = new DerInputStream(crlNumExtVal); byte[] encoded = in.getOctetString(); CRLNumberExtension crlNumExt = new CRLNumberExtension(Boolean.FALSE, encoded); crlNum = (BigInteger)crlNumExt.get(CRLNumberExtension.NUMBER); } catch (IOException ex) { if (debug != null) { debug.println("X509CRLSelector.match: exception in " + "decoding CRL number"); } return false; } /* match on minCRLNumber */ if (minCRL != null) { if (crlNum.compareTo(minCRL) < 0) { if (debug != null) { debug.println("X509CRLSelector.match: CRLNumber too small"); } return false; } } /* match on maxCRLNumber */ if (maxCRL != null) { if (crlNum.compareTo(maxCRL) > 0) { if (debug != null) { debug.println("X509CRLSelector.match: CRLNumber too large"); } return false; } } } /* match on dateAndTime */ if (dateAndTime != null) { Date crlThisUpdate = xcrl.getThisUpdate(); Date nextUpdate = xcrl.getNextUpdate(); if (nextUpdate == null) { if (debug != null) { debug.println("X509CRLSelector.match: nextUpdate null"); } return false; } if (crlThisUpdate.after(dateAndTime) || nextUpdate.before(dateAndTime)) { if (debug != null) { debug.println("X509CRLSelector.match: update out of range"); } 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<Object>(issuerNames); copy.issuerX500Principals = new HashSet<X500Principal>(issuerX500Principals); } return copy; } catch (CloneNotSupportedException e) { /* Cannot happen */ throw new InternalError(e.toString()); } } }