1package org.bouncycastle.jce.provider;
2
3import org.bouncycastle.asn1.ASN1InputStream;
4import org.bouncycastle.asn1.ASN1Sequence;
5import org.bouncycastle.asn1.ASN1Set;
6import org.bouncycastle.asn1.ASN1TaggedObject;
7import org.bouncycastle.asn1.DERObjectIdentifier;
8import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
9import org.bouncycastle.asn1.pkcs.SignedData;
10import org.bouncycastle.asn1.x509.CertificateList;
11import org.bouncycastle.asn1.x509.X509CertificateStructure;
12
13import java.io.IOException;
14import java.io.InputStream;
15import java.io.PushbackInputStream;
16import java.security.cert.CRL;
17import java.security.cert.CRLException;
18import java.security.cert.CertPath;
19import java.security.cert.Certificate;
20import java.security.cert.CertificateException;
21import java.security.cert.CertificateFactorySpi;
22import java.security.cert.CertificateParsingException;
23import java.security.cert.X509Certificate;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Iterator;
27import java.util.List;
28
29/**
30 * class for dealing with X509 certificates.
31 * <p>
32 * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----"
33 * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7
34 * objects.
35 */
36public class JDKX509CertificateFactory
37    extends CertificateFactorySpi
38{
39    private static final PEMUtil PEM_CERT_PARSER = new PEMUtil("CERTIFICATE");
40    private static final PEMUtil PEM_CRL_PARSER = new PEMUtil("CRL");
41
42    private ASN1Set            sData = null;
43    private int                sDataObjectCount = 0;
44    private InputStream        currentStream = null;
45
46    private ASN1Set            sCrlData = null;
47    private int                sCrlDataObjectCount = 0;
48    private InputStream        currentCrlStream = null;
49
50    private Certificate readDERCertificate(
51        ASN1InputStream dIn)
52        throws IOException, CertificateParsingException
53    {
54        ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();
55
56        if (seq.size() > 1
57                && seq.getObjectAt(0) instanceof DERObjectIdentifier)
58        {
59            if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
60            {
61                sData = new SignedData(ASN1Sequence.getInstance(
62                                (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates();
63
64                return getCertificate();
65            }
66        }
67
68        return new X509CertificateObject(
69                            X509CertificateStructure.getInstance(seq));
70    }
71
72    private Certificate getCertificate()
73        throws CertificateParsingException
74    {
75        if (sData != null)
76        {
77            while (sDataObjectCount < sData.size())
78            {
79                Object obj = sData.getObjectAt(sDataObjectCount++);
80
81                if (obj instanceof ASN1Sequence)
82                {
83                   return new X509CertificateObject(
84                                    X509CertificateStructure.getInstance(obj));
85                }
86            }
87        }
88
89        return null;
90    }
91
92    private Certificate readPEMCertificate(
93        InputStream  in)
94        throws IOException, CertificateParsingException
95    {
96        ASN1Sequence seq = PEM_CERT_PARSER.readPEMObject(in);
97
98        if (seq != null)
99        {
100            return new X509CertificateObject(
101                            X509CertificateStructure.getInstance(seq));
102        }
103
104        return null;
105    }
106
107    protected CRL createCRL(CertificateList c)
108    throws CRLException
109    {
110        return new X509CRLObject(c);
111    }
112
113    private CRL readPEMCRL(
114        InputStream  in)
115        throws IOException, CRLException
116    {
117        ASN1Sequence seq = PEM_CRL_PARSER.readPEMObject(in);
118
119        if (seq != null)
120        {
121            return createCRL(
122                            CertificateList.getInstance(seq));
123        }
124
125        return null;
126    }
127
128    private CRL readDERCRL(
129        ASN1InputStream  aIn)
130        throws IOException, CRLException
131    {
132        ASN1Sequence     seq = (ASN1Sequence)aIn.readObject();
133
134        if (seq.size() > 1
135                && seq.getObjectAt(0) instanceof DERObjectIdentifier)
136        {
137            if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
138            {
139                sCrlData = new SignedData(ASN1Sequence.getInstance(
140                                (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs();
141
142                return getCRL();
143            }
144        }
145
146        return createCRL(
147                     CertificateList.getInstance(seq));
148    }
149
150    private CRL getCRL()
151        throws CRLException
152    {
153        if (sCrlData == null || sCrlDataObjectCount >= sCrlData.size())
154        {
155            return null;
156        }
157
158        return createCRL(
159                            CertificateList.getInstance(
160                                    sCrlData.getObjectAt(sCrlDataObjectCount++)));
161    }
162
163    /**
164     * Generates a certificate object and initializes it with the data
165     * read from the input stream inStream.
166     */
167    public Certificate engineGenerateCertificate(
168        InputStream in)
169        throws CertificateException
170    {
171        if (currentStream == null)
172        {
173            currentStream = in;
174            sData = null;
175            sDataObjectCount = 0;
176        }
177        else if (currentStream != in) // reset if input stream has changed
178        {
179            currentStream = in;
180            sData = null;
181            sDataObjectCount = 0;
182        }
183
184        try
185        {
186            if (sData != null)
187            {
188                if (sDataObjectCount != sData.size())
189                {
190                    return getCertificate();
191                }
192                else
193                {
194                    sData = null;
195                    sDataObjectCount = 0;
196                    return null;
197                }
198            }
199
200            int limit = ProviderUtil.getReadLimit(in);
201
202            PushbackInputStream pis = new PushbackInputStream(in);
203            int tag = pis.read();
204
205            if (tag == -1)
206            {
207                return null;
208            }
209
210            pis.unread(tag);
211
212            if (tag != 0x30)  // assume ascii PEM encoded.
213            {
214                return readPEMCertificate(pis);
215            }
216            else
217            {
218                return readDERCertificate(new ASN1InputStream(pis, limit));
219            }
220        }
221        catch (Exception e)
222        {
223            throw new CertificateException(e);
224        }
225    }
226
227    /**
228     * Returns a (possibly empty) collection view of the certificates
229     * read from the given input stream inStream.
230     */
231    public Collection engineGenerateCertificates(
232        InputStream inStream)
233        throws CertificateException
234    {
235        Certificate     cert;
236        List            certs = new ArrayList();
237
238        while ((cert = engineGenerateCertificate(inStream)) != null)
239        {
240            certs.add(cert);
241        }
242
243        return certs;
244    }
245
246    /**
247     * Generates a certificate revocation list (CRL) object and initializes
248     * it with the data read from the input stream inStream.
249     */
250    public CRL engineGenerateCRL(
251        InputStream inStream)
252        throws CRLException
253    {
254        if (currentCrlStream == null)
255        {
256            currentCrlStream = inStream;
257            sCrlData = null;
258            sCrlDataObjectCount = 0;
259        }
260        else if (currentCrlStream != inStream) // reset if input stream has changed
261        {
262            currentCrlStream = inStream;
263            sCrlData = null;
264            sCrlDataObjectCount = 0;
265        }
266
267        try
268        {
269            if (sCrlData != null)
270            {
271                if (sCrlDataObjectCount != sCrlData.size())
272                {
273                    return getCRL();
274                }
275                else
276                {
277                    sCrlData = null;
278                    sCrlDataObjectCount = 0;
279                    return null;
280                }
281            }
282
283            int limit = ProviderUtil.getReadLimit(inStream);
284
285            PushbackInputStream pis = new PushbackInputStream(inStream);
286            int tag = pis.read();
287
288            if (tag == -1)
289            {
290                return null;
291            }
292
293            pis.unread(tag);
294
295            if (tag != 0x30)  // assume ascii PEM encoded.
296            {
297                return readPEMCRL(pis);
298            }
299            else
300            {       // lazy evaluate to help processing of large CRLs
301                return readDERCRL(new ASN1InputStream(pis, limit, true));
302            }
303        }
304        catch (CRLException e)
305        {
306            throw e;
307        }
308        catch (Exception e)
309        {
310            throw new CRLException(e.toString());
311        }
312    }
313
314    /**
315     * Returns a (possibly empty) collection view of the CRLs read from
316     * the given input stream inStream.
317     *
318     * The inStream may contain a sequence of DER-encoded CRLs, or
319     * a PKCS#7 CRL set.  This is a PKCS#7 SignedData object, with the
320     * only signficant field being crls.  In particular the signature
321     * and the contents are ignored.
322     */
323    public Collection engineGenerateCRLs(
324        InputStream inStream)
325        throws CRLException
326    {
327        CRL     crl;
328        List    crls = new ArrayList();
329
330        while ((crl = engineGenerateCRL(inStream)) != null)
331        {
332            crls.add(crl);
333        }
334
335        return crls;
336    }
337
338    public Iterator engineGetCertPathEncodings()
339    {
340        return PKIXCertPath.certPathEncodings.iterator();
341    }
342
343    public CertPath engineGenerateCertPath(
344        InputStream inStream)
345        throws CertificateException
346    {
347        return engineGenerateCertPath(inStream, "PkiPath");
348    }
349
350    public CertPath engineGenerateCertPath(
351        InputStream inStream,
352        String encoding)
353        throws CertificateException
354    {
355        return new PKIXCertPath(inStream, encoding);
356    }
357
358    public CertPath engineGenerateCertPath(
359        List certificates)
360        throws CertificateException
361    {
362        Iterator iter = certificates.iterator();
363        Object obj;
364        while (iter.hasNext())
365        {
366            obj = iter.next();
367            if (obj != null)
368            {
369                if (!(obj instanceof X509Certificate))
370                {
371                    throw new CertificateException("list contains non X509Certificate object while creating CertPath\n" + obj.toString());
372                }
373            }
374        }
375        return new PKIXCertPath(certificates);
376    }
377}
378