Source for java.security.cert.X509CertSelector

   1: /* X509CertSelector.java -- selects X.509 certificates by criteria.
   2:    Copyright (C) 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.security.cert;
  40: 
  41: import gnu.classpath.SystemProperties;
  42: import gnu.java.security.OID;
  43: 
  44: import java.io.IOException;
  45: import java.math.BigInteger;
  46: import java.security.KeyFactory;
  47: import java.security.PublicKey;
  48: import java.security.spec.X509EncodedKeySpec;
  49: import java.util.ArrayList;
  50: import java.util.Arrays;
  51: import java.util.Collection;
  52: import java.util.Collections;
  53: import java.util.Date;
  54: import java.util.HashSet;
  55: import java.util.Iterator;
  56: import java.util.LinkedList;
  57: import java.util.List;
  58: import java.util.Set;
  59: 
  60: import javax.security.auth.x500.X500Principal;
  61: 
  62: /**
  63:  * A concrete implementation of {@link CertSelector} for X.509 certificates,
  64:  * which allows a number of criteria to be set when accepting certificates,
  65:  * from validity dates, to issuer and subject distinguished names, to some
  66:  * of the various X.509 extensions.
  67:  *
  68:  * <p>Use of this class requires extensive knowledge of the Internet
  69:  * Engineering Task Force's Public Key Infrastructure (X.509). The primary
  70:  * document describing this standard is <a
  71:  * href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
  72:  * Public Key Infrastructure Certificate and Certificate Revocation List
  73:  * (CRL) Profile</a>.
  74:  *
  75:  * <p>Note that this class is not thread-safe. If multiple threads will
  76:  * use or modify this class then they need to synchronize on the object.
  77:  *
  78:  * @author Casey Marshall (csm@gnu.org)
  79:  */
  80: public class X509CertSelector implements CertSelector, Cloneable
  81: {
  82: 
  83:   // Constants and fields.
  84:   // -------------------------------------------------------------------------
  85: 
  86:   private static final String AUTH_KEY_ID = "2.5.29.35";
  87:   private static final String SUBJECT_KEY_ID = "2.5.29.14";
  88:   private static final String NAME_CONSTRAINTS_ID = "2.5.29.30";
  89: 
  90:   private int basicConstraints;
  91:   private X509Certificate cert;
  92:   private BigInteger serialNo;
  93:   private X500Principal issuer;
  94:   private X500Principal subject;
  95:   private byte[] subjectKeyId;
  96:   private byte[] authKeyId;
  97:   private boolean[] keyUsage;
  98:   private Date certValid;
  99:   private OID sigId;
 100:   private PublicKey subjectKey;
 101:   private X509EncodedKeySpec subjectKeySpec;
 102:   private Set keyPurposeSet;
 103:   private List altNames;
 104:   private boolean matchAllNames;
 105:   private byte[] nameConstraints;
 106:   private Set policy;
 107: 
 108:   // Constructors.
 109:   // ------------------------------------------------------------------------
 110: 
 111:   /**
 112:    * Creates a new X.509 certificate selector. The new selector will be
 113:    * empty, and will accept any certificate (provided that it is an
 114:    * {@link X509Certificate}).
 115:    */
 116:   public X509CertSelector()
 117:   {
 118:     basicConstraints = -1;
 119:   }
 120: 
 121:   // Instance methods.
 122:   // ------------------------------------------------------------------------
 123: 
 124:   /**
 125:    * Returns the certificate criterion, or <code>null</code> if this value
 126:    * was not set.
 127:    *
 128:    * @return The certificate.
 129:    */
 130:   public X509Certificate getCertificate()
 131:   {
 132:     return cert;
 133:   }
 134: 
 135:   /**
 136:    * Sets the certificate criterion. If set, only certificates that are
 137:    * equal to the certificate passed here will be accepted.
 138:    *
 139:    * @param cert The certificate.
 140:    */
 141:   public void setCertificate(X509Certificate cert)
 142:   {
 143:     this.cert = cert;
 144:   }
 145: 
 146:   /**
 147:    * Returns the serial number criterion, or <code>null</code> if this
 148:    * value was not set.
 149:    *
 150:    * @return The serial number.
 151:    */
 152:   public BigInteger getSerialNumber()
 153:   {
 154:     return serialNo;
 155:   }
 156: 
 157:   /**
 158:    * Sets the serial number of the desired certificate. Only certificates that
 159:    * contain this serial number are accepted.
 160:    *
 161:    * @param serialNo The serial number.
 162:    */
 163:   public void setSerialNumber(BigInteger serialNo)
 164:   {
 165:     this.serialNo = serialNo;
 166:   }
 167: 
 168:   /**
 169:    * Returns the issuer criterion as a string, or <code>null</code> if this
 170:    * value was not set.
 171:    *
 172:    * @return The issuer.
 173:    */
 174:   public String getIssuerAsString()
 175:   {
 176:     if (issuer != null)
 177:       return issuer.getName();
 178:     else
 179:       return null;
 180:   }
 181: 
 182:   /**
 183:    * Returns the issuer criterion as a sequence of DER bytes, or
 184:    * <code>null</code> if this value was not set.
 185:    *
 186:    * @return The issuer.
 187:    */
 188:   public byte[] getIssuerAsBytes() throws IOException
 189:   {
 190:     if (issuer != null)
 191:       return issuer.getEncoded();
 192:     else
 193:       return null;
 194:   }
 195: 
 196:   /**
 197:    * Sets the issuer, specified as a string representation of the issuer's
 198:    * distinguished name. Only certificates issued by this issuer will
 199:    * be accepted.
 200:    *
 201:    * @param name The string representation of the issuer's distinguished name.
 202:    * @throws IOException If the given name is incorrectly formatted.
 203:    */
 204:   public void setIssuer(String name) throws IOException
 205:   {
 206:     if (name != null)
 207:       {
 208:         try
 209:           {
 210:             issuer = new X500Principal(name);
 211:           }
 212:         catch (IllegalArgumentException iae)
 213:           {
 214:             throw new IOException(iae.getMessage());
 215:           }
 216:       }
 217:     else
 218:       issuer = null;
 219:   }
 220: 
 221:   /**
 222:    * Sets the issuer, specified as the DER encoding of the issuer's
 223:    * distinguished name. Only certificates issued by this issuer will
 224:    * be accepted.
 225:    *
 226:    * @param name The DER encoding of the issuer's distinguished name.
 227:    * @throws IOException If the given name is incorrectly formatted.
 228:    */
 229:   public void setIssuer(byte[] name) throws IOException
 230:   {
 231:     if (name != null)
 232:       {
 233:         try
 234:           {
 235:             issuer = new X500Principal(name);
 236:           }
 237:         catch (IllegalArgumentException iae)
 238:           {
 239:             throw new IOException(iae.getMessage());
 240:           }
 241:       }
 242:     else
 243:       issuer = null;
 244:   }
 245: 
 246:   /**
 247:    * Returns the subject criterion as a string, of <code>null</code> if
 248:    * this value was not set.
 249:    *
 250:    * @return The subject.
 251:    */
 252:   public String getSubjectAsString()
 253:   {
 254:     if (subject != null)
 255:       return subject.getName();
 256:     else
 257:       return null;
 258:   }
 259: 
 260:   /**
 261:    * Returns the subject criterion as a sequence of DER bytes, or
 262:    * <code>null</code> if this value is not set.
 263:    *
 264:    * @return The subject.
 265:    */
 266:   public byte[] getSubjectAsBytes() throws IOException
 267:   {
 268:     if (subject != null)
 269:       return subject.getEncoded();
 270:     else
 271:       return null;
 272:   }
 273: 
 274:   /**
 275:    * Sets the subject, specified as a string representation of the
 276:    * subject's distinguished name. Only certificates with the given
 277:    * subject will be accepted.
 278:    *
 279:    * @param name The string representation of the subject's distinguished name.
 280:    * @throws IOException If the given name is incorrectly formatted.
 281:    */
 282:   public void setSubject(String name) throws IOException
 283:   {
 284:     if (name != null)
 285:       {
 286:         try
 287:           {
 288:             subject = new X500Principal(name);
 289:           }
 290:         catch (IllegalArgumentException iae)
 291:           {
 292:             throw new IOException(iae.getMessage());
 293:           }
 294:       }
 295:     else
 296:       subject = null;
 297:   }
 298: 
 299:   /**
 300:    * Sets the subject, specified as the DER encoding of the subject's
 301:    * distinguished name. Only certificates with the given subject will
 302:    * be accepted.
 303:    *
 304:    * @param name The DER encoding of the subject's distinguished name.
 305:    * @throws IOException If the given name is incorrectly formatted.
 306:    */
 307:   public void setSubject(byte[] name) throws IOException
 308:   {
 309:     if (name != null)
 310:       {
 311:         try
 312:           {
 313:             subject = new X500Principal(name);
 314:           }
 315:         catch (IllegalArgumentException iae)
 316:           {
 317:             throw new IOException(iae.getMessage());
 318:           }
 319:       }
 320:     else
 321:       subject = null;
 322:   }
 323: 
 324:   /**
 325:    * Returns the subject key identifier criterion, or <code>null</code> if
 326:    * this value was not set. Note that the byte array is cloned to prevent
 327:    * modification.
 328:    *
 329:    * @return The subject key identifier.
 330:    */
 331:   public byte[] getSubjectKeyIdentifier()
 332:   {
 333:     if (subjectKeyId != null)
 334:       return (byte[]) subjectKeyId.clone();
 335:     else
 336:       return null;
 337:   }
 338: 
 339:   /**
 340:    * Sets the subject key identifier criterion, or <code>null</code> to clear
 341:    * this criterion. Note that the byte array is cloned to prevent modification.
 342:    *
 343:    * @param subjectKeyId The subject key identifier.
 344:    */
 345:   public void setSubjectKeyIdentifier(byte[] subjectKeyId)
 346:   {
 347:     this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() :
 348:       null;
 349:   }
 350: 
 351:   /**
 352:    * Returns the authority key identifier criterion, or <code>null</code> if
 353:    * this value was not set. Note that the byte array is cloned to prevent
 354:    * modification.
 355:    *
 356:    * @return The authority key identifier.
 357:    */
 358:   public byte[] getAuthorityKeyIdentifier()
 359:   {
 360:     if (authKeyId != null)
 361:       return (byte[]) authKeyId.clone();
 362:     else
 363:       return null;
 364:   }
 365: 
 366:   /**
 367:    * Sets the authority key identifier criterion, or <code>null</code> to clear
 368:    * this criterion. Note that the byte array is cloned to prevent modification.
 369:    *
 370:    * @param authKeyId The authority key identifier.
 371:    */
 372:   public void setAuthorityKeyIdentifier(byte[] authKeyId)
 373:   {
 374:     this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null;
 375:   }
 376: 
 377:   /**
 378:    * Returns the date at which certificates must be valid, or <code>null</code>
 379:    * if this criterion was not set.
 380:    *
 381:    * @return The target certificate valitity date.
 382:    */
 383:   public Date getCertificateValid()
 384:   {
 385:     if (certValid != null)
 386:       return (Date) certValid.clone();
 387:     else
 388:       return null;
 389:   }
 390: 
 391:   /**
 392:    * Sets the date at which certificates must be valid. Specify
 393:    * <code>null</code> to clear this criterion.
 394:    *
 395:    * @param certValid The certificate validity date.
 396:    */
 397:   public void setCertificateValid(Date certValid)
 398:   {
 399:     this.certValid = certValid != null ? (Date) certValid.clone() : null;
 400:   }
 401: 
 402:   /**
 403:    * This method, and its related X.509 certificate extension &mdash; the
 404:    * private key usage period &mdash; is not supported under the Internet
 405:    * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this
 406:    * method is not supported either.
 407:    *
 408:    * <p>Do not use this method. It is not deprecated, as it is not deprecated
 409:    * in the Java standard, but it is basically a no-operation and simply
 410:    * returns <code>null</code>.
 411:    *
 412:    * @return Null.
 413:    */
 414:   public Date getPrivateKeyValid()
 415:   {
 416:     return null;
 417:   }
 418: 
 419:   /**
 420:    * This method, and its related X.509 certificate extension &mdash; the
 421:    * private key usage period &mdash; is not supported under the Internet
 422:    * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this
 423:    * method is not supported either.
 424:    *
 425:    * <p>Do not use this method. It is not deprecated, as it is not deprecated
 426:    * in the Java standard, but it is basically a no-operation.
 427:    *
 428:    * @param UNUSED Is silently ignored.
 429:    */
 430:   public void setPrivateKeyValid(Date UNUSED)
 431:   {
 432:   }
 433: 
 434:   /**
 435:    * Returns the public key algorithm ID that matching certificates must have,
 436:    * or <code>null</code> if this criterion was not set.
 437:    *
 438:    * @return The public key algorithm ID.
 439:    */
 440:   public String getSubjectPublicKeyAlgID()
 441:   {
 442:     return String.valueOf(sigId);
 443:   }
 444: 
 445:   /**
 446:    * Sets the public key algorithm ID that matching certificates must have.
 447:    * Specify <code>null</code> to clear this criterion.
 448:    *
 449:    * @param sigId The public key ID.
 450:    * @throws IOException If the specified ID is not a valid object identifier.
 451:    */
 452:   public void setSubjectPublicKeyAlgID(String sigId) throws IOException
 453:   {
 454:     if (sigId != null)
 455:       {
 456:         try
 457:           {
 458:             OID oid = new OID(sigId);
 459:             int[] comp = oid.getIDs();
 460:             if (!checkOid(comp))
 461:               throw new IOException("malformed OID: " + sigId);
 462:             this.sigId = oid;
 463:           }
 464:         catch (IllegalArgumentException iae)
 465:           {
 466:             IOException ioe = new IOException("malformed OID: " + sigId);
 467:             ioe.initCause(iae);
 468:             throw ioe;
 469:           }
 470:       }
 471:     else
 472:       this.sigId = null;
 473:   }
 474: 
 475:   /**
 476:    * Returns the subject public key criterion, or <code>null</code> if this
 477:    * value is not set.
 478:    *
 479:    * @return The subject public key.
 480:    */
 481:   public PublicKey getSubjectPublicKey()
 482:   {
 483:     return subjectKey;
 484:   }
 485: 
 486:   /**
 487:    * Sets the subject public key criterion as an opaque representation.
 488:    * Specify <code>null</code> to clear this criterion.
 489:    *
 490:    * @param key The public key.
 491:    */
 492:   public void setSubjectPublicKey(PublicKey key)
 493:   {
 494:     this.subjectKey = key;
 495:     if (key == null)
 496:       {
 497:         subjectKeySpec = null;
 498:         return;
 499:       }
 500:     try
 501:       {
 502:         KeyFactory enc = KeyFactory.getInstance("X.509");
 503:         subjectKeySpec = (X509EncodedKeySpec)
 504:           enc.getKeySpec(key, X509EncodedKeySpec.class);
 505:       }
 506:     catch (Exception x)
 507:       {
 508:         subjectKey = null;
 509:         subjectKeySpec = null;
 510:       }
 511:   }
 512: 
 513:   /**
 514:    * Sets the subject public key criterion as a DER-encoded key. Specify
 515:    * <code>null</code> to clear this value.
 516:    *
 517:    * @param key The DER-encoded key bytes.
 518:    * @throws IOException If the argument is not a valid DER-encoded key.
 519:    */
 520:   public void setSubjectPublicKey(byte[] key) throws IOException
 521:   {
 522:     if (key == null)
 523:       {
 524:         subjectKey = null;
 525:         subjectKeySpec = null;
 526:         return;
 527:       }
 528:     try
 529:       {
 530:         subjectKeySpec = new X509EncodedKeySpec(key);
 531:         KeyFactory enc = KeyFactory.getInstance("X.509");
 532:         subjectKey = enc.generatePublic(subjectKeySpec);
 533:       }
 534:     catch (Exception x)
 535:       {
 536:         subjectKey = null;
 537:         subjectKeySpec = null;
 538:         IOException ioe = new IOException(x.getMessage());
 539:         ioe.initCause(x);
 540:         throw ioe;
 541:       }
 542:   }
 543: 
 544:   /**
 545:    * Returns the public key usage criterion, or <code>null</code> if this
 546:    * value is not set. Note that the array is cloned to prevent modification.
 547:    *
 548:    * @return The public key usage.
 549:    */
 550:   public boolean[] getKeyUsage()
 551:   {
 552:     if (keyUsage != null)
 553:       return (boolean[]) keyUsage.clone();
 554:     else
 555:       return null;
 556:   }
 557: 
 558:   /**
 559:    * Sets the public key usage criterion. Specify <code>null</code> to clear
 560:    * this value.
 561:    *
 562:    * @param keyUsage The public key usage.
 563:    */
 564:   public void setKeyUsage(boolean[] keyUsage)
 565:   {
 566:     this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null;
 567:   }
 568: 
 569:   /**
 570:    * Returns the set of extended key purpose IDs, as an unmodifiable set
 571:    * of OID strings. Returns <code>null</code> if this criterion is not
 572:    * set.
 573:    *
 574:    * @return The set of key purpose OIDs (strings).
 575:    */
 576:   public Set getExtendedKeyUsage()
 577:   {
 578:     if (keyPurposeSet != null)
 579:       return Collections.unmodifiableSet(keyPurposeSet);
 580:     else
 581:       return null;
 582:   }
 583: 
 584:   /**
 585:    * Sets the extended key usage criterion, as a set of OID strings. Specify
 586:    * <code>null</code> to clear this value.
 587:    *
 588:    * @param keyPurposeSet The set of key purpose OIDs.
 589:    * @throws IOException If any element of the set is not a valid OID string.
 590:    */
 591:   public void setExtendedKeyUsage(Set keyPurposeSet) throws IOException
 592:   {
 593:     if (keyPurposeSet == null)
 594:       {
 595:         this.keyPurposeSet = null;
 596:         return;
 597:       }
 598:     Set s = new HashSet();
 599:     for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); )
 600:       {
 601:         Object o = it.next();
 602:         if (!(o instanceof String))
 603:           throw new IOException("not a string: " + o);
 604:         try
 605:           {
 606:             OID oid = new OID((String) o);
 607:             int[] comp = oid.getIDs();
 608:             if (!checkOid(comp))
 609:               throw new IOException("malformed OID: " + o);
 610:           }
 611:         catch (IllegalArgumentException iae)
 612:           {
 613:             IOException ioe = new IOException("malformed OID: " + o);
 614:             ioe.initCause(iae);
 615:             throw ioe;
 616:           }
 617:       }
 618:     this.keyPurposeSet = s;
 619:   }
 620: 
 621:   /**
 622:    * Returns whether or not all specified alternative names must match.
 623:    * If false, a certificate is considered a match if <em>one</em> of the
 624:    * specified alternative names matches.
 625:    *
 626:    * @return true if all names must match.
 627:    */
 628:   public boolean getMatchAllSubjectAltNames()
 629:   {
 630:     return matchAllNames;
 631:   }
 632: 
 633:   /**
 634:    * Sets whether or not all subject alternative names must be matched.
 635:    * If false, then a certificate will be considered a match if one
 636:    * alternative name matches.
 637:    *
 638:    * @param matchAllNames Whether or not all alternative names must be
 639:    *        matched.
 640:    */
 641:   public void setMatchAllSubjectAltNames(boolean matchAllNames)
 642:   {
 643:     this.matchAllNames = matchAllNames;
 644:   }
 645: 
 646:   /**
 647:    * Sets the subject alternative names critertion. Each element of the
 648:    * argument must be a {@link java.util.List} that contains exactly two
 649:    * elements: the first an {@link Integer}, representing the type of
 650:    * name, and the second either a {@link String} or a byte array,
 651:    * representing the name itself.
 652:    *
 653:    * @param altNames The alternative names.
 654:    * @throws IOException If any element of the argument is invalid.
 655:    */
 656:   public void setSubjectAlternativeNames(Collection altNames)
 657:     throws IOException
 658:   {
 659:     if (altNames == null)
 660:       {
 661:         this.altNames = null;
 662:         return;
 663:       }
 664:     List l = new ArrayList(altNames.size());
 665:     for (Iterator it = altNames.iterator(); it.hasNext(); )
 666:       {
 667:         Object o = it.next();
 668:         if (!(o instanceof List) || ((List) o).size() != 2 ||
 669:             !(((List) o).get(0) instanceof Integer) ||
 670:             !(((List) o).get(1) instanceof String) ||
 671:             !(((List) o).get(1) instanceof byte[]))
 672:           throw new IOException("illegal alternative name: " + o);
 673:         Integer i = (Integer) ((List) o).get(0);
 674:         if (i.intValue() < 0 || i.intValue() > 8)
 675:           throw new IOException("illegal alternative name: " + o +
 676:                                 ", bad id: " + i);
 677:         l.add(new ArrayList((List) o));
 678:       }
 679:     this.altNames = l;
 680:   }
 681: 
 682:   /**
 683:    * Add a name to the subject alternative names criterion.
 684:    *
 685:    * @param id The type of name this is. Must be in the range [0,8].
 686:    * @param name The name.
 687:    * @throws IOException If the id is out of range, or if the name
 688:    *   is null.
 689:    */
 690:   public void addSubjectAlternativeName(int id, String name)
 691:     throws IOException
 692:   {
 693:     if (id < 0 || id > 8 || name == null)
 694:       throw new IOException("illegal alternative name");
 695:     if (altNames == null)
 696:       altNames = new LinkedList();
 697:     ArrayList l = new ArrayList(2);
 698:     l.add(Integer.valueOf(id));
 699:     l.add(name);
 700:     altNames.add(l);
 701:   }
 702: 
 703:   /**
 704:    * Add a name, as DER-encoded bytes, to the subject alternative names
 705:    * criterion.
 706:    *
 707:    * @param id The type of name this is.
 708:    */
 709:   public void addSubjectAlternativeName(int id, byte[] name)
 710:     throws IOException
 711:   {
 712:     if (id < 0 || id > 8 || name == null)
 713:       throw new IOException("illegal alternative name");
 714:     if (altNames == null)
 715:       altNames = new LinkedList();
 716:     ArrayList l = new ArrayList(2);
 717:     l.add(Integer.valueOf(id));
 718:     l.add(name);
 719:     altNames.add(l);
 720:   }
 721: 
 722:   /**
 723:    * Returns the name constraints criterion, or <code>null</code> if this
 724:    * value is not set. Note that the byte array is cloned to prevent
 725:    * modification.
 726:    *
 727:    * @return The name constraints.
 728:    */
 729:   public byte[] getNameConstraints()
 730:   {
 731:     if (nameConstraints != null)
 732:       return (byte[]) nameConstraints.clone();
 733:     else
 734:       return null;
 735:   }
 736: 
 737:   /**
 738:    * Sets the name constraints criterion; specify <code>null</code> to
 739:    * clear this criterion. Note that if non-null, the argument will be
 740:    * cloned to prevent modification.
 741:    *
 742:    * @param nameConstraints The new name constraints.
 743:    * @throws IOException If the argument is not a valid DER-encoded
 744:    *         name constraints.
 745:    */
 746:   public void setNameConstraints(byte[] nameConstraints)
 747:     throws IOException
 748:   {
 749:     // FIXME check if the argument is valid.
 750:     this.nameConstraints = nameConstraints != null
 751:       ? (byte[]) nameConstraints.clone() : null;
 752:   }
 753: 
 754:   /**
 755:    * Returns the basic constraints criterion, or -1 if this value is not set.
 756:    *
 757:    * @return The basic constraints.
 758:    */
 759:   public int getBasicConstraints()
 760:   {
 761:     return basicConstraints;
 762:   }
 763: 
 764:   /**
 765:    * Sets the basic constraints criterion. Specify -1 to clear this parameter.
 766:    *
 767:    * @param basicConstraints The new basic constraints value.
 768:    */
 769:   public void setBasicConstraints(int basicConstraints)
 770:   {
 771:     if (basicConstraints < -1)
 772:       basicConstraints = -1;
 773:     this.basicConstraints = basicConstraints;
 774:   }
 775: 
 776:   // The last two criteria not yet implemented are certificate policies
 777:   // and path-to-names. Both of these are somewhat advanced extensions
 778:   // (you could probably count the applications that actually use them
 779:   //  on one hand), and they both have no support in the X509Certificate
 780:   // class.
 781:   //
 782:   // Not having support in X509Certificate is not always a problem; for
 783:   // example, we can compare DER-encoded values as byte arrays for some
 784:   // extensions. We can't, however, compare them if they are specified
 785:   // in a set (as policies are). We need to parse the actual value in the
 786:   // certificate, and check it against the specified set.
 787: 
 788:   // FIXME
 789: //   public void setPolicy(Set policy) throws IOException
 790: //   {
 791: //     if (policy != null)
 792: //       {
 793: //         for (Iterator it = policy.iterator(); it.hasNext(); )
 794: //           try
 795: //             {
 796: //               OID oid = new OID((String) it.next());
 797: //               int[] i = oid.getIDs();
 798: //               if (!checkOid(i))
 799: //                 throw new IOException("invalid OID");
 800: //             }
 801: //           catch (Exception x)
 802: //             {
 803: //               throw new IOException("invalid OID");
 804: //             }
 805: //       }
 806: //     this.policy = policy != null ? new HashSet(policy) : null;
 807: //   }
 808: 
 809:   // FIXME
 810: //   public void setPathToNames(Collection names) throws IOException
 811: //   {
 812: //     if (names == null)
 813: //       {
 814: //         this.names = null;
 815: //         return;
 816: //       }
 817: //     for (Iterator it = names.iterator(); it.hasNext(); )
 818: //       {
 819: //         try
 820: //           {
 821: //             List l = (List) it.next();
 822: //             if (l.get(1) instanceof String)
 823: //               addPathToName(((Integer)l.get(0)).intValue(), (String)l.get(1));
 824: //             else
 825: //               addPathToName(((Integer)l.get(0)).intValue(), (byte[])l.get(1));
 826: //           }
 827: //         catch (Exception x)
 828: //           {
 829: //            this.names = null;
 830: //             throw new IOException("invalid names");
 831: //           }
 832: //       }
 833: //   }
 834: 
 835:   // FIXME
 836: //   public void addPathToName(int id, String name) throws IOException
 837: //   {
 838: //   }
 839: 
 840:   // FIXME
 841: //   public void addPathToName(int id, byte[] name) throws IOException
 842: //   {
 843: //   }
 844: 
 845:   // FIXME
 846: //   public Collection getSubjectAlternativeNames()
 847: //   {
 848: //     return null;
 849: //   }
 850: 
 851:   // FIXME
 852: //   public Set getPolicy()
 853: //   {
 854: //     return null;
 855: //   }
 856: 
 857:   // FIXME
 858: //   public Collection getPathToNames()
 859: //   {
 860: //     return null;
 861: //   }
 862: 
 863:   /**
 864:    * Match a certificate. This method will check the given certificate
 865:    * against all the enabled criteria of this selector, and will return
 866:    * <code>true</code> if the given certificate matches.
 867:    *
 868:    * @param certificate The certificate to check.
 869:    * @return true if the certificate matches all criteria.
 870:    */
 871:   public boolean match(Certificate certificate)
 872:   {
 873:     if (!(certificate instanceof X509Certificate))
 874:       return false;
 875:     X509Certificate cert = (X509Certificate) certificate;
 876:     if (this.cert != null)
 877:       {
 878:         try
 879:           {
 880:             byte[] e1 = this.cert.getEncoded();
 881:             byte[] e2 = cert.getEncoded();
 882:             if (!Arrays.equals(e1, e2))
 883:               return false;
 884:           }
 885:         catch (CertificateEncodingException cee)
 886:           {
 887:             return false;
 888:           }
 889:       }
 890:     if (serialNo != null)
 891:       {
 892:         if (!serialNo.equals(cert.getSerialNumber()))
 893:           return false;
 894:       }
 895:     if (certValid != null)
 896:       {
 897:         try
 898:           {
 899:             cert.checkValidity(certValid);
 900:           }
 901:         catch (CertificateException ce)
 902:           {
 903:             return false;
 904:           }
 905:       }
 906:     if (issuer != null)
 907:       {
 908:         if (!issuer.equals(cert.getIssuerX500Principal()))
 909:           return false;
 910:       }
 911:     if (subject != null)
 912:       {
 913:         if (!subject.equals(cert.getSubjectX500Principal()))
 914:           return false;
 915:       }
 916:     if (sigId != null)
 917:       {
 918:         if (!sigId.toString().equals(cert.getSigAlgOID()))
 919:           return false;
 920:       }
 921:     if (subjectKeyId != null)
 922:       {
 923:         byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID);
 924:         if (!Arrays.equals(b, subjectKeyId))
 925:           return false;
 926:       }
 927:     if (authKeyId != null)
 928:       {
 929:         byte[] b = cert.getExtensionValue(AUTH_KEY_ID);
 930:         if (!Arrays.equals(b, authKeyId))
 931:           return false;
 932:       }
 933:     if (keyUsage != null)
 934:       {
 935:         boolean[] b = cert.getKeyUsage();
 936:         if (!Arrays.equals(b, keyUsage))
 937:           return false;
 938:       }
 939:     if (basicConstraints >= 0)
 940:       {
 941:         if (cert.getBasicConstraints() != basicConstraints)
 942:           return false;
 943:       }
 944:     if (keyPurposeSet != null)
 945:       {
 946:         List kp = null;
 947:         try
 948:           {
 949:             kp = cert.getExtendedKeyUsage();
 950:           }
 951:         catch (CertificateParsingException cpe)
 952:           {
 953:             return false;
 954:           }
 955:         if (kp == null)
 956:           return false;
 957:         for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); )
 958:           {
 959:             if (!kp.contains(it.next()))
 960:               return false;
 961:           }
 962:       }
 963:     if (altNames != null)
 964:       {
 965:         Collection an = null;
 966:         try
 967:           {
 968:             an = cert.getSubjectAlternativeNames();
 969:           }
 970:         catch (CertificateParsingException cpe)
 971:           {
 972:             return false;
 973:           }
 974:         if (an == null)
 975:           return false;
 976:         int match = 0;
 977:         for (Iterator it = altNames.iterator(); it.hasNext(); )
 978:           {
 979:             List l = (List) it.next();
 980:             Integer id = (Integer) l.get(0);
 981:             String s = null;
 982:             byte[] b = null;
 983:             if (l.get(1) instanceof String)
 984:               s = (String) l.get(1);
 985:             else if (l.get(1) instanceof byte[])
 986:               b = (byte[]) l.get(1);
 987:             else
 988:               return false;
 989:             for (Iterator it2 = an.iterator(); it2.hasNext(); )
 990:               {
 991:                 Object o = it2.next();
 992:                 if (!(o instanceof List))
 993:                   continue;
 994:                 List l2 = (List) o;
 995:                 if (l2.size() != 2)
 996:                   continue;
 997:                 if (!id.equals(l2.get(0)))
 998:                   continue;
 999:                 if (s != null && (l2.get(1) instanceof String) &&
1000:                     s.equals(l2.get(1)))
1001:                   match++;
1002:                 else if (b != null && (l2.get(1) instanceof byte[]) &&
1003:                          Arrays.equals(b, (byte[]) l2.get(1)))
1004:                   match++;
1005:               }
1006:             if (match == 0 || (matchAllNames && match != altNames.size()))
1007:               return false;
1008:           }
1009:       }
1010:     if (nameConstraints != null)
1011:       {
1012:         byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID);
1013:         if (!Arrays.equals(nameConstraints, nc))
1014:           return false;
1015:       }
1016: 
1017:     // FIXME check policies.
1018:     // FIXME check path-to-names.
1019: 
1020:     return true;
1021:   }
1022: 
1023:   public String toString()
1024:   {
1025:     StringBuffer str = new StringBuffer(X509CertSelector.class.getName());
1026:     String nl = SystemProperties.getProperty("line.separator");
1027:     String eol = ";" + nl;
1028:     str.append(" {").append(nl);
1029:     if (cert != null)
1030:       str.append("  certificate = ").append(cert).append(eol);
1031:     if (basicConstraints >= 0)
1032:       str.append("  basic constraints = ").append(basicConstraints).append(eol);
1033:     if (serialNo != null)
1034:       str.append("  serial number = ").append(serialNo).append(eol);
1035:     if (certValid != null)
1036:       str.append("  valid date = ").append(certValid).append(eol);
1037:     if (issuer != null)
1038:       str.append("  issuer = ").append(issuer).append(eol);
1039:     if (subject != null)
1040:       str.append("  subject = ").append(subject).append(eol);
1041:     if (sigId != null)
1042:       str.append("  signature OID = ").append(sigId).append(eol);
1043:     if (subjectKey != null)
1044:       str.append("  subject public key = ").append(subjectKey).append(eol);
1045:     if (subjectKeyId != null)
1046:       {
1047:         str.append("  subject key ID = ");
1048:         for (int i = 0; i < subjectKeyId.length; i++)
1049:           {
1050:             str.append(Character.forDigit((subjectKeyId[i] & 0xF0) >>> 8, 16));
1051:             str.append(Character.forDigit((subjectKeyId[i] & 0x0F), 16));
1052:             if (i < subjectKeyId.length - 1)
1053:               str.append(':');
1054:           }
1055:         str.append(eol);
1056:       }
1057:     if (authKeyId != null)
1058:       {
1059:         str.append("  authority key ID = ");
1060:         for (int i = 0; i < authKeyId.length; i++)
1061:           {
1062:             str.append(Character.forDigit((authKeyId[i] & 0xF0) >>> 8, 16));
1063:             str.append(Character.forDigit((authKeyId[i] & 0x0F), 16));
1064:             if (i < authKeyId.length - 1)
1065:               str.append(':');
1066:           }
1067:         str.append(eol);
1068:       }
1069:     if (keyUsage != null)
1070:       {
1071:         str.append("  key usage = ");
1072:         for (int i = 0; i < keyUsage.length; i++)
1073:           str.append(keyUsage[i] ? '1' : '0');
1074:         str.append(eol);
1075:       }
1076:     if (keyPurposeSet != null)
1077:       str.append("  key purpose = ").append(keyPurposeSet).append(eol);
1078:     if (altNames != null)
1079:       str.append("  alternative names = ").append(altNames).append(eol);
1080:     if (nameConstraints != null)
1081:       str.append("  name constraints = <blob of data>").append(eol);
1082:     str.append("}").append(nl);
1083:     return str.toString();
1084:   }
1085: 
1086:   public Object clone()
1087:   {
1088:     try
1089:       {
1090:         return super.clone();
1091:       }
1092:     catch (CloneNotSupportedException shouldNotHappen)
1093:       {
1094:         throw new Error(shouldNotHappen);
1095:       }
1096:   }
1097: 
1098:   // Own methods.
1099:   // -------------------------------------------------------------------------
1100: 
1101:   private static boolean checkOid(int[] oid)
1102:   {
1103:     return (oid != null && oid.length > 2 &&
1104:             (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39));
1105:   }
1106: }