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