001    /* X509CertSelector.java -- selects X.509 certificates by criteria.
002       Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.security.cert;
040    
041    import gnu.classpath.SystemProperties;
042    import gnu.java.lang.CPStringBuilder;
043    import gnu.java.security.OID;
044    import gnu.java.security.x509.GnuPKIExtension;
045    import gnu.java.security.x509.ext.CertificatePolicies;
046    import gnu.java.security.x509.ext.Extension;
047    import gnu.java.security.x509.ext.GeneralName;
048    import gnu.java.security.x509.ext.GeneralSubtree;
049    import gnu.java.security.x509.ext.NameConstraints;
050    import gnu.java.security.x509.ext.GeneralName.Kind;
051    
052    import java.io.IOException;
053    import java.math.BigInteger;
054    import java.net.InetAddress;
055    import java.security.KeyFactory;
056    import java.security.PublicKey;
057    import java.security.spec.X509EncodedKeySpec;
058    import java.util.ArrayList;
059    import java.util.Arrays;
060    import java.util.Collection;
061    import java.util.Collections;
062    import java.util.Date;
063    import java.util.HashSet;
064    import java.util.Iterator;
065    import java.util.LinkedList;
066    import java.util.List;
067    import java.util.Set;
068    
069    import 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     */
090    public 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    }