1package org.bouncycastle.cert;
2
3import java.io.IOException;
4import java.io.OutputStream;
5import java.math.BigInteger;
6import java.util.ArrayList;
7import java.util.Date;
8import java.util.List;
9import java.util.Set;
10
11import org.bouncycastle.asn1.ASN1ObjectIdentifier;
12import org.bouncycastle.asn1.ASN1Primitive;
13import org.bouncycastle.asn1.ASN1Sequence;
14import org.bouncycastle.asn1.DEROutputStream;
15import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
16import org.bouncycastle.asn1.x509.AttCertValidityPeriod;
17import org.bouncycastle.asn1.x509.Attribute;
18import org.bouncycastle.asn1.x509.AttributeCertificate;
19import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
20import org.bouncycastle.asn1.x509.Extension;
21import org.bouncycastle.asn1.x509.Extensions;
22import org.bouncycastle.operator.ContentVerifier;
23import org.bouncycastle.operator.ContentVerifierProvider;
24
25/**
26 * Holding class for an X.509 AttributeCertificate structure.
27 */
28public class X509AttributeCertificateHolder
29{
30    private static Attribute[] EMPTY_ARRAY = new Attribute[0];
31
32    private AttributeCertificate attrCert;
33    private Extensions extensions;
34
35    private static AttributeCertificate parseBytes(byte[] certEncoding)
36        throws IOException
37    {
38        try
39        {
40            return AttributeCertificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
41        }
42        catch (ClassCastException e)
43        {
44            throw new CertIOException("malformed data: " + e.getMessage(), e);
45        }
46        catch (IllegalArgumentException e)
47        {
48            throw new CertIOException("malformed data: " + e.getMessage(), e);
49        }
50    }
51
52    /**
53     * Create a X509AttributeCertificateHolder from the passed in bytes.
54     *
55     * @param certEncoding BER/DER encoding of the certificate.
56     * @throws IOException in the event of corrupted data, or an incorrect structure.
57     */
58    public X509AttributeCertificateHolder(byte[] certEncoding)
59        throws IOException
60    {
61        this(parseBytes(certEncoding));
62    }
63
64    /**
65     * Create a X509AttributeCertificateHolder from the passed in ASN.1 structure.
66     *
67     * @param attrCert an ASN.1 AttributeCertificate structure.
68     */
69    public X509AttributeCertificateHolder(AttributeCertificate attrCert)
70    {
71        this.attrCert = attrCert;
72        this.extensions = attrCert.getAcinfo().getExtensions();
73    }
74
75    /**
76     * Return the ASN.1 encoding of this holder's attribute certificate.
77     *
78     * @return a DER encoded byte array.
79     * @throws IOException if an encoding cannot be generated.
80     */
81    public byte[] getEncoded()
82        throws IOException
83    {
84        return attrCert.getEncoded();
85    }
86
87    public int getVersion()
88    {
89        return attrCert.getAcinfo().getVersion().getValue().intValue() + 1;
90    }
91
92    /**
93     * Return the serial number of this attribute certificate.
94     *
95     * @return the serial number.
96     */
97    public BigInteger getSerialNumber()
98    {
99        return attrCert.getAcinfo().getSerialNumber().getValue();
100    }
101
102    /**
103     * Return the holder details for this attribute certificate.
104     *
105     * @return this attribute certificate's holder structure.
106     */
107    public AttributeCertificateHolder getHolder()
108    {
109        return new AttributeCertificateHolder((ASN1Sequence)attrCert.getAcinfo().getHolder().toASN1Primitive());
110    }
111
112    /**
113     * Return the issuer details for this attribute certificate.
114     *
115     * @return this attribute certificate's issuer structure,
116     */
117    public AttributeCertificateIssuer getIssuer()
118    {
119        return new AttributeCertificateIssuer(attrCert.getAcinfo().getIssuer());
120    }
121
122    /**
123     * Return the date before which this attribute certificate is not valid.
124     *
125     * @return the start date for the attribute certificate's validity period.
126     */
127    public Date getNotBefore()
128    {
129        return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotBeforeTime());
130    }
131
132    /**
133     * Return the date after which this attribute certificate is not valid.
134     *
135     * @return the final date for the attribute certificate's validity period.
136     */
137    public Date getNotAfter()
138    {
139        return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotAfterTime());
140    }
141
142    /**
143     * Return the attributes, if any associated with this request.
144     *
145     * @return an array of Attribute, zero length if none present.
146     */
147    public Attribute[] getAttributes()
148    {
149        ASN1Sequence seq = attrCert.getAcinfo().getAttributes();
150        Attribute[] attrs = new Attribute[seq.size()];
151
152        for (int i = 0; i != seq.size(); i++)
153        {
154            attrs[i] = Attribute.getInstance(seq.getObjectAt(i));
155        }
156
157        return attrs;
158    }
159
160    /**
161     * Return an  array of attributes matching the passed in type OID.
162     *
163     * @param type the type of the attribute being looked for.
164     * @return an array of Attribute of the requested type, zero length if none present.
165     */
166    public Attribute[] getAttributes(ASN1ObjectIdentifier type)
167    {
168        ASN1Sequence    seq = attrCert.getAcinfo().getAttributes();
169        List            list = new ArrayList();
170
171        for (int i = 0; i != seq.size(); i++)
172        {
173            Attribute attr = Attribute.getInstance(seq.getObjectAt(i));
174            if (attr.getAttrType().equals(type))
175            {
176                list.add(attr);
177            }
178        }
179
180        if (list.size() == 0)
181        {
182            return EMPTY_ARRAY;
183        }
184
185        return (Attribute[])list.toArray(new Attribute[list.size()]);
186    }
187
188    /**
189     * Return whether or not the holder's attribute certificate contains extensions.
190     *
191     * @return true if extension are present, false otherwise.
192     */
193    public boolean hasExtensions()
194    {
195        return extensions != null;
196    }
197
198    /**
199     * Look up the extension associated with the passed in OID.
200     *
201     * @param oid the OID of the extension of interest.
202     *
203     * @return the extension if present, null otherwise.
204     */
205    public Extension getExtension(ASN1ObjectIdentifier oid)
206    {
207        if (extensions != null)
208        {
209            return extensions.getExtension(oid);
210        }
211
212        return null;
213    }
214
215    /**
216     * Return the extensions block associated with this certificate if there is one.
217     *
218     * @return the extensions block, null otherwise.
219     */
220    public Extensions getExtensions()
221    {
222        return extensions;
223    }
224
225    /**
226     * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
227     * extensions contained in this holder's attribute certificate.
228     *
229     * @return a list of extension OIDs.
230     */
231    public List getExtensionOIDs()
232    {
233        return CertUtils.getExtensionOIDs(extensions);
234    }
235
236    /**
237     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
238     * critical extensions contained in this holder's attribute certificate.
239     *
240     * @return a set of critical extension OIDs.
241     */
242    public Set getCriticalExtensionOIDs()
243    {
244        return CertUtils.getCriticalExtensionOIDs(extensions);
245    }
246
247    /**
248     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
249     * non-critical extensions contained in this holder's attribute certificate.
250     *
251     * @return a set of non-critical extension OIDs.
252     */
253    public Set getNonCriticalExtensionOIDs()
254    {
255        return CertUtils.getNonCriticalExtensionOIDs(extensions);
256    }
257
258    public boolean[] getIssuerUniqueID()
259    {
260        return CertUtils.bitStringToBoolean(attrCert.getAcinfo().getIssuerUniqueID());
261    }
262
263    /**
264     * Return the details of the signature algorithm used to create this attribute certificate.
265     *
266     * @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
267     */
268    public AlgorithmIdentifier getSignatureAlgorithm()
269    {
270        return attrCert.getSignatureAlgorithm();
271    }
272
273    /**
274     * Return the bytes making up the signature associated with this attribute certificate.
275     *
276     * @return the attribute certificate signature bytes.
277     */
278    public byte[] getSignature()
279    {
280        return attrCert.getSignatureValue().getBytes();
281    }
282
283    /**
284     * Return the underlying ASN.1 structure for the attribute certificate in this holder.
285     *
286     * @return a AttributeCertificate object.
287     */
288    public AttributeCertificate toASN1Structure()
289    {
290        return attrCert;
291    }
292
293    /**
294     * Return whether or not this attribute certificate is valid on a particular date.
295     *
296     * @param date the date of interest.
297     * @return true if the attribute certificate is valid, false otherwise.
298     */
299    public boolean isValidOn(Date date)
300    {
301        AttCertValidityPeriod certValidityPeriod = attrCert.getAcinfo().getAttrCertValidityPeriod();
302
303        return !date.before(CertUtils.recoverDate(certValidityPeriod.getNotBeforeTime())) && !date.after(CertUtils.recoverDate(certValidityPeriod.getNotAfterTime()));
304    }
305
306    /**
307     * Validate the signature on the attribute certificate in this holder.
308     *
309     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
310     * @return true if the signature is valid, false otherwise.
311     * @throws CertException if the signature cannot be processed or is inappropriate.
312     */
313    public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
314        throws CertException
315    {
316        AttributeCertificateInfo acinfo = attrCert.getAcinfo();
317
318        if (!CertUtils.isAlgIdEqual(acinfo.getSignature(), attrCert.getSignatureAlgorithm()))
319        {
320            throw new CertException("signature invalid - algorithm identifier mismatch");
321        }
322
323        ContentVerifier verifier;
324
325        try
326        {
327            verifier = verifierProvider.get((acinfo.getSignature()));
328
329            OutputStream sOut = verifier.getOutputStream();
330            DEROutputStream dOut = new DEROutputStream(sOut);
331
332            dOut.writeObject(acinfo);
333
334            sOut.close();
335        }
336        catch (Exception e)
337        {
338            throw new CertException("unable to process signature: " + e.getMessage(), e);
339        }
340
341        return verifier.verify(attrCert.getSignatureValue().getBytes());
342    }
343
344    public boolean equals(
345        Object o)
346    {
347        if (o == this)
348        {
349            return true;
350        }
351
352        if (!(o instanceof X509AttributeCertificateHolder))
353        {
354            return false;
355        }
356
357        X509AttributeCertificateHolder other = (X509AttributeCertificateHolder)o;
358
359        return this.attrCert.equals(other.attrCert);
360    }
361
362    public int hashCode()
363    {
364        return this.attrCert.hashCode();
365    }
366}
367