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