X509CRLObject.java revision 16f9ee464b68937f45d009d9c1b0eb9b544a8dee
1package org.bouncycastle.jce.provider;
2
3import java.io.IOException;
4import java.math.BigInteger;
5import java.security.InvalidKeyException;
6import java.security.NoSuchAlgorithmException;
7import java.security.NoSuchProviderException;
8import java.security.Principal;
9import java.security.PublicKey;
10import java.security.Signature;
11import java.security.SignatureException;
12import java.security.cert.CRLException;
13import java.security.cert.Certificate;
14import java.security.cert.CertificateEncodingException;
15import java.security.cert.X509CRL;
16import java.security.cert.X509CRLEntry;
17import java.security.cert.X509Certificate;
18import java.util.Collections;
19import java.util.Date;
20import java.util.Enumeration;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.Set;
24
25import javax.security.auth.x500.X500Principal;
26
27import org.bouncycastle.asn1.ASN1Encodable;
28import org.bouncycastle.asn1.ASN1Encoding;
29import org.bouncycastle.asn1.ASN1InputStream;
30import org.bouncycastle.asn1.ASN1Integer;
31import org.bouncycastle.asn1.ASN1ObjectIdentifier;
32import org.bouncycastle.asn1.ASN1OctetString;
33import org.bouncycastle.asn1.util.ASN1Dump;
34import org.bouncycastle.asn1.x500.X500Name;
35import org.bouncycastle.asn1.x509.CRLDistPoint;
36import org.bouncycastle.asn1.x509.CRLNumber;
37import org.bouncycastle.asn1.x509.CertificateList;
38import org.bouncycastle.asn1.x509.Extension;
39import org.bouncycastle.asn1.x509.Extensions;
40import org.bouncycastle.asn1.x509.GeneralNames;
41import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
42import org.bouncycastle.asn1.x509.TBSCertList;
43import org.bouncycastle.jce.X509Principal;
44import org.bouncycastle.util.encoders.Hex;
45
46/**
47 * The following extensions are listed in RFC 2459 as relevant to CRLs
48 *
49 * Authority Key Identifier
50 * Issuer Alternative Name
51 * CRL Number
52 * Delta CRL Indicator (critical)
53 * Issuing Distribution Point (critical)
54 */
55public class X509CRLObject
56    extends X509CRL
57{
58    private CertificateList c;
59    private String sigAlgName;
60    private byte[] sigAlgParams;
61    private boolean isIndirect;
62
63    static boolean isIndirectCRL(X509CRL crl)
64        throws CRLException
65    {
66        try
67        {
68            byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId());
69            return idp != null
70                && IssuingDistributionPoint.getInstance(ASN1OctetString.getInstance(idp).getOctets()).isIndirectCRL();
71        }
72        catch (Exception e)
73        {
74            throw new ExtCRLException(
75                    "Exception reading IssuingDistributionPoint", e);
76        }
77    }
78
79    public X509CRLObject(
80        CertificateList c)
81        throws CRLException
82    {
83        this.c = c;
84
85        try
86        {
87            this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
88
89            if (c.getSignatureAlgorithm().getParameters() != null)
90            {
91                this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).toASN1Primitive().getEncoded(ASN1Encoding.DER);
92            }
93            else
94            {
95                this.sigAlgParams = null;
96            }
97
98            this.isIndirect = isIndirectCRL(this);
99        }
100        catch (Exception e)
101        {
102            throw new CRLException("CRL contents invalid: " + e);
103        }
104    }
105
106    /**
107     * Will return true if any extensions are present and marked
108     * as critical as we currently dont handle any extensions!
109     */
110    public boolean hasUnsupportedCriticalExtension()
111    {
112        Set extns = getCriticalExtensionOIDs();
113
114        if (extns == null)
115        {
116            return false;
117        }
118
119        extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
120        extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
121
122        return !extns.isEmpty();
123    }
124
125    private Set getExtensionOIDs(boolean critical)
126    {
127        if (this.getVersion() == 2)
128        {
129            Extensions extensions = c.getTBSCertList().getExtensions();
130
131            if (extensions != null)
132            {
133                Set set = new HashSet();
134                Enumeration e = extensions.oids();
135
136                while (e.hasMoreElements())
137                {
138                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
139                    Extension ext = extensions.getExtension(oid);
140
141                    if (critical == ext.isCritical())
142                    {
143                        set.add(oid.getId());
144                    }
145                }
146
147                return set;
148            }
149        }
150
151        return null;
152    }
153
154    public Set getCriticalExtensionOIDs()
155    {
156        return getExtensionOIDs(true);
157    }
158
159    public Set getNonCriticalExtensionOIDs()
160    {
161        return getExtensionOIDs(false);
162    }
163
164    public byte[] getExtensionValue(String oid)
165    {
166        Extensions exts = c.getTBSCertList().getExtensions();
167
168        if (exts != null)
169        {
170            Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
171
172            if (ext != null)
173            {
174                try
175                {
176                    return ext.getExtnValue().getEncoded();
177                }
178                catch (Exception e)
179                {
180                    throw new IllegalStateException("error parsing " + e.toString());
181                }
182            }
183        }
184
185        return null;
186    }
187
188    public byte[] getEncoded()
189        throws CRLException
190    {
191        try
192        {
193            return c.getEncoded(ASN1Encoding.DER);
194        }
195        catch (IOException e)
196        {
197            throw new CRLException(e.toString());
198        }
199    }
200
201    public void verify(PublicKey key)
202        throws CRLException,  NoSuchAlgorithmException,
203            InvalidKeyException, NoSuchProviderException, SignatureException
204    {
205        verify(key, BouncyCastleProvider.PROVIDER_NAME);
206    }
207
208    public void verify(PublicKey key, String sigProvider)
209        throws CRLException, NoSuchAlgorithmException,
210            InvalidKeyException, NoSuchProviderException, SignatureException
211    {
212        if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature()))
213        {
214            throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
215        }
216
217        Signature sig;
218
219        if (sigProvider != null)
220        {
221            sig = Signature.getInstance(getSigAlgName(), sigProvider);
222        }
223        else
224        {
225            sig = Signature.getInstance(getSigAlgName());
226        }
227
228        sig.initVerify(key);
229        sig.update(this.getTBSCertList());
230
231        if (!sig.verify(this.getSignature()))
232        {
233            throw new SignatureException("CRL does not verify with supplied public key.");
234        }
235    }
236
237    public int getVersion()
238    {
239        return c.getVersionNumber();
240    }
241
242    public Principal getIssuerDN()
243    {
244        return new X509Principal(X500Name.getInstance(c.getIssuer().toASN1Primitive()));
245    }
246
247    public X500Principal getIssuerX500Principal()
248    {
249        try
250        {
251            return new X500Principal(c.getIssuer().getEncoded());
252        }
253        catch (IOException e)
254        {
255            throw new IllegalStateException("can't encode issuer DN");
256        }
257    }
258
259    public Date getThisUpdate()
260    {
261        return c.getThisUpdate().getDate();
262    }
263
264    public Date getNextUpdate()
265    {
266        if (c.getNextUpdate() != null)
267        {
268            return c.getNextUpdate().getDate();
269        }
270
271        return null;
272    }
273
274    private Set loadCRLEntries()
275    {
276        Set entrySet = new HashSet();
277        Enumeration certs = c.getRevokedCertificateEnumeration();
278
279        X500Name previousCertificateIssuer = null; // the issuer
280        while (certs.hasMoreElements())
281        {
282            TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
283            X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
284            entrySet.add(crlEntry);
285            if (isIndirect && entry.hasExtensions())
286            {
287                Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
288
289                if (currentCaName != null)
290                {
291                    previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
292                }
293            }
294        }
295
296        return entrySet;
297    }
298
299    public X509CRLEntry getRevokedCertificate(BigInteger serialNumber)
300    {
301        Enumeration certs = c.getRevokedCertificateEnumeration();
302
303        X500Name previousCertificateIssuer = null; // the issuer
304        while (certs.hasMoreElements())
305        {
306            TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
307
308            if (serialNumber.equals(entry.getUserCertificate().getValue()))
309            {
310                return new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
311            }
312
313            if (isIndirect && entry.hasExtensions())
314            {
315                Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
316
317                if (currentCaName != null)
318                {
319                    previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
320                }
321            }
322        }
323
324        return null;
325    }
326
327    public Set getRevokedCertificates()
328    {
329        Set entrySet = loadCRLEntries();
330
331        if (!entrySet.isEmpty())
332        {
333            return Collections.unmodifiableSet(entrySet);
334        }
335
336        return null;
337    }
338
339    public byte[] getTBSCertList()
340        throws CRLException
341    {
342        try
343        {
344            return c.getTBSCertList().getEncoded("DER");
345        }
346        catch (IOException e)
347        {
348            throw new CRLException(e.toString());
349        }
350    }
351
352    public byte[] getSignature()
353    {
354        return c.getSignature().getBytes();
355    }
356
357    public String getSigAlgName()
358    {
359        return sigAlgName;
360    }
361
362    public String getSigAlgOID()
363    {
364        return c.getSignatureAlgorithm().getAlgorithm().getId();
365    }
366
367    public byte[] getSigAlgParams()
368    {
369        if (sigAlgParams != null)
370        {
371            byte[] tmp = new byte[sigAlgParams.length];
372
373            System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length);
374
375            return tmp;
376        }
377
378        return null;
379    }
380
381    /**
382     * Returns a string representation of this CRL.
383     *
384     * @return a string representation of this CRL.
385     */
386    public String toString()
387    {
388        StringBuffer buf = new StringBuffer();
389        String nl = System.getProperty("line.separator");
390
391        buf.append("              Version: ").append(this.getVersion()).append(
392            nl);
393        buf.append("             IssuerDN: ").append(this.getIssuerDN())
394            .append(nl);
395        buf.append("          This update: ").append(this.getThisUpdate())
396            .append(nl);
397        buf.append("          Next update: ").append(this.getNextUpdate())
398            .append(nl);
399        buf.append("  Signature Algorithm: ").append(this.getSigAlgName())
400            .append(nl);
401
402        byte[] sig = this.getSignature();
403
404        buf.append("            Signature: ").append(
405            new String(Hex.encode(sig, 0, 20))).append(nl);
406        for (int i = 20; i < sig.length; i += 20)
407        {
408            if (i < sig.length - 20)
409            {
410                buf.append("                       ").append(
411                    new String(Hex.encode(sig, i, 20))).append(nl);
412            }
413            else
414            {
415                buf.append("                       ").append(
416                    new String(Hex.encode(sig, i, sig.length - i))).append(nl);
417            }
418        }
419
420        Extensions extensions = c.getTBSCertList().getExtensions();
421
422        if (extensions != null)
423        {
424            Enumeration e = extensions.oids();
425
426            if (e.hasMoreElements())
427            {
428                buf.append("           Extensions: ").append(nl);
429            }
430
431            while (e.hasMoreElements())
432            {
433                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
434                Extension ext = extensions.getExtension(oid);
435
436                if (ext.getExtnValue() != null)
437                {
438                    byte[] octs = ext.getExtnValue().getOctets();
439                    ASN1InputStream dIn = new ASN1InputStream(octs);
440                    buf.append("                       critical(").append(
441                        ext.isCritical()).append(") ");
442                    try
443                    {
444                        if (oid.equals(Extension.cRLNumber))
445                        {
446                            buf.append(
447                                new CRLNumber(ASN1Integer.getInstance(
448                                    dIn.readObject()).getPositiveValue()))
449                                .append(nl);
450                        }
451                        else if (oid.equals(Extension.deltaCRLIndicator))
452                        {
453                            buf.append(
454                                "Base CRL: "
455                                    + new CRLNumber(ASN1Integer.getInstance(
456                                        dIn.readObject()).getPositiveValue()))
457                                .append(nl);
458                        }
459                        else if (oid
460                            .equals(Extension.issuingDistributionPoint))
461                        {
462                            buf.append(
463                               IssuingDistributionPoint.getInstance(dIn.readObject())).append(nl);
464                        }
465                        else if (oid
466                            .equals(Extension.cRLDistributionPoints))
467                        {
468                            buf.append(
469                                CRLDistPoint.getInstance(dIn.readObject())).append(nl);
470                        }
471                        else if (oid.equals(Extension.freshestCRL))
472                        {
473                            buf.append(
474                                CRLDistPoint.getInstance(dIn.readObject())).append(nl);
475                        }
476                        else
477                        {
478                            buf.append(oid.getId());
479                            buf.append(" value = ").append(
480                                ASN1Dump.dumpAsString(dIn.readObject()))
481                                .append(nl);
482                        }
483                    }
484                    catch (Exception ex)
485                    {
486                        buf.append(oid.getId());
487                        buf.append(" value = ").append("*****").append(nl);
488                    }
489                }
490                else
491                {
492                    buf.append(nl);
493                }
494            }
495        }
496        Set set = getRevokedCertificates();
497        if (set != null)
498        {
499            Iterator it = set.iterator();
500            while (it.hasNext())
501            {
502                buf.append(it.next());
503                buf.append(nl);
504            }
505        }
506        return buf.toString();
507    }
508
509    /**
510     * Checks whether the given certificate is on this CRL.
511     *
512     * @param cert the certificate to check for.
513     * @return true if the given certificate is on this CRL,
514     * false otherwise.
515     */
516    public boolean isRevoked(Certificate cert)
517    {
518        if (!cert.getType().equals("X.509"))
519        {
520            throw new RuntimeException("X.509 CRL used with non X.509 Cert");
521        }
522
523        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
524
525        X500Name caName = c.getIssuer();
526
527        if (certs != null)
528        {
529            BigInteger serial = ((X509Certificate)cert).getSerialNumber();
530
531            for (int i = 0; i < certs.length; i++)
532            {
533                if (isIndirect && certs[i].hasExtensions())
534                {
535                    Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer);
536
537                    if (currentCaName != null)
538                    {
539                        caName = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
540                    }
541                }
542
543                if (certs[i].getUserCertificate().getValue().equals(serial))
544                {
545                    X500Name issuer;
546
547                    if (cert instanceof  X509Certificate)
548                    {
549                        issuer = X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded());
550                    }
551                    else
552                    {
553                        try
554                        {
555                            issuer = org.bouncycastle.asn1.x509.Certificate.getInstance(cert.getEncoded()).getIssuer();
556                        }
557                        catch (CertificateEncodingException e)
558                        {
559                            throw new RuntimeException("Cannot process certificate");
560                        }
561                    }
562
563                    if (!caName.equals(issuer))
564                    {
565                        return false;
566                    }
567
568                    return true;
569                }
570            }
571        }
572
573        return false;
574    }
575}
576
577