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