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