1package org.bouncycastle.jcajce.provider.asymmetric.x509;
2
3import java.io.IOException;
4import java.math.BigInteger;
5import java.security.cert.CRLException;
6import java.security.cert.X509CRLEntry;
7import java.util.Date;
8import java.util.Enumeration;
9import java.util.HashSet;
10import java.util.Set;
11
12import javax.security.auth.x500.X500Principal;
13
14import org.bouncycastle.asn1.ASN1Encoding;
15import org.bouncycastle.asn1.ASN1Enumerated;
16import org.bouncycastle.asn1.ASN1InputStream;
17import org.bouncycastle.asn1.ASN1ObjectIdentifier;
18import org.bouncycastle.asn1.util.ASN1Dump;
19import org.bouncycastle.asn1.x500.X500Name;
20import org.bouncycastle.asn1.x509.CRLReason;
21import org.bouncycastle.asn1.x509.Extension;
22import org.bouncycastle.asn1.x509.Extensions;
23import org.bouncycastle.asn1.x509.GeneralName;
24import org.bouncycastle.asn1.x509.GeneralNames;
25import org.bouncycastle.asn1.x509.TBSCertList;
26
27/**
28 * The following extensions are listed in RFC 2459 as relevant to CRL Entries
29 *
30 * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer
31 * (critical)
32 */
33class X509CRLEntryObject extends X509CRLEntry
34{
35    private TBSCertList.CRLEntry c;
36
37    private X500Name certificateIssuer;
38    private int           hashValue;
39    private boolean       isHashValueSet;
40
41    protected X509CRLEntryObject(TBSCertList.CRLEntry c)
42    {
43        this.c = c;
44        this.certificateIssuer = null;
45    }
46
47    /**
48     * Constructor for CRLEntries of indirect CRLs. If <code>isIndirect</code>
49     * is <code>false</code> {@link #getCertificateIssuer()} will always
50     * return <code>null</code>, <code>previousCertificateIssuer</code> is
51     * ignored. If this <code>isIndirect</code> is specified and this CRLEntry
52     * has no certificate issuer CRL entry extension
53     * <code>previousCertificateIssuer</code> is returned by
54     * {@link #getCertificateIssuer()}.
55     *
56     * @param c
57     *            TBSCertList.CRLEntry object.
58     * @param isIndirect
59     *            <code>true</code> if the corresponding CRL is a indirect
60     *            CRL.
61     * @param previousCertificateIssuer
62     *            Certificate issuer of the previous CRLEntry.
63     */
64    protected X509CRLEntryObject(
65        TBSCertList.CRLEntry c,
66        boolean isIndirect,
67        X500Name previousCertificateIssuer)
68    {
69        this.c = c;
70        this.certificateIssuer = loadCertificateIssuer(isIndirect, previousCertificateIssuer);
71    }
72
73    /**
74     * Will return true if any extensions are present and marked as critical as
75     * we currently don't handle any extensions!
76     */
77    public boolean hasUnsupportedCriticalExtension()
78    {
79        Set extns = getCriticalExtensionOIDs();
80
81        return extns != null && !extns.isEmpty();
82    }
83
84    private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer)
85    {
86        if (!isIndirect)
87        {
88            return null;
89        }
90
91        Extension ext = getExtension(Extension.certificateIssuer);
92        if (ext == null)
93        {
94            return previousCertificateIssuer;
95        }
96
97        try
98        {
99            GeneralName[] names = GeneralNames.getInstance(ext.getParsedValue()).getNames();
100            for (int i = 0; i < names.length; i++)
101            {
102                if (names[i].getTagNo() == GeneralName.directoryName)
103                {
104                    return X500Name.getInstance(names[i].getName());
105                }
106            }
107            return null;
108        }
109        catch (Exception e)
110        {
111            return null;
112        }
113    }
114
115    public X500Principal getCertificateIssuer()
116    {
117        if (certificateIssuer == null)
118        {
119            return null;
120        }
121        try
122        {
123            return new X500Principal(certificateIssuer.getEncoded());
124        }
125        catch (IOException e)
126        {
127            return null;
128        }
129    }
130
131    private Set getExtensionOIDs(boolean critical)
132    {
133        Extensions extensions = c.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        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    private Extension getExtension(ASN1ObjectIdentifier oid)
168    {
169        Extensions exts = c.getExtensions();
170
171        if (exts != null)
172        {
173            return exts.getExtension(oid);
174        }
175
176        return null;
177    }
178
179    public byte[] getExtensionValue(String oid)
180    {
181        Extension ext = getExtension(new ASN1ObjectIdentifier(oid));
182
183        if (ext != null)
184        {
185            try
186            {
187                return ext.getExtnValue().getEncoded();
188            }
189            catch (Exception e)
190            {
191                throw new RuntimeException("error encoding " + e.toString());
192            }
193        }
194
195        return null;
196    }
197
198    /**
199     * Cache the hashCode value - calculating it with the standard method.
200     * @return  calculated hashCode.
201     */
202    public int hashCode()
203    {
204        if (!isHashValueSet)
205        {
206            hashValue = super.hashCode();
207            isHashValueSet = true;
208        }
209
210        return hashValue;
211    }
212
213    public boolean equals(Object o)
214    {
215        if (o == this)
216        {
217            return true;
218        }
219
220        if (o instanceof X509CRLEntryObject)
221        {
222            X509CRLEntryObject other = (X509CRLEntryObject)o;
223
224            return this.c.equals(other.c);
225        }
226
227        return super.equals(this);
228    }
229
230    public byte[] getEncoded()
231        throws CRLException
232    {
233        try
234        {
235            return c.getEncoded(ASN1Encoding.DER);
236        }
237        catch (IOException e)
238        {
239            throw new CRLException(e.toString());
240        }
241    }
242
243    public BigInteger getSerialNumber()
244    {
245        return c.getUserCertificate().getValue();
246    }
247
248    public Date getRevocationDate()
249    {
250        return c.getRevocationDate().getDate();
251    }
252
253    public boolean hasExtensions()
254    {
255        return c.getExtensions() != null;
256    }
257
258    public String toString()
259    {
260        StringBuffer buf = new StringBuffer();
261        String nl = System.getProperty("line.separator");
262
263        buf.append("      userCertificate: ").append(this.getSerialNumber()).append(nl);
264        buf.append("       revocationDate: ").append(this.getRevocationDate()).append(nl);
265        buf.append("       certificateIssuer: ").append(this.getCertificateIssuer()).append(nl);
266
267        Extensions extensions = c.getExtensions();
268
269        if (extensions != null)
270        {
271            Enumeration e = extensions.oids();
272            if (e.hasMoreElements())
273            {
274                buf.append("   crlEntryExtensions:").append(nl);
275
276                while (e.hasMoreElements())
277                {
278                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
279                    Extension ext = extensions.getExtension(oid);
280                    if (ext.getExtnValue() != null)
281                    {
282                        byte[]                  octs = ext.getExtnValue().getOctets();
283                        ASN1InputStream dIn = new ASN1InputStream(octs);
284                        buf.append("                       critical(").append(ext.isCritical()).append(") ");
285                        try
286                        {
287                            if (oid.equals(Extension.reasonCode))
288                            {
289                                buf.append(CRLReason.getInstance(ASN1Enumerated.getInstance(dIn.readObject()))).append(nl);
290                            }
291                            else if (oid.equals(Extension.certificateIssuer))
292                            {
293                                buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl);
294                            }
295                            else
296                            {
297                                buf.append(oid.getId());
298                                buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
299                            }
300                        }
301                        catch (Exception ex)
302                        {
303                            buf.append(oid.getId());
304                            buf.append(" value = ").append("*****").append(nl);
305                        }
306                    }
307                    else
308                    {
309                        buf.append(nl);
310                    }
311                }
312            }
313        }
314
315        return buf.toString();
316    }
317}
318