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