1package org.bouncycastle.jce.provider;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5import java.math.BigInteger;
6import java.security.InvalidKeyException;
7import java.security.NoSuchAlgorithmException;
8import java.security.NoSuchProviderException;
9import java.security.Principal;
10import java.security.PublicKey;
11import java.security.Signature;
12import java.security.SignatureException;
13import java.security.cert.CRLException;
14import java.security.cert.Certificate;
15import java.security.cert.X509CRL;
16import java.security.cert.X509CRLEntry;
17import java.security.cert.X509Certificate;
18import java.util.Date;
19import java.util.Enumeration;
20import java.util.HashSet;
21import java.util.Set;
22
23import javax.security.auth.x500.X500Principal;
24
25import org.bouncycastle.asn1.ASN1Encodable;
26import org.bouncycastle.asn1.ASN1OutputStream;
27import org.bouncycastle.asn1.DERObjectIdentifier;
28import org.bouncycastle.asn1.DEROutputStream;
29import org.bouncycastle.asn1.x509.CertificateList;
30import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
31import org.bouncycastle.asn1.x509.TBSCertList;
32import org.bouncycastle.asn1.x509.X509Extension;
33import org.bouncycastle.asn1.x509.X509Extensions;
34import org.bouncycastle.jce.X509Principal;
35import org.bouncycastle.x509.extension.X509ExtensionUtil;
36
37/**
38 * The following extensions are listed in RFC 2459 as relevant to CRLs
39 *
40 * Authority Key Identifier
41 * Issuer Alternative Name
42 * CRL Number
43 * Delta CRL Indicator (critical)
44 * Issuing Distribution Point (critical)
45 */
46public class X509CRLObject
47    extends X509CRL
48{
49    private CertificateList c;
50    private String sigAlgName;
51    private byte[] sigAlgParams;
52
53    public X509CRLObject(
54        CertificateList c)
55        throws CRLException
56    {
57        this.c = c;
58
59        try
60        {
61            this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
62
63            if (c.getSignatureAlgorithm().getParameters() != null)
64            {
65                this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).getDEREncoded();
66            }
67            else
68            {
69                this.sigAlgParams = null;
70            }
71        }
72        catch (Exception e)
73        {
74            throw new CRLException("CRL contents invalid: " + e);
75        }
76    }
77
78    /**
79     * Will return true if any extensions are present and marked
80     * as critical as we currently dont handle any extensions!
81     */
82    public boolean hasUnsupportedCriticalExtension()
83    {
84        Set extns = getCriticalExtensionOIDs();
85        if (extns != null && !extns.isEmpty())
86        {
87            return true;
88        }
89
90        return false;
91    }
92
93    private Set getExtensionOIDs(boolean critical)
94    {
95        if (this.getVersion() == 2)
96        {
97            Set             set = new HashSet();
98            X509Extensions  extensions = c.getTBSCertList().getExtensions();
99            Enumeration     e = extensions.oids();
100
101            while (e.hasMoreElements())
102            {
103                DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
104                X509Extension       ext = extensions.getExtension(oid);
105
106                if (critical == ext.isCritical())
107                {
108                    set.add(oid.getId());
109                }
110            }
111
112            return set;
113        }
114
115        return null;
116    }
117
118    public Set getCriticalExtensionOIDs()
119    {
120        return getExtensionOIDs(true);
121    }
122
123    public Set getNonCriticalExtensionOIDs()
124    {
125        return getExtensionOIDs(false);
126    }
127
128    public byte[] getExtensionValue(String oid)
129    {
130        X509Extensions exts = c.getTBSCertList().getExtensions();
131
132        if (exts != null)
133        {
134            X509Extension   ext = exts.getExtension(new DERObjectIdentifier(oid));
135
136            if (ext != null)
137            {
138                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
139                DEROutputStream dOut = new DEROutputStream(bOut);
140
141                try
142                {
143                    dOut.writeObject(ext.getValue());
144
145                    return bOut.toByteArray();
146                }
147                catch (Exception e)
148                {
149                    throw new RuntimeException("error encoding " + e.toString());
150                }
151            }
152        }
153
154        return null;
155    }
156
157    public byte[] getEncoded()
158        throws CRLException
159    {
160        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
161        DEROutputStream            dOut = new DEROutputStream(bOut);
162
163        try
164        {
165            dOut.writeObject(c);
166
167            return bOut.toByteArray();
168        }
169        catch (IOException e)
170        {
171            throw new CRLException(e.toString());
172        }
173    }
174
175    public void verify(PublicKey key)
176        throws CRLException,  NoSuchAlgorithmException,
177            InvalidKeyException, NoSuchProviderException, SignatureException
178    {
179        verify(key, "BC");
180    }
181
182    public void verify(PublicKey key, String sigProvider)
183        throws CRLException, NoSuchAlgorithmException,
184            InvalidKeyException, NoSuchProviderException, SignatureException
185    {
186        if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature()))
187        {
188            throw new CRLException("Signature algorithm on CertifcateList does not match TBSCertList.");
189        }
190
191        Signature sig = Signature.getInstance(getSigAlgName(), sigProvider);
192
193        sig.initVerify(key);
194        sig.update(this.getTBSCertList());
195        if (!sig.verify(this.getSignature()))
196        {
197            throw new SignatureException("CRL does not verify with supplied public key.");
198        }
199    }
200
201    public int getVersion()
202    {
203        return c.getVersion();
204    }
205
206    public Principal getIssuerDN()
207    {
208        return new X509Principal(c.getIssuer());
209    }
210
211    public X500Principal getIssuerX500Principal()
212    {
213        try
214        {
215            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
216            ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
217
218            aOut.writeObject(c.getIssuer());
219
220            return new X500Principal(bOut.toByteArray());
221        }
222        catch (IOException e)
223        {
224            throw new IllegalStateException("can't encode issuer DN");
225        }
226    }
227
228    public Date getThisUpdate()
229    {
230        return c.getThisUpdate().getDate();
231    }
232
233    public Date getNextUpdate()
234    {
235        if (c.getNextUpdate() != null)
236        {
237            return c.getNextUpdate().getDate();
238        }
239
240        return null;
241    }
242
243    public X509CRLEntry getRevokedCertificate(BigInteger serialNumber)
244    {
245        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
246        boolean isIndirect = isIndirectCRL();
247        if (certs != null)
248        {
249            X500Principal previousCertificateIssuer = getIssuerX500Principal();
250            for (int i = 0; i < certs.length; i++)
251            {
252                X509CRLEntryObject crlentry = new X509CRLEntryObject(certs[i],
253                        isIndirect, previousCertificateIssuer);
254                previousCertificateIssuer = crlentry.getCertificateIssuer();
255                if (crlentry.getSerialNumber().equals(serialNumber))
256                {
257                    return crlentry;
258                }
259            }
260        }
261
262        return null;
263    }
264
265    private boolean isIndirectCRL()
266    {
267        byte[] idp = getExtensionValue(X509Extensions.IssuingDistributionPoint.getId());
268        boolean isIndirect = false;
269        try
270        {
271            if (idp != null)
272            {
273                isIndirect = IssuingDistributionPoint.getInstance(
274                        X509ExtensionUtil.fromExtensionValue(idp))
275                        .isIndirectCRL();
276            }
277        }
278        catch (IOException e)
279        {
280            throw new RuntimeException(
281                    "Exception reading IssuingDistributionPoint" + e);
282        }
283
284        return isIndirect;
285    }
286
287    public Set getRevokedCertificates()
288    {
289        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
290        boolean isIndirect = isIndirectCRL();
291        if (certs != null)
292        {
293            Set set = new HashSet();
294            X500Principal previousCertificateIssuer = getIssuerX500Principal();
295            for (int i = 0; i < certs.length; i++)
296            {
297                X509CRLEntryObject crlentry = new X509CRLEntryObject(certs[i],
298                        isIndirect, previousCertificateIssuer);
299                set.add(crlentry);
300                previousCertificateIssuer = crlentry.getCertificateIssuer();
301            }
302
303            return set;
304        }
305
306        return null;
307    }
308
309    public byte[] getTBSCertList()
310        throws CRLException
311    {
312        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
313        DEROutputStream            dOut = new DEROutputStream(bOut);
314
315        try
316        {
317            dOut.writeObject(c.getTBSCertList());
318
319            return bOut.toByteArray();
320        }
321        catch (IOException e)
322        {
323            throw new CRLException(e.toString());
324        }
325    }
326
327    public byte[] getSignature()
328    {
329        return c.getSignature().getBytes();
330    }
331
332    public String getSigAlgName()
333    {
334        return sigAlgName;
335    }
336
337    public String getSigAlgOID()
338    {
339        return c.getSignatureAlgorithm().getObjectId().getId();
340    }
341
342    public byte[] getSigAlgParams()
343    {
344        if (sigAlgParams != null)
345        {
346            byte[] tmp = new byte[sigAlgParams.length];
347
348            System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length);
349
350            return tmp;
351        }
352
353        return null;
354    }
355
356    /**
357     * Returns a string representation of this CRL.
358     *
359     * @return a string representation of this CRL.
360     */
361    public String toString()
362    {
363        return "X.509 CRL";
364    }
365
366    /**
367     * Checks whether the given certificate is on this CRL.
368     *
369     * @param cert the certificate to check for.
370     * @return true if the given certificate is on this CRL,
371     * false otherwise.
372     */
373    public boolean isRevoked(Certificate cert)
374    {
375        if (!cert.getType().equals("X.509"))
376        {
377            throw new RuntimeException("X.509 CRL used with non X.509 Cert");
378        }
379
380        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
381
382        if (certs != null)
383        {
384            BigInteger serial = ((X509Certificate)cert).getSerialNumber();
385
386            for (int i = 0; i < certs.length; i++)
387            {
388                if (certs[i].getUserCertificate().getValue().equals(serial))
389                {
390                    return true;
391                }
392            }
393        }
394
395        return false;
396    }
397}
398
399