Source for java.security.cert.X509CertSelector

   1: /* X509CertSelector.java -- selects X.509 certificates by criteria.
   2:    Copyright (C) 2004, 2005, 2006 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: import gnu.java.security.x509.GnuPKIExtension;
  44: import gnu.java.security.x509.ext.CertificatePolicies;
  45: import gnu.java.security.x509.ext.Extension;
  46: import gnu.java.security.x509.ext.GeneralName;
  47: import gnu.java.security.x509.ext.GeneralSubtree;
  48: import gnu.java.security.x509.ext.NameConstraints;
  49: import gnu.java.security.x509.ext.GeneralName.Kind;
  50: 
  51: import java.io.IOException;
  52: import java.math.BigInteger;
  53: import java.net.InetAddress;
  54: import java.security.KeyFactory;
  55: import java.security.PublicKey;
  56: import java.security.spec.X509EncodedKeySpec;
  57: import java.util.ArrayList;
  58: import java.util.Arrays;
  59: import java.util.Collection;
  60: import java.util.Collections;
  61: import java.util.Date;
  62: import java.util.HashSet;
  63: import java.util.Iterator;
  64: import java.util.LinkedList;
  65: import java.util.List;
  66: import java.util.Set;
  67: 
  68: import javax.security.auth.x500.X500Principal;
  69: 
  70: /**
  71:  * A concrete implementation of {@link CertSelector} for X.509 certificates,
  72:  * which allows a number of criteria to be set when accepting certificates,
  73:  * from validity dates, to issuer and subject distinguished names, to some
  74:  * of the various X.509 extensions.
  75:  *
  76:  * <p>Use of this class requires extensive knowledge of the Internet
  77:  * Engineering Task Force's Public Key Infrastructure (X.509). The primary
  78:  * document describing this standard is <a
  79:  * href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
  80:  * Public Key Infrastructure Certificate and Certificate Revocation List
  81:  * (CRL) Profile</a>.
  82:  *
  83:  * <p>Note that this class is not thread-safe. If multiple threads will
  84:  * use or modify this class then they need to synchronize on the object.
  85:  *
  86:  * @author Casey Marshall (csm@gnu.org)
  87:  * @since 1.4
  88:  */
  89: public class X509CertSelector implements CertSelector, Cloneable
  90: {
  91: 
  92:   // Constants and fields.
  93:   // -------------------------------------------------------------------------
  94: 
  95:   private static final String AUTH_KEY_ID = "2.5.29.35";
  96:   private static final String SUBJECT_KEY_ID = "2.5.29.14";
  97:   private static final String NAME_CONSTRAINTS_ID = "2.5.29.30";
  98: 
  99:   private static boolean checkOid(int[] oid)
 100:   {
 101:     return (oid != null && oid.length > 2 &&
 102:             (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39));
 103:   }
 104:   
 105:   private static GeneralName makeName(int id, String name) throws IOException
 106:   {
 107:     byte[] nameBytes = null;
 108:     GeneralName.Kind kind = GeneralName.Kind.forTag(id);
 109:     switch (Kind.forTag(id))
 110:     {
 111:       case dNSName:
 112:       case rfc822Name:
 113:       case uniformResourceIdentifier:
 114:         nameBytes = name.getBytes("ASCII");
 115:         break;
 116:         
 117:       case iPAddress:
 118:         InetAddress addr = InetAddress.getByName(name);
 119:         nameBytes = addr.getAddress();
 120:         break;
 121:         
 122:       case registeredId:
 123:         OID oid = new OID(name);
 124:         nameBytes = oid.getDER();
 125:         break;
 126:         
 127:       case directoryName:
 128:         X500Principal xname = new X500Principal(name);
 129:         nameBytes = xname.getEncoded();
 130:         break;
 131:         
 132:       case ediPartyName:
 133:       case x400Address:
 134:       case otherName:
 135:         throw new IOException("cannot decode string representation of "
 136:                               + kind);
 137:     }
 138:     return new GeneralName(kind, nameBytes);
 139:   }
 140:   
 141:   private int basicConstraints;
 142:   private X509Certificate cert;
 143:   private BigInteger serialNo;
 144:   private X500Principal issuer;
 145:   private X500Principal subject;
 146:   private byte[] subjectKeyId;
 147:   private byte[] authKeyId;
 148:   private boolean[] keyUsage;
 149:   private Date certValid;
 150:   private OID sigId;
 151:   private PublicKey subjectKey;
 152:   private X509EncodedKeySpec subjectKeySpec;
 153:   private Set<String> keyPurposeSet;
 154:   private List<GeneralName> altNames;
 155:   private boolean matchAllNames;
 156:   private byte[] nameConstraints;
 157:   private Set<OID> policy;
 158:   private List<GeneralName> pathToNames;
 159: 
 160:   /**
 161:    * Creates a new X.509 certificate selector. The new selector will be
 162:    * empty, and will accept any certificate (provided that it is an
 163:    * {@link X509Certificate}).
 164:    */
 165:   public X509CertSelector()
 166:   {
 167:     basicConstraints = -1;
 168:   }
 169: 
 170:   /**
 171:    * Add a name to match in the NameConstraints extension. The argument is
 172:    * the DER-encoded bytes of a GeneralName structure.
 173:    * 
 174:    * See the method {@link #addSubjectAlternativeName(int, byte[])} for the
 175:    * format of the GeneralName structure.
 176:    *
 177:    * @param id The name identifier. Must be between 0 and 8.
 178:    * @param name The DER-encoded bytes of the name to match.
 179:    * @throws IOException If the name DER is malformed.
 180:    */
 181:   public void addPathToName(int id, byte[] name) throws IOException
 182:   {
 183:     GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name);
 184:     if (pathToNames == null)
 185:       pathToNames = new LinkedList<GeneralName>();
 186:     pathToNames.add(generalName);
 187:   }
 188: 
 189:   /**
 190:    * Add a name to match in the NameConstraints extension. This method will
 191:    * only recognize certain types of name that have convenient string
 192:    * encodings. For robustness, you should use the {@link
 193:    *  #addPathToName(int, byte[])} method whenever possible.
 194:    *
 195:    * @param id The name identifier. Must be between 0 and 8.
 196:    * @param name The name.
 197:    * @throws IOException If the name cannot be decoded.
 198:    */
 199:   public void addPathToName(int id, String name) throws IOException
 200:   {
 201:     GeneralName generalName = makeName(id, name);
 202:     if (pathToNames == null)
 203:       pathToNames = new LinkedList<GeneralName>();
 204:     pathToNames.add(generalName);
 205:   }
 206: 
 207:   /**
 208:    * Add a name, as DER-encoded bytes, to the subject alternative names
 209:    * criterion.
 210:    * 
 211:    * The name is a GeneralName structure, which has the ASN.1 format:
 212:    * 
 213:    * <pre>
 214:   GeneralName ::= CHOICE {
 215:     otherName                       [0]     OtherName,
 216:     rfc822Name                      [1]     IA5String,
 217:     dNSName                         [2]     IA5String,
 218:     x400Address                     [3]     ORAddress,
 219:     directoryName                   [4]     Name,
 220:     ediPartyName                    [5]     EDIPartyName,
 221:     uniformResourceIdentifier       [6]     IA5String,
 222:     iPAddress                       [7]     OCTET STRING,
 223:     registeredID                    [8]     OBJECT IDENTIFIER }
 224: </pre>
 225:    *
 226:    * @param id The type of name this is.
 227:    * @param name The DER-encoded name.
 228:    * @throws IOException If the name is not a valid DER sequence.
 229:    */
 230:   public void addSubjectAlternativeName(int id, byte[] name)
 231:     throws IOException
 232:   {
 233:     GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name);
 234:     if (altNames == null)
 235:       altNames = new LinkedList<GeneralName>();
 236:     altNames.add(generalName);
 237:   }
 238: 
 239:   /**
 240:    * Add a name to the subject alternative names criterion. This method will
 241:    * only recognize certain types of name that have convenient string
 242:    * encodings. For robustness, you should use the {@link
 243:    *  #addSubjectAlternativeName(int, byte[])} method whenever possible.
 244:    * 
 245:    * This method can only decode certain name kinds of names as strings.
 246:    *
 247:    * @param id The type of name this is. Must be in the range [0,8].
 248:    * @param name The name.
 249:    * @throws IOException If the id is out of range, or if the name
 250:    *   is null.
 251:    */
 252:   public void addSubjectAlternativeName(int id, String name)
 253:     throws IOException
 254:   {
 255:     GeneralName generalName = makeName(id, name);
 256:     if (altNames == null)
 257:       altNames = new LinkedList<GeneralName>();
 258:     altNames.add(generalName);
 259:   }
 260: 
 261:   public Object clone()
 262:   {
 263:     try
 264:       {
 265:         return super.clone();
 266:       }
 267:     catch (CloneNotSupportedException shouldNotHappen)
 268:       {
 269:         throw new Error(shouldNotHappen);
 270:       }
 271:   }
 272: 
 273:   /**
 274:    * Returns the authority key identifier criterion, or <code>null</code> if
 275:    * this value was not set. Note that the byte array is cloned to prevent
 276:    * modification.
 277:    *
 278:    * @return The authority key identifier.
 279:    */
 280:   public byte[] getAuthorityKeyIdentifier()
 281:   {
 282:     if (authKeyId != null)
 283:       return (byte[]) authKeyId.clone();
 284:     else
 285:       return null;
 286:   }
 287: 
 288:   /**
 289:    * Returns the basic constraints criterion, or -1 if this value is not set.
 290:    *
 291:    * @return The basic constraints.
 292:    */
 293:   public int getBasicConstraints()
 294:   {
 295:     return basicConstraints;
 296:   }
 297: 
 298:   /**
 299:    * Returns the certificate criterion, or <code>null</code> if this value
 300:    * was not set.
 301:    *
 302:    * @return The certificate.
 303:    */
 304:   public X509Certificate getCertificate()
 305:   {
 306:     return cert;
 307:   }
 308: 
 309:   /**
 310:    * Returns the date at which certificates must be valid, or <code>null</code>
 311:    * if this criterion was not set.
 312:    *
 313:    * @return The target certificate valitity date.
 314:    */
 315:   public Date getCertificateValid()
 316:   {
 317:     if (certValid != null)
 318:       return (Date) certValid.clone();
 319:     else
 320:       return null;
 321:   }
 322: 
 323:   /**
 324:    * Returns the set of extended key purpose IDs, as an unmodifiable set
 325:    * of OID strings. Returns <code>null</code> if this criterion is not
 326:    * set.
 327:    *
 328:    * @return The set of key purpose OIDs (strings).
 329:    */
 330:   public Set<String> getExtendedKeyUsage()
 331:   {
 332:     if (keyPurposeSet != null)
 333:       return Collections.unmodifiableSet(keyPurposeSet);
 334:     else
 335:       return null;
 336:   }
 337: 
 338:   /**
 339:    * Returns the issuer criterion as a sequence of DER bytes, or
 340:    * <code>null</code> if this value was not set.
 341:    *
 342:    * @return The issuer.
 343:    */
 344:   public byte[] getIssuerAsBytes() throws IOException
 345:   {
 346:     if (issuer != null)
 347:       return issuer.getEncoded();
 348:     else
 349:       return null;
 350:   }
 351: 
 352:   /**
 353:    * Returns the issuer criterion as a string, or <code>null</code> if this
 354:    * value was not set.
 355:    *
 356:    * @return The issuer.
 357:    */
 358:   public String getIssuerAsString()
 359:   {
 360:     if (issuer != null)
 361:       return issuer.getName();
 362:     else
 363:       return null;
 364:   }
 365: 
 366:   /**
 367:    * Returns the public key usage criterion, or <code>null</code> if this
 368:    * value is not set. Note that the array is cloned to prevent modification.
 369:    *
 370:    * @return The public key usage.
 371:    */
 372:   public boolean[] getKeyUsage()
 373:   {
 374:     if (keyUsage != null)
 375:       return (boolean[]) keyUsage.clone();
 376:     else
 377:       return null;
 378:   }
 379: 
 380:   /**
 381:    * Returns whether or not all specified alternative names must match.
 382:    * If false, a certificate is considered a match if <em>one</em> of the
 383:    * specified alternative names matches.
 384:    *
 385:    * @return true if all names must match.
 386:    */
 387:   public boolean getMatchAllSubjectAltNames()
 388:   {
 389:     return matchAllNames;
 390:   }
 391: 
 392:   /**
 393:    * Returns the name constraints criterion, or <code>null</code> if this
 394:    * value is not set. Note that the byte array is cloned to prevent
 395:    * modification.
 396:    *
 397:    * @return The name constraints.
 398:    */
 399:   public byte[] getNameConstraints()
 400:   {
 401:     if (nameConstraints != null)
 402:       return (byte[]) nameConstraints.clone();
 403:     else
 404:       return null;
 405:   }
 406: 
 407:   public Collection<List<?>> getPathToNames()
 408:   {
 409:     if (pathToNames != null)
 410:       {
 411:         List<List<?>> names = new ArrayList<List<?>>(pathToNames.size());
 412:         for (GeneralName name : pathToNames)
 413:           {
 414:             List<Object> n = new ArrayList<Object>(2);
 415:             n.add(name.kind().tag());
 416:             n.add(name.name());
 417:             names.add(n);
 418:           }
 419:         
 420:         return names;
 421:       }
 422:     return null;
 423:   }
 424: 
 425:   /**
 426:    * Returns the certificate policy extension that will be matched by this
 427:    * selector, or null if the certificate policy will not be matched.
 428:    *
 429:    * @return The policy to be matched, or null.
 430:    */
 431:   public Set<String> getPolicy()
 432:   {
 433:     Set<OID> p = this.policy;
 434:     if (p != null)
 435:       {
 436:         Set<String> strings = new HashSet<String>(p.size());
 437:         for (OID o : p)
 438:           {
 439:             strings.add(o.toString());
 440:           }
 441:         return strings;
 442:       }
 443:     return null;
 444:   }
 445: 
 446:   /**
 447:    * This method, and its related X.509 certificate extension &mdash; the
 448:    * private key usage period &mdash; is not supported under the Internet
 449:    * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this
 450:    * method is not supported either.
 451:    *
 452:    * <p>Do not use this method. It is not deprecated, as it is not deprecated
 453:    * in the Java standard, but it is basically a no-operation and simply
 454:    * returns <code>null</code>.
 455:    *
 456:    * @return Null.
 457:    */
 458:   public Date getPrivateKeyValid()
 459:   {
 460:     return null;
 461:   }
 462: 
 463:   /**
 464:    * Returns the serial number criterion, or <code>null</code> if this
 465:    * value was not set.
 466:    *
 467:    * @return The serial number.
 468:    */
 469:   public BigInteger getSerialNumber()
 470:   {
 471:     return serialNo;
 472:   }
 473: 
 474:   /**
 475:    * Get the subject alternative names criterion. The collection returned
 476:    * is a collection of pairs: the first element is an {@link Integer}
 477:    * containing the name type, and the second is a byte array containing
 478:    * the DER-encoded name bytes.
 479:    *
 480:    * @return The subject alternative names criterion. Returns null if this
 481:    *  criterion is not set.
 482:    */
 483:   public Collection<List<?>> getSubjectAlternativeNames()
 484:   {
 485:     if (altNames != null)
 486:       {
 487:         List<List<?>> names = new ArrayList<List<?>>(altNames.size());
 488:         for (GeneralName name : altNames)
 489:           {
 490:             List<Object> n = new ArrayList<Object>(2);
 491:             n.add(name.kind().tag());
 492:             n.add(name.name());
 493:             names.add(n);
 494:           }
 495:         return names;
 496:       }
 497:     return null;
 498:   }
 499: 
 500:   /**
 501:    * Returns the subject criterion as a sequence of DER bytes, or
 502:    * <code>null</code> if this value is not set.
 503:    *
 504:    * @return The subject.
 505:    */
 506:   public byte[] getSubjectAsBytes() throws IOException
 507:   {
 508:     if (subject != null)
 509:       return subject.getEncoded();
 510:     else
 511:       return null;
 512:   }
 513: 
 514:   /**
 515:    * Returns the subject criterion as a string, of <code>null</code> if
 516:    * this value was not set.
 517:    *
 518:    * @return The subject.
 519:    */
 520:   public String getSubjectAsString()
 521:   {
 522:     if (subject != null)
 523:       return subject.getName();
 524:     else
 525:       return null;
 526:   }
 527: 
 528:   /**
 529:    * Returns the subject key identifier criterion, or <code>null</code> if
 530:    * this value was not set. Note that the byte array is cloned to prevent
 531:    * modification.
 532:    *
 533:    * @return The subject key identifier.
 534:    */
 535:   public byte[] getSubjectKeyIdentifier()
 536:   {
 537:     if (subjectKeyId != null)
 538:       return (byte[]) subjectKeyId.clone();
 539:     else
 540:       return null;
 541:   }
 542: 
 543:   /**
 544:    * Returns the subject public key criterion, or <code>null</code> if this
 545:    * value is not set.
 546:    *
 547:    * @return The subject public key.
 548:    */
 549:   public PublicKey getSubjectPublicKey()
 550:   {
 551:     return subjectKey;
 552:   }
 553: 
 554:   /**
 555:    * Returns the public key algorithm ID that matching certificates must have,
 556:    * or <code>null</code> if this criterion was not set.
 557:    *
 558:    * @return The public key algorithm ID.
 559:    */
 560:   public String getSubjectPublicKeyAlgID()
 561:   {
 562:     return String.valueOf(sigId);
 563:   }
 564: 
 565:   /**
 566:    * Match a certificate. This method will check the given certificate
 567:    * against all the enabled criteria of this selector, and will return
 568:    * <code>true</code> if the given certificate matches.
 569:    *
 570:    * @param certificate The certificate to check.
 571:    * @return true if the certificate matches all criteria.
 572:    */
 573:   public boolean match(Certificate certificate)
 574:   {
 575:     if (!(certificate instanceof X509Certificate))
 576:       return false;
 577:     X509Certificate cert = (X509Certificate) certificate;
 578:     if (this.cert != null)
 579:       {
 580:         try
 581:           {
 582:             byte[] e1 = this.cert.getEncoded();
 583:             byte[] e2 = cert.getEncoded();
 584:             if (!Arrays.equals(e1, e2))
 585:               return false;
 586:           }
 587:         catch (CertificateEncodingException cee)
 588:           {
 589:             return false;
 590:           }
 591:       }
 592:     if (serialNo != null)
 593:       {
 594:         if (!serialNo.equals(cert.getSerialNumber()))
 595:           return false;
 596:       }
 597:     if (certValid != null)
 598:       {
 599:         try
 600:           {
 601:             cert.checkValidity(certValid);
 602:           }
 603:         catch (CertificateException ce)
 604:           {
 605:             return false;
 606:           }
 607:       }
 608:     if (issuer != null)
 609:       {
 610:         if (!issuer.equals(cert.getIssuerX500Principal()))
 611:           return false;
 612:       }
 613:     if (subject != null)
 614:       {
 615:         if (!subject.equals(cert.getSubjectX500Principal()))
 616:           return false;
 617:       }
 618:     if (sigId != null)
 619:       {
 620:         if (!sigId.toString().equals(cert.getSigAlgOID()))
 621:           return false;
 622:       }
 623:     if (subjectKeyId != null)
 624:       {
 625:         byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID);
 626:         if (!Arrays.equals(b, subjectKeyId))
 627:           return false;
 628:       }
 629:     if (authKeyId != null)
 630:       {
 631:         byte[] b = cert.getExtensionValue(AUTH_KEY_ID);
 632:         if (!Arrays.equals(b, authKeyId))
 633:           return false;
 634:       }
 635:     if (keyUsage != null)
 636:       {
 637:         boolean[] b = cert.getKeyUsage();
 638:         if (!Arrays.equals(b, keyUsage))
 639:           return false;
 640:       }
 641:     if (basicConstraints >= 0)
 642:       {
 643:         if (cert.getBasicConstraints() != basicConstraints)
 644:           return false;
 645:       }
 646:     if (keyPurposeSet != null)
 647:       {
 648:         List kp = null;
 649:         try
 650:           {
 651:             kp = cert.getExtendedKeyUsage();
 652:           }
 653:         catch (CertificateParsingException cpe)
 654:           {
 655:             return false;
 656:           }
 657:         if (kp == null)
 658:           return false;
 659:         for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); )
 660:           {
 661:             if (!kp.contains(it.next()))
 662:               return false;
 663:           }
 664:       }
 665:     if (altNames != null)
 666:       {
 667:         Collection<List<?>> an = null;
 668:         try
 669:           {
 670:             an = cert.getSubjectAlternativeNames();
 671:           }
 672:         catch (CertificateParsingException cpe)
 673:           {
 674:             return false;
 675:           }
 676:         if (an == null)
 677:           return false;
 678:         int match = 0;
 679:         for (GeneralName name : altNames)
 680:           {
 681:             for (List<?> list : an)
 682:               {
 683:                 try
 684:                   {
 685:                     Integer id = (Integer) list.get(0);
 686:                     Object val = list.get(1);
 687:                     GeneralName n = null;
 688:                     if (val instanceof String)
 689:                       n = makeName(id, (String) val);
 690:                     else if (val instanceof byte[])
 691:                       {
 692:                         n = new GeneralName(GeneralName.Kind.forTag(id),
 693:                                             (byte[]) val);
 694:                       }
 695:                     else
 696:                       continue;
 697:                     if (name.equals(n))
 698:                       match++;
 699:                   }
 700:                 catch (Exception e)
 701:                   {
 702:                     continue;
 703:                   }
 704:               }
 705:             if (match == 0 || (matchAllNames && match < altNames.size()))
 706:               return false;
 707:           }
 708:       }
 709:     if (nameConstraints != null)
 710:       {
 711:         byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID);
 712:         if (!Arrays.equals(nameConstraints, nc))
 713:           return false;
 714:       }
 715: 
 716:     if (policy != null)
 717:       {
 718:         CertificatePolicies policies = null;
 719:         if (cert instanceof GnuPKIExtension)
 720:           {
 721:             policies = (CertificatePolicies)
 722:               ((GnuPKIExtension) cert).getExtension(CertificatePolicies.ID).getValue();
 723:           }
 724:         else
 725:           {
 726:             byte[] policiesDer =
 727:               cert.getExtensionValue(CertificatePolicies.ID.toString());
 728:             try
 729:               {
 730:                 policies = new CertificatePolicies(policiesDer);
 731:               }
 732:             catch (IOException ioe)
 733:               {
 734:                 // ignored
 735:               }
 736:           }
 737:         
 738:         if (policies == null)
 739:           return false;
 740:         if (!policies.getPolicies().containsAll(policy))
 741:           return false;
 742:       }
 743: 
 744:     if (pathToNames != null)
 745:       {
 746:         NameConstraints nc = null;
 747:         if (cert instanceof GnuPKIExtension)
 748:           {
 749:             Extension e =
 750:               ((GnuPKIExtension) cert).getExtension(NameConstraints.ID);
 751:             if (e != null)
 752:               nc = (NameConstraints) e.getValue();
 753:           }
 754:         else
 755:           {
 756:             byte[] b = cert.getExtensionValue(NameConstraints.ID.toString());
 757:             if (b != null)
 758:               {
 759:                 try
 760:                   {
 761:                     nc = new NameConstraints(b);
 762:                   }
 763:                 catch (IOException ioe)
 764:                   {
 765:                   }
 766:               }
 767:           }
 768:         
 769:         if (nc == null)
 770:           return false;
 771: 
 772:         int match = 0;
 773:         for (GeneralName name : pathToNames)
 774:           {
 775:             for (GeneralSubtree subtree : nc.permittedSubtrees())
 776:               {
 777:                 if (name.equals(subtree.base()))
 778:                   match++;
 779:               }
 780:           }
 781:         if (match == 0 || (matchAllNames && match < pathToNames.size()))
 782:           return false;
 783:       }
 784: 
 785:     return true;
 786:   }
 787: 
 788:   /**
 789:    * Sets the authority key identifier criterion, or <code>null</code> to clear
 790:    * this criterion. Note that the byte array is cloned to prevent modification.
 791:    *
 792:    * @param authKeyId The authority key identifier.
 793:    */
 794:   public void setAuthorityKeyIdentifier(byte[] authKeyId)
 795:   {
 796:     this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null;
 797:   }
 798: 
 799:   /**
 800:    * Sets the basic constraints criterion. Specify -1 to clear this parameter.
 801:    *
 802:    * @param basicConstraints The new basic constraints value.
 803:    */
 804:   public void setBasicConstraints(int basicConstraints)
 805:   {
 806:     if (basicConstraints < -1)
 807:       basicConstraints = -1;
 808:     this.basicConstraints = basicConstraints;
 809:   }
 810: 
 811:   /**
 812:    * Sets the certificate criterion. If set, only certificates that are
 813:    * equal to the certificate passed here will be accepted.
 814:    *
 815:    * @param cert The certificate.
 816:    */
 817:   public void setCertificate(X509Certificate cert)
 818:   {
 819:     this.cert = cert;
 820:   }
 821: 
 822:   /**
 823:    * Sets the date at which certificates must be valid. Specify
 824:    * <code>null</code> to clear this criterion.
 825:    *
 826:    * @param certValid The certificate validity date.
 827:    */
 828:   public void setCertificateValid(Date certValid)
 829:   {
 830:     this.certValid = certValid != null ? (Date) certValid.clone() : null;
 831:   }
 832: 
 833:   /**
 834:    * Sets the extended key usage criterion, as a set of OID strings. Specify
 835:    * <code>null</code> to clear this value.
 836:    *
 837:    * @param keyPurposeSet The set of key purpose OIDs.
 838:    * @throws IOException If any element of the set is not a valid OID string.
 839:    */
 840:   public void setExtendedKeyUsage(Set<String> keyPurposeSet) throws IOException
 841:   {
 842:     if (keyPurposeSet == null)
 843:       {
 844:         this.keyPurposeSet = null;
 845:         return;
 846:       }
 847:     Set<String> s = new HashSet<String>();
 848:     for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); )
 849:       {
 850:         Object o = it.next();
 851:         if (!(o instanceof String))
 852:           throw new IOException("not a string: " + o);
 853:         try
 854:           {
 855:             OID oid = new OID((String) o);
 856:             int[] comp = oid.getIDs();
 857:             if (!checkOid(comp))
 858:               throw new IOException("malformed OID: " + o);
 859:           }
 860:         catch (IllegalArgumentException iae)
 861:           {
 862:             IOException ioe = new IOException("malformed OID: " + o);
 863:             ioe.initCause(iae);
 864:             throw ioe;
 865:           }
 866:       }
 867:     this.keyPurposeSet = s;
 868:   }
 869: 
 870:   /**
 871:    * Sets the issuer, specified as the DER encoding of the issuer's
 872:    * distinguished name. Only certificates issued by this issuer will
 873:    * be accepted.
 874:    *
 875:    * @param name The DER encoding of the issuer's distinguished name.
 876:    * @throws IOException If the given name is incorrectly formatted.
 877:    */
 878:   public void setIssuer(byte[] name) throws IOException
 879:   {
 880:     if (name != null)
 881:       {
 882:         try
 883:           {
 884:             issuer = new X500Principal(name);
 885:           }
 886:         catch (IllegalArgumentException iae)
 887:           {
 888:             throw new IOException(iae.getMessage());
 889:           }
 890:       }
 891:     else
 892:       issuer = null;
 893:   }
 894: 
 895:   /**
 896:    * Sets the issuer, specified as a string representation of the issuer's
 897:    * distinguished name. Only certificates issued by this issuer will
 898:    * be accepted.
 899:    *
 900:    * @param name The string representation of the issuer's distinguished name.
 901:    * @throws IOException If the given name is incorrectly formatted.
 902:    */
 903:   public void setIssuer(String name) throws IOException
 904:   {
 905:     if (name != null)
 906:       {
 907:         try
 908:           {
 909:             issuer = new X500Principal(name);
 910:           }
 911:         catch (IllegalArgumentException iae)
 912:           {
 913:             throw new IOException(iae.getMessage());
 914:           }
 915:       }
 916:     else
 917:       issuer = null;
 918:   }
 919: 
 920:   /**
 921:    * Sets the public key usage criterion. Specify <code>null</code> to clear
 922:    * this value.
 923:    *
 924:    * @param keyUsage The public key usage.
 925:    */
 926:   public void setKeyUsage(boolean[] keyUsage)
 927:   {
 928:     this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null;
 929:   }
 930: 
 931:   /**
 932:    * Sets whether or not all subject alternative names must be matched.
 933:    * If false, then a certificate will be considered a match if one
 934:    * alternative name matches.
 935:    *
 936:    * @param matchAllNames Whether or not all alternative names must be
 937:    *        matched.
 938:    */
 939:   public void setMatchAllSubjectAltNames(boolean matchAllNames)
 940:   {
 941:     this.matchAllNames = matchAllNames;
 942:   }
 943: 
 944:   /**
 945:    * Sets the name constraints criterion; specify <code>null</code> to
 946:    * clear this criterion. Note that if non-null, the argument will be
 947:    * cloned to prevent modification.
 948:    *
 949:    * @param nameConstraints The new name constraints.
 950:    * @throws IOException If the argument is not a valid DER-encoded
 951:    *         name constraints.
 952:    */
 953:   public void setNameConstraints(byte[] nameConstraints)
 954:     throws IOException
 955:   {
 956:     // Check if the input is well-formed...
 957:     new NameConstraints(nameConstraints);
 958:     
 959:     // But we just compare raw byte arrays.
 960:     this.nameConstraints = nameConstraints != null
 961:       ? (byte[]) nameConstraints.clone() : null;
 962:   }
 963:   
 964:   /**
 965:    * Sets the pathToNames criterion. The argument is a collection of 
 966:    * pairs, the first element of which is an {@link Integer} giving
 967:    * the ID of the name, and the second element is either a {@link String}
 968:    * or a byte array.
 969:    * 
 970:    * See {@link #addPathToName(int, byte[])} and {@link #addPathToName(int, String)}
 971:    * for how these arguments are handled.
 972:    *
 973:    * @param names The names.
 974:    * @throws IOException If any argument is malformed.
 975:    */
 976:   public void setPathToNames(Collection<List<?>> names) throws IOException
 977:   {
 978:     if (names == null || names.size() == 0)
 979:       {
 980:         pathToNames = null;
 981:       }
 982:     else
 983:       {
 984:         pathToNames = new ArrayList<GeneralName>(names.size());
 985:         for (List<?> name : names)
 986:           {
 987:             Integer id = (Integer) name.get(0);
 988:             Object name2 = name.get(1);
 989:             if (name2 instanceof String)
 990:               addPathToName(id, (String) name2);
 991:             else if (name2 instanceof byte[])
 992:               addPathToName(id, (byte[]) name2);
 993:             else
 994:               throw new IOException("invalid name type: "
 995:                                     + name2.getClass().getName());
 996:           }
 997:       }
 998:   }
 999: 
