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