1. /*
  2. * @(#)X509CRLSelector.java 1.11 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.security.cert;
  8. import java.io.IOException;
  9. import java.math.BigInteger;
  10. import java.util.Collection;
  11. import java.util.Date;
  12. import java.util.HashSet;
  13. import java.util.Iterator;
  14. import javax.security.auth.x500.X500Principal;
  15. import sun.security.util.Debug;
  16. import sun.security.util.DerInputStream;
  17. import sun.security.x509.CRLNumberExtension;
  18. import sun.security.x509.X500Name;
  19. /**
  20. * A <code>CRLSelector</code> that selects <code>X509CRLs</code> that
  21. * match all specified criteria. This class is particularly useful when
  22. * selecting CRLs from a <code>CertStore</code> to check revocation status
  23. * of a particular certificate.
  24. * <p>
  25. * When first constructed, an <code>X509CRLSelector</code> has no criteria
  26. * enabled and each of the <code>get</code> methods return a default
  27. * value (<code>null</code>). Therefore, the {@link #match match} method
  28. * would return <code>true</code> for any <code>X509CRL</code>. Typically,
  29. * several criteria are enabled (by calling {@link #setIssuerNames setIssuerNames}
  30. * or {@link #setDateAndTime setDateAndTime}, for instance) and then the
  31. * <code>X509CRLSelector</code> is passed to
  32. * {@link CertStore#getCRLs CertStore.getCRLs} or some similar
  33. * method.
  34. * <p>
  35. * Please refer to RFC 2459 for definitions of the X.509 CRL fields and
  36. * extensions mentioned below.
  37. * <p>
  38. * <b>Concurrent Access</b>
  39. * <p>
  40. * Unless otherwise specified, the methods defined in this class are not
  41. * thread-safe. Multiple threads that need to access a single
  42. * object concurrently should synchronize amongst themselves and
  43. * provide the necessary locking. Multiple threads each manipulating
  44. * separate objects need not synchronize.
  45. *
  46. * @see CRLSelector
  47. * @see X509CRL
  48. *
  49. * @version 1.11 01/23/03
  50. * @since 1.4
  51. * @author Steve Hanna
  52. */
  53. public class X509CRLSelector implements CRLSelector {
  54. static {
  55. CertPathHelperImpl.initialize();
  56. }
  57. private static final Debug debug = Debug.getInstance("certpath");
  58. private HashSet issuerNames;
  59. private HashSet issuerX500Principals;
  60. private BigInteger minCRL;
  61. private BigInteger maxCRL;
  62. private Date dateAndTime;
  63. private X509Certificate certChecking;
  64. /**
  65. * Creates an <code>X509CRLSelector</code>. Initially, no criteria are set
  66. * so any <code>X509CRL</code> will match.
  67. */
  68. public X509CRLSelector() {}
  69. /**
  70. * Sets the issuerNames criterion. The issuer distinguished name in the
  71. * <code>X509CRL</code> must match at least one of the specified
  72. * distinguished names. If <code>null</code>, any issuer distinguished name
  73. * will do.
  74. * <p>
  75. * This method allows the caller to specify, with a single method call,
  76. * the complete set of issuer names which <code>X509CRLs</code> may contain.
  77. * The specified value replaces the previous value for the issuerNames
  78. * criterion.
  79. * <p>
  80. * The <code>names</code> parameter (if not <code>null</code>) is a
  81. * <code>Collection</code> of names. Each name is a <code>String</code>
  82. * or a byte array representing a distinguished name (in RFC 2253 or
  83. * ASN.1 DER encoded form, respectively). If <code>null</code> is supplied
  84. * as the value for this argument, no issuerNames check will be performed.
  85. * <p>
  86. * Note that the <code>names</code> parameter can contain duplicate
  87. * distinguished names, but they may be removed from the
  88. * <code>Collection</code> of names returned by the
  89. * {@link #getIssuerNames getIssuerNames} method.
  90. * <p>
  91. * If a name is specified as a byte array, it should contain a single DER
  92. * encoded distinguished name, as defined in X.501. The ASN.1 notation for
  93. * this structure is as follows.
  94. * <pre><code>
  95. * Name ::= CHOICE {
  96. * RDNSequence }
  97. *
  98. * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
  99. *
  100. * RelativeDistinguishedName ::=
  101. * SET SIZE (1 .. MAX) OF AttributeTypeAndValue
  102. *
  103. * AttributeTypeAndValue ::= SEQUENCE {
  104. * type AttributeType,
  105. * value AttributeValue }
  106. *
  107. * AttributeType ::= OBJECT IDENTIFIER
  108. *
  109. * AttributeValue ::= ANY DEFINED BY AttributeType
  110. * ....
  111. * DirectoryString ::= CHOICE {
  112. * teletexString TeletexString (SIZE (1..MAX)),
  113. * printableString PrintableString (SIZE (1..MAX)),
  114. * universalString UniversalString (SIZE (1..MAX)),
  115. * utf8String UTF8String (SIZE (1.. MAX)),
  116. * bmpString BMPString (SIZE (1..MAX)) }
  117. * </code></pre>
  118. * <p>
  119. * Note that a deep copy is performed on the <code>Collection</code> to
  120. * protect against subsequent modifications.
  121. *
  122. * @param names a <code>Collection</code> of names (or <code>null</code>)
  123. * @throws IOException if a parsing error occurs
  124. * @see #getIssuerNames
  125. */
  126. public void setIssuerNames(Collection names) throws IOException {
  127. if (names == null || names.size() == 0) {
  128. issuerNames = null;
  129. issuerX500Principals = null;
  130. } else {
  131. HashSet tempNames = cloneAndCheckIssuerNames(names);
  132. // Ensure that we either set both of these or neither
  133. issuerX500Principals = parseIssuerNames(tempNames);
  134. issuerNames = tempNames;
  135. }
  136. }
  137. /**
  138. * Adds a name to the issuerNames criterion. The issuer distinguished
  139. * name in the <code>X509CRL</code> must match at least one of the specified
  140. * distinguished names.
  141. * <p>
  142. * This method allows the caller to add a name to the set of issuer names
  143. * which <code>X509CRLs</code> may contain. The specified name is added to
  144. * any previous value for the issuerNames criterion.
  145. * If the specified name is a duplicate, it may be ignored.
  146. *
  147. * @param name the name in RFC 2253 form
  148. * @throws IOException if a parsing error occurs
  149. */
  150. public void addIssuerName(String name) throws IOException {
  151. addIssuerNameInternal(name, new X500Name(name, "RFC2253").asX500Principal());
  152. }
  153. /**
  154. * Adds a name to the issuerNames criterion. The issuer distinguished
  155. * name in the <code>X509CRL</code> must match at least one of the specified
  156. * distinguished names.
  157. * <p>
  158. * This method allows the caller to add a name to the set of issuer names
  159. * which <code>X509CRLs</code> may contain. The specified name is added to
  160. * any previous value for the issuerNames criterion. If the specified name
  161. * is a duplicate, it may be ignored.
  162. * If a name is specified as a byte array, it should contain a single DER
  163. * encoded distinguished name, as defined in X.501. The ASN.1 notation for
  164. * this structure is as follows.
  165. * <p>
  166. * The name is provided as a byte array. This byte array should contain
  167. * a single DER encoded distinguished name, as defined in X.501. The ASN.1
  168. * notation for this structure appears in the documentation for
  169. * {@link #setIssuerNames setIssuerNames(Collection names)}.
  170. * <p>
  171. * Note that the byte array supplied here is cloned to protect against
  172. * subsequent modifications.
  173. *
  174. * @param name a byte array containing the name in ASN.1 DER encoded form
  175. * @throws IOException if a parsing error occurs
  176. */
  177. public void addIssuerName(byte [] name) throws IOException {
  178. // clone because byte arrays are modifiable
  179. addIssuerNameInternal(name.clone(), new X500Name(name).asX500Principal());
  180. }
  181. // called from CertPathHelper, to be made public in a future release
  182. void addIssuer(X500Principal issuer) {
  183. addIssuerNameInternal(issuer.getName(), issuer);
  184. }
  185. // called from CertPathHelper
  186. Collection getIssuers() {
  187. return issuerX500Principals;
  188. }
  189. /**
  190. * A private method that adds a name (String or byte array) to the
  191. * issuerNames criterion. The issuer distinguished
  192. * name in the <code>X509CRL</code> must match at least one of the specified
  193. * distinguished names.
  194. *
  195. * @param name the name in string or byte array form
  196. * @param principal the name in X500Principal form
  197. * @throws IOException if a parsing error occurs
  198. */
  199. private void addIssuerNameInternal(Object name, X500Principal principal) {
  200. if (issuerNames == null) {
  201. issuerNames = new HashSet();
  202. }
  203. if (issuerX500Principals == null) {
  204. issuerX500Principals = new HashSet();
  205. }
  206. issuerNames.add(name);
  207. issuerX500Principals.add(principal);
  208. }
  209. /**
  210. * Clone and check an argument of the form passed to
  211. * setIssuerNames. Throw an IOException if the argument is malformed.
  212. *
  213. * @param names a <code>Collection</code> of names. Each entry is a
  214. * String or a byte array (the name, in string or ASN.1
  215. * DER encoded form, respectively). <code>null</code> is
  216. * not an acceptable value.
  217. * @return a deep copy of the specified <code>Collection</code>
  218. * @throws IOException if a parsing error occurs
  219. */
  220. private static HashSet cloneAndCheckIssuerNames(Collection names)
  221. throws IOException
  222. {
  223. HashSet namesCopy = new HashSet();
  224. Iterator i = names.iterator();
  225. while (i.hasNext()) {
  226. Object nameObject = i.next();
  227. if (!(nameObject instanceof byte []) &&
  228. !(nameObject instanceof String))
  229. throw new IOException("name not byte array or String");
  230. if (nameObject instanceof byte [])
  231. namesCopy.add(((byte []) nameObject).clone());
  232. else
  233. namesCopy.add(nameObject);
  234. }
  235. return(namesCopy);
  236. }
  237. /**
  238. * Clone an argument of the form passed to setIssuerNames.
  239. * Throw a RuntimeException if the argument is malformed.
  240. * <p>
  241. * This method wraps cloneAndCheckIssuerNames, changing any IOException
  242. * into a RuntimeException. This method should be used when the object being
  243. * cloned has already been checked, so there should never be any exceptions.
  244. *
  245. * @param names a <code>Collection</code> of names. Each entry is a
  246. * String or a byte array (the name, in string or ASN.1
  247. * DER encoded form, respectively). <code>null</code> is
  248. * not an acceptable value.
  249. * @return a deep copy of the specified <code>Collection</code>
  250. * @throws RuntimeException if a parsing error occurs
  251. */
  252. private static HashSet cloneIssuerNames(Collection names) {
  253. try {
  254. return cloneAndCheckIssuerNames(names);
  255. } catch (IOException ioe) {
  256. throw new RuntimeException(ioe);
  257. }
  258. }
  259. /**
  260. * Parse an argument of the form passed to setIssuerNames,
  261. * returning a Collection of issuerX500Principals.
  262. * Throw an IOException if the argument is malformed.
  263. *
  264. * @param names a <code>Collection</code> of names. Each entry is a
  265. * String or a byte array (the name, in string or ASN.1
  266. * DER encoded form, respectively). <Code>Null</Code> is
  267. * not an acceptable value.
  268. * @return a HashSet of issuerX500Principals
  269. * @throws IOException if a parsing error occurs
  270. */
  271. private static HashSet parseIssuerNames(Collection names)
  272. throws IOException {
  273. HashSet x500Principals = new HashSet();
  274. for (Iterator t = names.iterator(); t.hasNext(); ) {
  275. Object nameObject = t.next();
  276. if (nameObject instanceof String) {
  277. x500Principals.add(new X500Name((String)nameObject, "RFC2253").asX500Principal());
  278. } else {
  279. try {
  280. x500Principals.add(new X500Principal((byte[])nameObject));
  281. } catch (IllegalArgumentException e) {
  282. throw (IOException)new IOException("Invalid name").initCause(e);
  283. }
  284. }
  285. }
  286. return x500Principals;
  287. }
  288. /**
  289. * Sets the minCRLNumber criterion. The <code>X509CRL</code> must have a
  290. * CRL number extension whose value is greater than or equal to the
  291. * specified value. If <code>null</code>, no minCRLNumber check will be
  292. * done.
  293. *
  294. * @param minCRL the minimum CRL number accepted (or <code>null</code>)
  295. */
  296. public void setMinCRLNumber(BigInteger minCRL) {
  297. this.minCRL = minCRL;
  298. }
  299. /**
  300. * Sets the maxCRLNumber criterion. The <code>X509CRL</code> must have a
  301. * CRL number extension whose value is less than or equal to the
  302. * specified value. If <code>null</code>, no maxCRLNumber check will be
  303. * done.
  304. *
  305. * @param maxCRL the maximum CRL number accepted (or <code>null</code>)
  306. */
  307. public void setMaxCRLNumber(BigInteger maxCRL) {
  308. this.maxCRL = maxCRL;
  309. }
  310. /**
  311. * Sets the dateAndTime criterion. The specified date must be
  312. * equal to or later than the value of the thisUpdate component
  313. * of the <code>X509CRL</code> and earlier than the value of the
  314. * nextUpdate component. There is no match if the <code>X509CRL</code>
  315. * does not contain a nextUpdate component.
  316. * If <code>null</code>, no dateAndTime check will be done.
  317. * <p>
  318. * Note that the <code>Date</code> supplied here is cloned to protect
  319. * against subsequent modifications.
  320. *
  321. * @param dateAndTime the <code>Date</code> to match against
  322. * (or <code>null</code>)
  323. * @see #getDateAndTime
  324. */
  325. public void setDateAndTime(Date dateAndTime) {
  326. if (dateAndTime == null)
  327. this.dateAndTime = null;
  328. else
  329. this.dateAndTime = (Date) dateAndTime.clone();
  330. }
  331. /**
  332. * Sets the certificate being checked. This is not a criterion. Rather,
  333. * it is optional information that may help a <code>CertStore</code>
  334. * find CRLs that would be relevant when checking revocation for the
  335. * specified certificate. If <code>null</code> is specified, then no
  336. * such optional information is provided.
  337. *
  338. * @param cert the <code>X509Certificate</code> being checked
  339. * (or <code>null</code>)
  340. * @see #getCertificateChecking
  341. */
  342. public void setCertificateChecking(X509Certificate cert) {
  343. certChecking = cert;
  344. }
  345. /**
  346. * Returns a copy of the issuerNames criterion. The issuer distinguished
  347. * name in the <code>X509CRL</code> must match at least one of the specified
  348. * distinguished names. If the value returned is <code>null</code>, any
  349. * issuer distinguished name will do.
  350. * <p>
  351. * If the value returned is not <code>null</code>, it is a
  352. * <code>Collection</code> of names. Each name is a <code>String</code>
  353. * or a byte array representing a distinguished name (in RFC 2253 or
  354. * ASN.1 DER encoded form, respectively). Note that the
  355. * <code>Collection</code> returned may contain duplicate names.
  356. * <p>
  357. * If a name is specified as a byte array, it should contain a single DER
  358. * encoded distinguished name, as defined in X.501. The ASN.1 notation for
  359. * this structure is given in the documentation for
  360. * {@link #setIssuerNames setIssuerNames(Collection names)}.
  361. * <p>
  362. * Note that a deep copy is performed on the <code>Collection</code> to
  363. * protect against subsequent modifications.
  364. *
  365. * @return a <code>Collection</code> of names (or <code>null</code>)
  366. * @see #setIssuerNames
  367. */
  368. public Collection getIssuerNames() {
  369. if (issuerNames == null)
  370. return null;
  371. return(cloneIssuerNames(issuerNames));
  372. }
  373. /**
  374. * Returns the minCRLNumber criterion. The <code>X509CRL</code> must have a
  375. * CRL number extension whose value is greater than or equal to the
  376. * specified value. If <code>null</code>, no minCRLNumber check will be done.
  377. *
  378. * @return the minimum CRL number accepted (or <code>null</code>)
  379. */
  380. public BigInteger getMinCRL() {
  381. return minCRL;
  382. }
  383. /**
  384. * Returns the maxCRLNumber criterion. The <code>X509CRL</code> must have a
  385. * CRL number extension whose value is less than or equal to the
  386. * specified value. If <code>null</code>, no maxCRLNumber check will be
  387. * done.
  388. *
  389. * @return the maximum CRL number accepted (or <code>null</code>)
  390. */
  391. public BigInteger getMaxCRL() {
  392. return maxCRL;
  393. }
  394. /**
  395. * Returns the dateAndTime criterion. The specified date must be
  396. * equal to or later than the value of the thisUpdate component
  397. * of the <code>X509CRL</code> and earlier than the value of the
  398. * nextUpdate component. There is no match if the
  399. * <code>X509CRL</code> does not contain a nextUpdate component.
  400. * If <code>null</code>, no dateAndTime check will be done.
  401. * <p>
  402. * Note that the <code>Date</code> returned is cloned to protect against
  403. * subsequent modifications.
  404. *
  405. * @return the <code>Date</code> to match against (or <code>null</code>)
  406. * @see #setDateAndTime
  407. */
  408. public Date getDateAndTime() {
  409. if (dateAndTime == null)
  410. return null;
  411. return (Date) dateAndTime.clone();
  412. }
  413. /**
  414. * Returns the certificate being checked. This is not a criterion. Rather,
  415. * it is optional information that may help a <code>CertStore</code>
  416. * find CRLs that would be relevant when checking revocation for the
  417. * specified certificate. If the value returned is <code>null</code>, then
  418. * no such optional information is provided.
  419. *
  420. * @return the certificate being checked (or <code>null</code>)
  421. * @see #setCertificateChecking
  422. */
  423. public X509Certificate getCertificateChecking() {
  424. return certChecking;
  425. }
  426. /**
  427. * Returns a printable representation of the <code>X509CRLSelector</code>.
  428. *
  429. * @return a <code>String</code> describing the contents of the
  430. * <code>X509CRLSelector</code>.
  431. */
  432. public String toString() {
  433. StringBuffer sb = new StringBuffer();
  434. sb.append("X509CRLSelector: [\n");
  435. if (issuerNames != null) {
  436. sb.append(" IssuerNames:\n");
  437. Iterator i = issuerNames.iterator();
  438. while (i.hasNext())
  439. sb.append(" " + i.next() + "\n");
  440. }
  441. if (minCRL != null)
  442. sb.append(" minCRLNumber: " + minCRL + "\n");
  443. if (maxCRL != null)
  444. sb.append(" maxCRLNumber: " + maxCRL + "\n");
  445. if (dateAndTime != null)
  446. sb.append(" dateAndTime: " + dateAndTime + "\n");
  447. if (certChecking != null)
  448. sb.append(" Certificate being checked: " + certChecking + "\n");
  449. sb.append("]");
  450. return sb.toString();
  451. }
  452. /**
  453. * Decides whether a <code>CRL</code> should be selected.
  454. *
  455. * @param crl the <code>CRL</code> to be checked
  456. * @return <code>true</code> if the <code>CRL</code> should be selected,
  457. * <code>false</code> otherwise
  458. */
  459. public boolean match(CRL crl) {
  460. if (!(crl instanceof X509CRL)) {
  461. return false;
  462. }
  463. X509CRL xcrl = (X509CRL)crl;
  464. /* match on issuer name */
  465. if (issuerNames != null) {
  466. X500Principal issuer = xcrl.getIssuerX500Principal();
  467. Iterator i = issuerX500Principals.iterator();
  468. boolean found = false;
  469. while (!found && i.hasNext()) {
  470. if (i.next().equals(issuer)) {
  471. found = true;
  472. }
  473. }
  474. if (!found) {
  475. if (debug != null) {
  476. debug.println("X509CRLSelector.match: issuer DNs "
  477. + "don't match");
  478. }
  479. return false;
  480. }
  481. }
  482. if ((minCRL != null) || (maxCRL != null)) {
  483. /* Get CRL number extension from CRL */
  484. byte[] crlNumExtVal = xcrl.getExtensionValue("2.5.29.20");
  485. if (crlNumExtVal == null) {
  486. if (debug != null) {
  487. debug.println("X509CRLSelector.match: no CRLNumber");
  488. }
  489. }
  490. BigInteger crlNum;
  491. try {
  492. DerInputStream in = new DerInputStream(crlNumExtVal);
  493. byte[] encoded = in.getOctetString();
  494. CRLNumberExtension crlNumExt =
  495. new CRLNumberExtension(Boolean.FALSE, encoded);
  496. crlNum = (BigInteger)crlNumExt.get(CRLNumberExtension.NUMBER);
  497. } catch (IOException ex) {
  498. if (debug != null) {
  499. debug.println("X509CRLSelector.match: exception in "
  500. + "decoding CRL number");
  501. }
  502. return false;
  503. }
  504. /* match on minCRLNumber */
  505. if (minCRL != null) {
  506. if (crlNum.compareTo(minCRL) < 0) {
  507. if (debug != null) {
  508. debug.println("X509CRLSelector.match: CRLNumber too small");
  509. }
  510. return false;
  511. }
  512. }
  513. /* match on maxCRLNumber */
  514. if (maxCRL != null) {
  515. if (crlNum.compareTo(maxCRL) > 0) {
  516. if (debug != null) {
  517. debug.println("X509CRLSelector.match: CRLNumber too large");
  518. }
  519. return false;
  520. }
  521. }
  522. }
  523. /* match on dateAndTime */
  524. if (dateAndTime != null) {
  525. Date crlThisUpdate = xcrl.getThisUpdate();
  526. Date nextUpdate = xcrl.getNextUpdate();
  527. if (nextUpdate == null) {
  528. if (debug != null) {
  529. debug.println("X509CRLSelector.match: nextUpdate null");
  530. }
  531. return false;
  532. }
  533. if (crlThisUpdate.after(dateAndTime)
  534. || nextUpdate.before(dateAndTime)) {
  535. if (debug != null) {
  536. debug.println("X509CRLSelector.match: update out of range");
  537. }
  538. return false;
  539. }
  540. }
  541. return true;
  542. }
  543. /**
  544. * Returns a copy of this object.
  545. *
  546. * @return the copy
  547. */
  548. public Object clone() {
  549. try {
  550. Object copy = super.clone();
  551. if (issuerNames != null) {
  552. issuerNames = new HashSet(issuerNames);
  553. issuerX500Principals = new HashSet(issuerX500Principals);
  554. }
  555. return copy;
  556. } catch (CloneNotSupportedException e) {
  557. /* Cannot happen */
  558. throw new InternalError(e.toString());
  559. }
  560. }
  561. }