1000:   /**
1001:    * Sets the certificate policy to match, or null if this criterion should
1002:    * not be checked. Each element if the set must be a dotted-decimal form
1003:    * of certificate policy object identifier.
1004:    *
1005:    * @param policy The policy to match.
1006:    * @throws IOException If some element of the policy is not a valid
1007:    *  policy extenison OID.
1008:    */
1009:   public void setPolicy(Set<String> policy) throws IOException
1010:   {
1011:     if (policy != null)
1012:       {
1013:         HashSet<OID> p = new HashSet<OID>(policy.size());
1014:         for (String s : policy)
1015:           {
1016:             try
1017:               {
1018:                 OID oid = new OID(s);
1019:                 int[] i = oid.getIDs();
1020:                 if (!checkOid(i))
1021:                   throw new IOException("invalid OID");
1022:                 p.add(oid);
1023:               }
1024:             catch (IOException ioe)
1025:               {
1026:                 throw ioe;
1027:               }
1028:             catch (Exception x)
1029:               {
1030:                 IOException ioe = new IOException("invalid OID");
1031:                 ioe.initCause(x);
1032:                 throw ioe;
1033:               }
1034:           }
1035:         this.policy = p;
1036:       }
1037:     else
1038:       this.policy = null;
1039:   }
1040: 
1041:   /**
1042:    * This method, and its related X.509 certificate extension &mdash; the
1043:    * private key usage period &mdash; is not supported under the Internet
1044:    * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this
1045:    * method is not supported either.
1046:    *
1047:    * <p>Do not use this method. It is not deprecated, as it is not deprecated
1048:    * in the Java standard, but it is basically a no-operation.
1049:    *
1050:    * @param UNUSED Is silently ignored.
1051:    */
1052:   public void setPrivateKeyValid(Date UNUSED)
1053:   {
1054:   }
1055: 
1056:   /**
1057:    * Sets the serial number of the desired certificate. Only certificates that
1058:    * contain this serial number are accepted.
1059:    *
1060:    * @param serialNo The serial number.
1061:    */
1062:   public void setSerialNumber(BigInteger serialNo)
1063:   {
1064:     this.serialNo = serialNo;
1065:   }
1066: 
1067:   /**
1068:    * Sets the subject, specified as the DER encoding of the subject's
1069:    * distinguished name. Only certificates with the given subject will
1070:    * be accepted.
1071:    *
1072:    * @param name The DER encoding of the subject's distinguished name.
1073:    * @throws IOException If the given name is incorrectly formatted.
1074:    */
1075:   public void setSubject(byte[] name) throws IOException
1076:   {
1077:     if (name != null)
1078:       {
1079:         try
1080:           {
1081:             subject = new X500Principal(name);
1082:           }
1083:         catch (IllegalArgumentException iae)
1084:           {
1085:             throw new IOException(iae.getMessage());
1086:           }
1087:       }
1088:     else
1089:       subject = null;
1090:   }
1091: 
1092:   /**
1093:    * Sets the subject, specified as a string representation of the
1094:    * subject's distinguished name. Only certificates with the given
1095:    * subject will be accepted.
1096:    *
1097:    * @param name The string representation of the subject's distinguished name.
1098:    * @throws IOException If the given name is incorrectly formatted.
1099:    */
1100:   public void setSubject(String name) throws IOException
1101:   {
1102:     if (name != null)
1103:       {
1104:         try
1105:           {
1106:             subject = new X500Principal(name);
1107:           }
1108:         catch (IllegalArgumentException iae)
1109:           {
1110:             throw new IOException(iae.getMessage());
1111:           }
1112:       }
1113:     else
1114:       subject = null;
1115:   }
1116: 
1117:   /**
1118:    * Sets the subject alternative names critertion. Each element of the
1119:    * argument must be a {@link java.util.List} that contains exactly two
1120:    * elements: the first an {@link Integer}, representing the type of
1121:    * name, and the second either a {@link String} or a byte array,
1122:    * representing the name itself.
1123:    *
1124:    * @param altNames The alternative names.
1125:    * @throws IOException If any element of the argument is invalid.
1126:    */
1127:   public void setSubjectAlternativeNames(Collection<List<?>> altNames)
1128:     throws IOException
1129:   {
1130:     if (altNames == null || altNames.isEmpty())
1131:       {
1132:         this.altNames = null;
1133:         return;
1134:       }
1135:     List<GeneralName> l = new ArrayList<GeneralName>(altNames.size());
1136:     for (List<?> list : altNames)
1137:       {
1138:         Integer id = (Integer) list.get(0);
1139:         Object value = list.get(1);
1140:         GeneralName name = null;
1141:         if (value instanceof String)
1142:           name = makeName(id, (String) value);
1143:         else if (value instanceof byte[])
1144:           name = new GeneralName(GeneralName.Kind.forTag(id), (byte[]) value);
1145:         else
1146:           throw new IOException("invalid name type: " + value.getClass().getName());
1147:         l.add(name);
1148:       }
1149:     this.altNames = l;
1150:   }
1151: 
1152:   /**
1153:    * Sets the subject key identifier criterion, or <code>null</code> to clear
1154:    * this criterion. Note that the byte array is cloned to prevent modification.
1155:    *
1156:    * @param subjectKeyId The subject key identifier.
1157:    */
1158:   public void setSubjectKeyIdentifier(byte[] subjectKeyId)
1159:   {
1160:     this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() :
1161:       null;
1162:   }
1163: 
1164:   /**
1165:    * Sets the subject public key criterion as a DER-encoded key. Specify
1166:    * <code>null</code> to clear this value.
1167:    *
1168:    * @param key The DER-encoded key bytes.
1169:    * @throws IOException If the argument is not a valid DER-encoded key.
1170:    */
1171:   public void setSubjectPublicKey(byte[] key) throws IOException
1172:   {
1173:     if (key == null)
1174:       {
1175:         subjectKey = null;
1176:         subjectKeySpec = null;
1177:         return;
1178:       }
1179:     try
1180:       {
1181:         subjectKeySpec = new X509EncodedKeySpec(key);
1182:         KeyFactory enc = KeyFactory.getInstance("X.509");
1183:         subjectKey = enc.generatePublic(subjectKeySpec);
1184:       }
1185:     catch (Exception x)
1186:       {
1187:         subjectKey = null;
1188:         subjectKeySpec = null;
1189:         IOException ioe = new IOException(x.getMessage());
1190:         ioe.initCause(x);
1191:         throw ioe;
1192:       }
1193:   }
1194: 
1195:   /**
1196:    * Sets the subject public key criterion as an opaque representation.
1197:    * Specify <code>null</code> to clear this criterion.
1198:    *
1199:    * @param key The public key.
1200:    */
1201:   public void setSubjectPublicKey(PublicKey key)
1202:   {
1203:     this.subjectKey = key;
1204:     if (key == null)
1205:       {
1206:         subjectKeySpec = null;
1207:         return;
1208:       }
1209:     try
1210:       {
1211:         KeyFactory enc = KeyFactory.getInstance("X.509");
1212:         subjectKeySpec = (X509EncodedKeySpec)
1213:           enc.getKeySpec(key, X509EncodedKeySpec.class);
1214:       }
1215:     catch (Exception x)
1216:       {
1217:         subjectKey = null;
1218:         subjectKeySpec = null;
1219:       }
1220:   }
1221: 
1222:   /**
1223:    * Sets the public key algorithm ID that matching certificates must have.
1224:    * Specify <code>null</code> to clear this criterion.
1225:    *
1226:    * @param sigId The public key ID.
1227:    * @throws IOException If the specified ID is not a valid object identifier.
1228:    */
1229:   public void setSubjectPublicKeyAlgID(String sigId) throws IOException
1230:   {
1231:     if (sigId != null)
1232:       {
1233:         try
1234:           {
1235:             OID oid = new OID(sigId);
1236:             int[] comp = oid.getIDs();
1237:             if (!checkOid(comp))
1238:               throw new IOException("malformed OID: " + sigId);
1239:             this.sigId = oid;
1240:           }
1241:         catch (IllegalArgumentException iae)
1242:           {
1243:             IOException ioe = new IOException("malformed OID: " + sigId);
1244:             ioe.initCause(iae);
1245:             throw ioe;
1246:           }
1247:       }
1248:     else
1249:       this.sigId = null;
1250:   }
1251:   
1252:   public String toString()
1253:   {
1254:     StringBuffer str = new StringBuffer(X509CertSelector.class.getName());
1255:     String nl = SystemProperties.getProperty("line.separator");
1256:     String eol = ";" + nl;
1257:     str.append(" {").append(nl);
1258:     if (cert != null)
1259:       str.append("  certificate = ").append(cert).append(eol);
1260:     if (basicConstraints >= 0)
1261:       str.append("  basic constraints = ").append(basicConstraints).append(eol);
1262:     if (serialNo != null)
1263:       str.append("  serial number = ").append(serialNo).append(eol);
1264:     if (certValid != null)
1265:       str.append("  valid date = ").append(certValid).append(eol);
1266:     if (issuer != null)
1267:       str.append("  issuer = ").append(issuer).append(eol);
1268:     if (subject != null)
1269:       str.append("  subject = ").append(subject).append(eol);
1270:     if (sigId != null)
1271:       str.append("  signature OID = ").append(sigId).append(eol);
1272:     if (subjectKey != null)
1273:       str.append("  subject public key = ").append(subjectKey).append(eol);
1274:     if (subjectKeyId != null)
1275:       {
1276:         str.append("  subject key ID = ");
1277:         for (int i = 0; i < subjectKeyId.length; i++)
1278:           {
1279:             str.append(Character.forDigit((subjectKeyId[i] & 0xF0) >>> 8, 16));
1280:             str.append(Character.forDigit((subjectKeyId[i] & 0x0F), 16));
1281:             if (i < subjectKeyId.length - 1)
1282:               str.append(':');
1283:           }
1284:         str.append(eol);
1285:       }
1286:     if (authKeyId != null)
1287:       {
1288:         str.append("  authority key ID = ");
1289:         for (int i = 0; i < authKeyId.length; i++)
1290:           {
1291:             str.append(Character.forDigit((authKeyId[i] & 0xF0) >>> 8, 16));
1292:             str.append(Character.forDigit((authKeyId[i] & 0x0F), 16));
1293:             if (i < authKeyId.length - 1)
1294:               str.append(':');
1295:           }
1296:         str.append(eol);
1297:       }
1298:     if (keyUsage != null)
1299:       {
1300:         str.append("  key usage = ");
1301:         for (int i = 0; i < keyUsage.length; i++)
1302:           str.append(keyUsage[i] ? '1' : '0');
1303:         str.append(eol);
1304:       }
1305:     if (keyPurposeSet != null)
1306:       str.append("  key purpose = ").append(keyPurposeSet).append(eol);
1307:     if (altNames != null)
1308:       str.append("  alternative names = ").append(altNames).append(eol);
1309:     if (nameConstraints != null)
1310:       str.append("  name constraints = <blob of data>").append(eol);
1311:     if (policy != null)
1312:       str.append("  policy = ").append(policy).append(eol);
1313:     if (pathToNames != null)
1314:       str.append("  pathToNames = ").append(pathToNames).append(eol);
1315:     str.append("}").append(nl);
1316:     return str.toString();
1317:   }
1318: }