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