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.security.OID;
043    import gnu.java.security.x509.GnuPKIExtension;
044    import gnu.java.security.x509.ext.CertificatePolicies;
045    import gnu.java.security.x509.ext.Extension;
046    import gnu.java.security.x509.ext.GeneralName;
047    import gnu.java.security.x509.ext.GeneralSubtree;
048    import gnu.java.security.x509.ext.NameConstraints;
049    import gnu.java.security.x509.ext.GeneralName.Kind;
050    
051    import java.io.IOException;
052    import java.math.BigInteger;
053    import java.net.InetAddress;
054    import java.security.KeyFactory;
055    import java.security.PublicKey;
056    import java.security.spec.X509EncodedKeySpec;
057    import java.util.ArrayList;
058    import java.util.Arrays;
059    import java.util.Collection;
060    import java.util.Collections;
061    import java.util.Date;
062    import java.util.HashSet;
063    import java.util.Iterator;
064    import java.util.LinkedList;
065    import java.util.List;
066    import java.util.Set;
067    
068    import javax.security.auth.x500.X500Principal;
069    
070    /**
071     * A concrete implementation of {@link CertSelector} for X.509 certificates,
072     * which allows a number of criteria to be set when accepting certificates,
073     * from validity dates, to issuer and subject distinguished names, to some
074     * of the various X.509 extensions.
075     *
076     * <p>Use of this class requires extensive knowledge of the Internet
077     * Engineering Task Force's Public Key Infrastructure (X.509). The primary
078     * document describing this standard is <a
079     * href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
080     * Public Key Infrastructure Certificate and Certificate Revocation List
081     * (CRL) Profile</a>.
082     *
083     * <p>Note that this class is not thread-safe. If multiple threads will
084     * use or modify this class then they need to synchronize on the object.
085     *
086     * @author Casey Marshall (csm@gnu.org)
087     * @since 1.4
088     */
089    public class X509CertSelector implements CertSelector, Cloneable
090    {
091    
092      // Constants and fields.
093      // -------------------------------------------------------------------------
094    
095      private static final String AUTH_KEY_ID = "2.5.29.35";
096      private static final String SUBJECT_KEY_ID = "2.5.29.14";
097      private static final String NAME_CONSTRAINTS_ID = "2.5.29.30";
098    
099      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    }