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