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