1 package org.bouncycastle.x509;
2 
3 import org.bouncycastle.asn1.ASN1InputStream;
4 import org.bouncycastle.asn1.ASN1Object;
5 import org.bouncycastle.asn1.ASN1Primitive;
6 import org.bouncycastle.asn1.DEROctetString;
7 import org.bouncycastle.asn1.x509.GeneralName;
8 import org.bouncycastle.asn1.x509.Target;
9 import org.bouncycastle.asn1.x509.TargetInformation;
10 import org.bouncycastle.asn1.x509.Targets;
11 import org.bouncycastle.asn1.x509.X509Extensions;
12 import org.bouncycastle.util.Selector;
13 
14 import java.io.IOException;
15 import java.math.BigInteger;
16 import java.security.cert.CertificateExpiredException;
17 import java.security.cert.CertificateNotYetValidException;
18 import java.security.cert.X509CertSelector;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Date;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.Set;
25 
26 /**
27  * This class is an <code>Selector</code> like implementation to select
28  * attribute certificates from a given set of criteria.
29  *
30  * @see org.bouncycastle.x509.X509AttributeCertificate
31  * @see org.bouncycastle.x509.X509Store
32  */
33 public class X509AttributeCertStoreSelector
34     implements Selector
35 {
36 
37     // TODO: name constraints???
38 
39     private AttributeCertificateHolder holder;
40 
41     private AttributeCertificateIssuer issuer;
42 
43     private BigInteger serialNumber;
44 
45     private Date attributeCertificateValid;
46 
47     private X509AttributeCertificate attributeCert;
48 
49     private Collection targetNames = new HashSet();
50 
51     private Collection targetGroups = new HashSet();
52 
X509AttributeCertStoreSelector()53     public X509AttributeCertStoreSelector()
54     {
55         super();
56     }
57 
58     /**
59      * Decides if the given attribute certificate should be selected.
60      *
61      * @param obj The attribute certificate which should be checked.
62      * @return <code>true</code> if the attribute certificate can be selected,
63      *         <code>false</code> otherwise.
64      */
match(Object obj)65     public boolean match(Object obj)
66     {
67         if (!(obj instanceof X509AttributeCertificate))
68         {
69             return false;
70         }
71 
72         X509AttributeCertificate attrCert = (X509AttributeCertificate) obj;
73 
74         if (this.attributeCert != null)
75         {
76             if (!this.attributeCert.equals(attrCert))
77             {
78                 return false;
79             }
80         }
81         if (serialNumber != null)
82         {
83             if (!attrCert.getSerialNumber().equals(serialNumber))
84             {
85                 return false;
86             }
87         }
88         if (holder != null)
89         {
90             if (!attrCert.getHolder().equals(holder))
91             {
92                 return false;
93             }
94         }
95         if (issuer != null)
96         {
97             if (!attrCert.getIssuer().equals(issuer))
98             {
99                 return false;
100             }
101         }
102 
103         if (attributeCertificateValid != null)
104         {
105             try
106             {
107                 attrCert.checkValidity(attributeCertificateValid);
108             }
109             catch (CertificateExpiredException e)
110             {
111                 return false;
112             }
113             catch (CertificateNotYetValidException e)
114             {
115                 return false;
116             }
117         }
118         if (!targetNames.isEmpty() || !targetGroups.isEmpty())
119         {
120 
121             byte[] targetInfoExt = attrCert
122                 .getExtensionValue(X509Extensions.TargetInformation.getId());
123             if (targetInfoExt != null)
124             {
125                 TargetInformation targetinfo;
126                 try
127                 {
128                     targetinfo = TargetInformation
129                         .getInstance(new ASN1InputStream(
130                             ((DEROctetString) DEROctetString
131                                 .fromByteArray(targetInfoExt)).getOctets())
132                             .readObject());
133                 }
134                 catch (IOException e)
135                 {
136                     return false;
137                 }
138                 catch (IllegalArgumentException e)
139                 {
140                     return false;
141                 }
142                 Targets[] targetss = targetinfo.getTargetsObjects();
143                 if (!targetNames.isEmpty())
144                 {
145                     boolean found = false;
146 
147                     for (int i=0; i<targetss.length; i++)
148                     {
149                         Targets t = targetss[i];
150                         Target[] targets = t.getTargets();
151                         for (int j=0; j<targets.length; j++)
152                         {
153                             if (targetNames.contains(targets[j]
154                                                        .getTargetName()))
155                             {
156                                 found = true;
157                                 break;
158                             }
159                         }
160                     }
161                     if (!found)
162                     {
163                         return false;
164                     }
165                 }
166                 if (!targetGroups.isEmpty())
167                 {
168                     boolean found = false;
169 
170                     for (int i=0; i<targetss.length; i++)
171                     {
172                         Targets t = targetss[i];
173                         Target[] targets = t.getTargets();
174                         for (int j=0; j<targets.length; j++)
175                         {
176                             if (targetGroups.contains(targets[j]
177                                                         .getTargetGroup()))
178                             {
179                                 found = true;
180                                 break;
181                             }
182                         }
183                     }
184                     if (!found)
185                     {
186                         return false;
187                     }
188                 }
189             }
190         }
191         return true;
192     }
193 
194     /**
195      * Returns a clone of this object.
196      *
197      * @return the clone.
198      */
clone()199     public Object clone()
200     {
201         X509AttributeCertStoreSelector sel = new X509AttributeCertStoreSelector();
202         sel.attributeCert = attributeCert;
203         sel.attributeCertificateValid = getAttributeCertificateValid();
204         sel.holder = holder;
205         sel.issuer = issuer;
206         sel.serialNumber = serialNumber;
207         sel.targetGroups = getTargetGroups();
208         sel.targetNames = getTargetNames();
209         return sel;
210     }
211 
212     /**
213      * Returns the attribute certificate which must be matched.
214      *
215      * @return Returns the attribute certificate.
216      */
getAttributeCert()217     public X509AttributeCertificate getAttributeCert()
218     {
219         return attributeCert;
220     }
221 
222     /**
223      * Set the attribute certificate to be matched. If <code>null</code> is
224      * given any will do.
225      *
226      * @param attributeCert The attribute certificate to set.
227      */
setAttributeCert(X509AttributeCertificate attributeCert)228     public void setAttributeCert(X509AttributeCertificate attributeCert)
229     {
230         this.attributeCert = attributeCert;
231     }
232 
233     /**
234      * Get the criteria for the validity.
235      *
236      * @return Returns the attributeCertificateValid.
237      */
getAttributeCertificateValid()238     public Date getAttributeCertificateValid()
239     {
240         if (attributeCertificateValid != null)
241         {
242             return new Date(attributeCertificateValid.getTime());
243         }
244 
245         return null;
246     }
247 
248     /**
249      * Set the time, when the certificate must be valid. If <code>null</code>
250      * is given any will do.
251      *
252      * @param attributeCertificateValid The attribute certificate validation
253      *            time to set.
254      */
setAttributeCertificateValid(Date attributeCertificateValid)255     public void setAttributeCertificateValid(Date attributeCertificateValid)
256     {
257         if (attributeCertificateValid != null)
258         {
259             this.attributeCertificateValid = new Date(attributeCertificateValid
260                 .getTime());
261         }
262         else
263         {
264             this.attributeCertificateValid = null;
265         }
266     }
267 
268     /**
269      * Gets the holder.
270      *
271      * @return Returns the holder.
272      */
getHolder()273     public AttributeCertificateHolder getHolder()
274     {
275         return holder;
276     }
277 
278     /**
279      * Sets the holder. If <code>null</code> is given any will do.
280      *
281      * @param holder The holder to set.
282      */
setHolder(AttributeCertificateHolder holder)283     public void setHolder(AttributeCertificateHolder holder)
284     {
285         this.holder = holder;
286     }
287 
288     /**
289      * Returns the issuer criterion.
290      *
291      * @return Returns the issuer.
292      */
getIssuer()293     public AttributeCertificateIssuer getIssuer()
294     {
295         return issuer;
296     }
297 
298     /**
299      * Sets the issuer the attribute certificate must have. If <code>null</code>
300      * is given any will do.
301      *
302      * @param issuer The issuer to set.
303      */
setIssuer(AttributeCertificateIssuer issuer)304     public void setIssuer(AttributeCertificateIssuer issuer)
305     {
306         this.issuer = issuer;
307     }
308 
309     /**
310      * Gets the serial number the attribute certificate must have.
311      *
312      * @return Returns the serialNumber.
313      */
getSerialNumber()314     public BigInteger getSerialNumber()
315     {
316         return serialNumber;
317     }
318 
319     /**
320      * Sets the serial number the attribute certificate must have. If
321      * <code>null</code> is given any will do.
322      *
323      * @param serialNumber The serialNumber to set.
324      */
setSerialNumber(BigInteger serialNumber)325     public void setSerialNumber(BigInteger serialNumber)
326     {
327         this.serialNumber = serialNumber;
328     }
329 
330     /**
331      * Adds a target name criterion for the attribute certificate to the target
332      * information extension criteria. The <code>X509AttributeCertificate</code>
333      * must contain at least one of the specified target names.
334      * <p>
335      * Each attribute certificate may contain a target information extension
336      * limiting the servers where this attribute certificate can be used. If
337      * this extension is not present, the attribute certificate is not targeted
338      * and may be accepted by any server.
339      *
340      * @param name The name as a GeneralName (not <code>null</code>)
341      */
addTargetName(GeneralName name)342     public void addTargetName(GeneralName name)
343     {
344         targetNames.add(name);
345     }
346 
347     /**
348      * Adds a target name criterion for the attribute certificate to the target
349      * information extension criteria. The <code>X509AttributeCertificate</code>
350      * must contain at least one of the specified target names.
351      * <p>
352      * Each attribute certificate may contain a target information extension
353      * limiting the servers where this attribute certificate can be used. If
354      * this extension is not present, the attribute certificate is not targeted
355      * and may be accepted by any server.
356      *
357      * @param name a byte array containing the name in ASN.1 DER encoded form of a GeneralName
358      * @throws IOException if a parsing error occurs.
359      */
addTargetName(byte[] name)360     public void addTargetName(byte[] name) throws IOException
361     {
362         addTargetName(GeneralName.getInstance(ASN1Primitive.fromByteArray(name)));
363     }
364 
365     /**
366      * Adds a collection with target names criteria. If <code>null</code> is
367      * given any will do.
368      * <p>
369      * The collection consists of either GeneralName objects or byte[] arrays representing
370      * DER encoded GeneralName structures.
371      *
372      * @param names A collection of target names.
373      * @throws IOException if a parsing error occurs.
374      * @see #addTargetName(byte[])
375      * @see #addTargetName(GeneralName)
376      */
setTargetNames(Collection names)377     public void setTargetNames(Collection names) throws IOException
378     {
379         targetNames = extractGeneralNames(names);
380     }
381 
382     /**
383      * Gets the target names. The collection consists of <code>List</code>s
384      * made up of an <code>Integer</code> in the first entry and a DER encoded
385      * byte array or a <code>String</code> in the second entry.
386      * <p>
387      * The returned collection is immutable.
388      *
389      * @return The collection of target names
390      * @see #setTargetNames(Collection)
391      */
getTargetNames()392     public Collection getTargetNames()
393     {
394         return Collections.unmodifiableCollection(targetNames);
395     }
396 
397     /**
398      * Adds a target group criterion for the attribute certificate to the target
399      * information extension criteria. The <code>X509AttributeCertificate</code>
400      * must contain at least one of the specified target groups.
401      * <p>
402      * Each attribute certificate may contain a target information extension
403      * limiting the servers where this attribute certificate can be used. If
404      * this extension is not present, the attribute certificate is not targeted
405      * and may be accepted by any server.
406      *
407      * @param group The group as GeneralName form (not <code>null</code>)
408      */
addTargetGroup(GeneralName group)409     public void addTargetGroup(GeneralName group)
410     {
411         targetGroups.add(group);
412     }
413 
414     /**
415      * Adds a target group criterion for the attribute certificate to the target
416      * information extension criteria. The <code>X509AttributeCertificate</code>
417      * must contain at least one of the specified target groups.
418      * <p>
419      * Each attribute certificate may contain a target information extension
420      * limiting the servers where this attribute certificate can be used. If
421      * this extension is not present, the attribute certificate is not targeted
422      * and may be accepted by any server.
423      *
424      * @param name a byte array containing the group in ASN.1 DER encoded form of a GeneralName
425      * @throws IOException if a parsing error occurs.
426      */
addTargetGroup(byte[] name)427     public void addTargetGroup(byte[] name) throws IOException
428     {
429         addTargetGroup(GeneralName.getInstance(ASN1Primitive.fromByteArray(name)));
430     }
431 
432     /**
433      * Adds a collection with target groups criteria. If <code>null</code> is
434      * given any will do.
435      * <p>
436      * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code representing DER
437      * encoded GeneralNames.
438      *
439      * @param names A collection of target groups.
440      * @throws IOException if a parsing error occurs.
441      * @see #addTargetGroup(byte[])
442      * @see #addTargetGroup(GeneralName)
443      */
setTargetGroups(Collection names)444     public void setTargetGroups(Collection names) throws IOException
445     {
446         targetGroups = extractGeneralNames(names);
447     }
448 
449 
450 
451     /**
452      * Gets the target groups. The collection consists of <code>List</code>s
453      * made up of an <code>Integer</code> in the first entry and a DER encoded
454      * byte array or a <code>String</code> in the second entry.
455      * <p>
456      * The returned collection is immutable.
457      *
458      * @return The collection of target groups.
459      * @see #setTargetGroups(Collection)
460      */
getTargetGroups()461     public Collection getTargetGroups()
462     {
463         return Collections.unmodifiableCollection(targetGroups);
464     }
465 
extractGeneralNames(Collection names)466     private Set extractGeneralNames(Collection names)
467         throws IOException
468     {
469         if (names == null || names.isEmpty())
470         {
471             return new HashSet();
472         }
473         Set temp = new HashSet();
474         for (Iterator it = names.iterator(); it.hasNext();)
475         {
476             Object o = it.next();
477             if (o instanceof GeneralName)
478             {
479                 temp.add(o);
480             }
481             else
482             {
483                 temp.add(GeneralName.getInstance(ASN1Primitive.fromByteArray((byte[])o)));
484             }
485         }
486         return temp;
487     }
488 }
489