1 /* X509CertSelector.java -- selects X.509 certificates by criteria.
2    Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.security.cert;
40 
41 import gnu.classpath.SystemProperties;
42 import gnu.java.lang.CPStringBuilder;
43 import gnu.java.security.OID;
44 import gnu.java.security.x509.GnuPKIExtension;
45 import gnu.java.security.x509.ext.CertificatePolicies;
46 import gnu.java.security.x509.ext.Extension;
47 import gnu.java.security.x509.ext.GeneralName;
48 import gnu.java.security.x509.ext.GeneralSubtree;
49 import gnu.java.security.x509.ext.NameConstraints;
50 import gnu.java.security.x509.ext.GeneralName.Kind;
51 
52 import java.io.IOException;
53 import java.math.BigInteger;
54 import java.net.InetAddress;
55 import java.security.KeyFactory;
56 import java.security.PublicKey;
57 import java.security.spec.X509EncodedKeySpec;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Collection;
61 import java.util.Collections;
62 import java.util.Date;
63 import java.util.HashSet;
64 import java.util.Iterator;
65 import java.util.LinkedList;
66 import java.util.List;
67 import java.util.Set;
68 
69 import javax.security.auth.x500.X500Principal;
70 
71 /**
72  * A concrete implementation of {@link CertSelector} for X.509 certificates,
73  * which allows a number of criteria to be set when accepting certificates,
74  * from validity dates, to issuer and subject distinguished names, to some
75  * of the various X.509 extensions.
76  *
77  * <p>Use of this class requires extensive knowledge of the Internet
78  * Engineering Task Force's Public Key Infrastructure (X.509). The primary
79  * document describing this standard is <a
80  * href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
81  * Public Key Infrastructure Certificate and Certificate Revocation List
82  * (CRL) Profile</a>.
83  *
84  * <p>Note that this class is not thread-safe. If multiple threads will
85  * use or modify this class then they need to synchronize on the object.
86  *
87  * @author Casey Marshall (csm@gnu.org)
88  * @since 1.4
89  */
90 public class X509CertSelector implements CertSelector, Cloneable
91 {
92 
93   // Constants and fields.
94   // -------------------------------------------------------------------------
95 
96   private static final String AUTH_KEY_ID = "2.5.29.35";
97   private static final String SUBJECT_KEY_ID = "2.5.29.14";
98   private static final String NAME_CONSTRAINTS_ID = "2.5.29.30";
99 
checkOid(int[] oid)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 
makeName(int id, String name)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    */
X509CertSelector()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    */
addPathToName(int id, byte[] name)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    */
addPathToName(int id, String name)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    */
addSubjectAlternativeName(int id, byte[] name)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    */
addSubjectAlternativeName(int id, String name)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 
clone()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    */
getAuthorityKeyIdentifier()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    */
getBasicConstraints()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    */
getCertificate()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    */
getCertificateValid()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    */
getExtendedKeyUsage()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    */
getIssuerAsBytes()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    */
getIssuerAsString()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    */
getKeyUsage()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    */
getMatchAllSubjectAltNames()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    */
getNameConstraints()400   public byte[] getNameConstraints()
401   {
402     if (nameConstraints != null)
403       return (byte[]) nameConstraints.clone();
404     else
405       return null;
406   }
407 
getPathToNames()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    */
getPolicy()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    */
getPrivateKeyValid()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    */
getSerialNumber()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    */
getSubjectAlternativeNames()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    */
getSubjectAsBytes()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    */
getSubjectAsString()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    */
getSubjectKeyIdentifier()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    */
getSubjectPublicKey()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    */
getSubjectPublicKeyAlgID()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    */
match(Certificate certificate)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    */
setAuthorityKeyIdentifier(byte[] authKeyId)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    */
setBasicConstraints(int basicConstraints)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    */
setCertificate(X509Certificate cert)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    */
setCertificateValid(Date certValid)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    */
setExtendedKeyUsage(Set<String> keyPurposeSet)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    */
setIssuer(byte[] name)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    */
setIssuer(String name)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    */
setKeyUsage(boolean[] keyUsage)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    */
setMatchAllSubjectAltNames(boolean matchAllNames)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    */
setNameConstraints(byte[] nameConstraints)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    */
setPathToNames(Collection<List<?>> names)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    */
setPolicy(Set<String> policy)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    */
setPrivateKeyValid(Date UNUSED)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    */
setSerialNumber(BigInteger serialNo)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    */
setSubject(byte[] name)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    */
setSubject(String name)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    */
setSubjectAlternativeNames(Collection<List<?>> altNames)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    */
setSubjectKeyIdentifier(byte[] subjectKeyId)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    */
setSubjectPublicKey(byte[] key)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    */
setSubjectPublicKey(PublicKey key)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    */
setSubjectPublicKeyAlgID(String sigId)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 
toString()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 }
1320