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