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