• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.bouncycastle.jcajce.provider.asymmetric.x509;
2 
3 import java.io.BufferedInputStream;
4 import java.io.ByteArrayInputStream;
5 import java.io.ByteArrayOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.OutputStreamWriter;
9 import java.security.NoSuchProviderException;
10 import java.security.cert.CertPath;
11 import java.security.cert.Certificate;
12 import java.security.cert.CertificateEncodingException;
13 import java.security.cert.CertificateException;
14 import java.security.cert.CertificateFactory;
15 import java.security.cert.X509Certificate;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Enumeration;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.ListIterator;
22 
23 import javax.security.auth.x500.X500Principal;
24 
25 import org.bouncycastle.asn1.ASN1Encodable;
26 import org.bouncycastle.asn1.ASN1EncodableVector;
27 import org.bouncycastle.asn1.ASN1Encoding;
28 import org.bouncycastle.asn1.ASN1InputStream;
29 import org.bouncycastle.asn1.ASN1Integer;
30 import org.bouncycastle.asn1.ASN1Primitive;
31 import org.bouncycastle.asn1.ASN1Sequence;
32 import org.bouncycastle.asn1.DERSequence;
33 import org.bouncycastle.asn1.DERSet;
34 import org.bouncycastle.asn1.pkcs.ContentInfo;
35 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
36 import org.bouncycastle.asn1.pkcs.SignedData;
37 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
38 import org.bouncycastle.jcajce.util.JcaJceHelper;
39 import org.bouncycastle.util.io.pem.PemObject;
40 // BEGIN android-removed
41 // import org.bouncycastle.util.io.pem.PemWriter;
42 // END android-removed
43 
44 /**
45  * CertPath implementation for X.509 certificates.
46  * <br />
47  **/
48 public  class PKIXCertPath
49     extends CertPath
50 {
51     private final JcaJceHelper helper = new BCJcaJceHelper();
52 
53     static final List certPathEncodings;
54 
55     static
56     {
57         List encodings = new ArrayList();
58         encodings.add("PkiPath");
59         // BEGIN android-removed
60         // encodings.add("PEM");
61         // END android-removed
62         encodings.add("PKCS7");
63         certPathEncodings = Collections.unmodifiableList(encodings);
64     }
65 
66     private List certificates;
67 
68     /**
69      * @param certs
70      */
sortCerts( List certs)71     private List sortCerts(
72         List certs)
73     {
74         if (certs.size() < 2)
75         {
76             return certs;
77         }
78 
79         X500Principal issuer = ((X509Certificate)certs.get(0)).getIssuerX500Principal();
80         boolean         okay = true;
81 
82         for (int i = 1; i != certs.size(); i++)
83         {
84             X509Certificate cert = (X509Certificate)certs.get(i);
85 
86             if (issuer.equals(cert.getSubjectX500Principal()))
87             {
88                 issuer = ((X509Certificate)certs.get(i)).getIssuerX500Principal();
89             }
90             else
91             {
92                 okay = false;
93                 break;
94             }
95         }
96 
97         if (okay)
98         {
99             return certs;
100         }
101 
102         // find end-entity cert
103         List retList = new ArrayList(certs.size());
104         List orig = new ArrayList(certs);
105 
106         for (int i = 0; i < certs.size(); i++)
107         {
108             X509Certificate cert = (X509Certificate)certs.get(i);
109             boolean         found = false;
110 
111             X500Principal subject = cert.getSubjectX500Principal();
112 
113             for (int j = 0; j != certs.size(); j++)
114             {
115                 X509Certificate c = (X509Certificate)certs.get(j);
116                 if (c.getIssuerX500Principal().equals(subject))
117                 {
118                     found = true;
119                     break;
120                 }
121             }
122 
123             if (!found)
124             {
125                 retList.add(cert);
126                 certs.remove(i);
127             }
128         }
129 
130         // can only have one end entity cert - something's wrong, give up.
131         if (retList.size() > 1)
132         {
133             return orig;
134         }
135 
136         for (int i = 0; i != retList.size(); i++)
137         {
138             issuer = ((X509Certificate)retList.get(i)).getIssuerX500Principal();
139 
140             for (int j = 0; j < certs.size(); j++)
141             {
142                 X509Certificate c = (X509Certificate)certs.get(j);
143                 if (issuer.equals(c.getSubjectX500Principal()))
144                 {
145                     retList.add(c);
146                     certs.remove(j);
147                     break;
148                 }
149             }
150         }
151 
152         // make sure all certificates are accounted for.
153         if (certs.size() > 0)
154         {
155             return orig;
156         }
157 
158         return retList;
159     }
160 
PKIXCertPath(List certificates)161     PKIXCertPath(List certificates)
162     {
163         super("X.509");
164         this.certificates = sortCerts(new ArrayList(certificates));
165     }
166 
167     /**
168      * Creates a CertPath of the specified type.
169      * This constructor is protected because most users should use
170      * a CertificateFactory to create CertPaths.
171      **/
PKIXCertPath( InputStream inStream, String encoding)172     PKIXCertPath(
173         InputStream inStream,
174         String encoding)
175         throws CertificateException
176     {
177         super("X.509");
178         try
179         {
180             if (encoding.equalsIgnoreCase("PkiPath"))
181             {
182                 ASN1InputStream derInStream = new ASN1InputStream(inStream);
183                 ASN1Primitive derObject = derInStream.readObject();
184                 if (!(derObject instanceof ASN1Sequence))
185                 {
186                     throw new CertificateException("input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
187                 }
188                 Enumeration e = ((ASN1Sequence)derObject).getObjects();
189                 certificates = new ArrayList();
190                 CertificateFactory certFactory = helper.createCertificateFactory("X.509");
191                 while (e.hasMoreElements())
192                 {
193                     ASN1Encodable element = (ASN1Encodable)e.nextElement();
194                     byte[] encoded = element.toASN1Primitive().getEncoded(ASN1Encoding.DER);
195                     certificates.add(0, certFactory.generateCertificate(
196                         new ByteArrayInputStream(encoded)));
197                 }
198             }
199             else if (encoding.equalsIgnoreCase("PKCS7") || encoding.equalsIgnoreCase("PEM"))
200             {
201                 inStream = new BufferedInputStream(inStream);
202                 certificates = new ArrayList();
203                 CertificateFactory certFactory= helper.createCertificateFactory("X.509");
204                 Certificate cert;
205                 while ((cert = certFactory.generateCertificate(inStream)) != null)
206                 {
207                     certificates.add(cert);
208                 }
209             }
210             else
211             {
212                 throw new CertificateException("unsupported encoding: " + encoding);
213             }
214         }
215         catch (IOException ex)
216         {
217             throw new CertificateException("IOException throw while decoding CertPath:\n" + ex.toString());
218         }
219         catch (NoSuchProviderException ex)
220         {
221             throw new CertificateException("BouncyCastle provider not found while trying to get a CertificateFactory:\n" + ex.toString());
222         }
223 
224         this.certificates = sortCerts(certificates);
225     }
226 
227     /**
228      * Returns an iteration of the encodings supported by this
229      * certification path, with the default encoding
230      * first. Attempts to modify the returned Iterator via its
231      * remove method result in an UnsupportedOperationException.
232      *
233      * @return an Iterator over the names of the supported encodings (as Strings)
234      **/
getEncodings()235     public Iterator getEncodings()
236     {
237         return certPathEncodings.iterator();
238     }
239 
240     /**
241      * Returns the encoded form of this certification path, using
242      * the default encoding.
243      *
244      * @return the encoded bytes
245      * @exception java.security.cert.CertificateEncodingException if an encoding error occurs
246      **/
getEncoded()247     public byte[] getEncoded()
248         throws CertificateEncodingException
249     {
250         Iterator iter = getEncodings();
251         if (iter.hasNext())
252         {
253             Object enc = iter.next();
254             if (enc instanceof String)
255             {
256             return getEncoded((String)enc);
257             }
258         }
259         return null;
260     }
261 
262     /**
263      * Returns the encoded form of this certification path, using
264      * the specified encoding.
265      *
266      * @param encoding the name of the encoding to use
267      * @return the encoded bytes
268      * @exception java.security.cert.CertificateEncodingException if an encoding error
269      * occurs or the encoding requested is not supported
270      *
271      **/
getEncoded(String encoding)272     public byte[] getEncoded(String encoding)
273         throws CertificateEncodingException
274     {
275         if (encoding.equalsIgnoreCase("PkiPath"))
276         {
277             ASN1EncodableVector v = new ASN1EncodableVector();
278 
279             ListIterator iter = certificates.listIterator(certificates.size());
280             while (iter.hasPrevious())
281             {
282                 v.add(toASN1Object((X509Certificate)iter.previous()));
283             }
284 
285             return toDEREncoded(new DERSequence(v));
286         }
287         else if (encoding.equalsIgnoreCase("PKCS7"))
288         {
289             ContentInfo encInfo = new ContentInfo(PKCSObjectIdentifiers.data, null);
290 
291             ASN1EncodableVector v = new ASN1EncodableVector();
292             for (int i = 0; i != certificates.size(); i++)
293             {
294                 v.add(toASN1Object((X509Certificate)certificates.get(i)));
295             }
296 
297             SignedData sd = new SignedData(
298                                      new ASN1Integer(1),
299                                      new DERSet(),
300                                      encInfo,
301                                      new DERSet(v),
302                                      null,
303                                      new DERSet());
304 
305             return toDEREncoded(new ContentInfo(
306                     PKCSObjectIdentifiers.signedData, sd));
307         }
308         // BEGIN android-removed
309         // else if (encoding.equalsIgnoreCase("PEM"))
310         // {
311         //     ByteArrayOutputStream bOut = new ByteArrayOutputStream();
312         //     PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
313         //
314         //     try
315         //     {
316         //         for (int i = 0; i != certificates.size(); i++)
317         //         {
318         //             pWrt.writeObject(new PemObject("CERTIFICATE", ((X509Certificate)certificates.get(i)).getEncoded()));
319         //         }
320         //
321         //         pWrt.close();
322         //     }
323         //     catch (Exception e)
324         //     {
325         //         throw new CertificateEncodingException("can't encode certificate for PEM encoded path");
326         //     }
327         //
328         //     return bOut.toByteArray();
329         // }
330         // END android-removed
331         else
332         {
333             throw new CertificateEncodingException("unsupported encoding: " + encoding);
334         }
335     }
336 
337     /**
338      * Returns the list of certificates in this certification
339      * path. The List returned must be immutable and thread-safe.
340      *
341      * @return an immutable List of Certificates (may be empty, but not null)
342      **/
getCertificates()343     public List getCertificates()
344     {
345         return Collections.unmodifiableList(new ArrayList(certificates));
346     }
347 
348     /**
349      * Return a DERObject containing the encoded certificate.
350      *
351      * @param cert the X509Certificate object to be encoded
352      *
353      * @return the DERObject
354      **/
toASN1Object( X509Certificate cert)355     private ASN1Primitive toASN1Object(
356         X509Certificate cert)
357         throws CertificateEncodingException
358     {
359         try
360         {
361             return new ASN1InputStream(cert.getEncoded()).readObject();
362         }
363         catch (Exception e)
364         {
365             throw new CertificateEncodingException("Exception while encoding certificate: " + e.toString());
366         }
367     }
368 
toDEREncoded(ASN1Encodable obj)369     private byte[] toDEREncoded(ASN1Encodable obj)
370         throws CertificateEncodingException
371     {
372         try
373         {
374             return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
375         }
376         catch (IOException e)
377         {
378             throw new CertificateEncodingException("Exception thrown: " + e);
379         }
380     }
381 }
382