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