1package org.bouncycastle.jcajce.provider.asymmetric.x509; 2 3import java.io.BufferedInputStream; 4import java.io.ByteArrayInputStream; 5import java.io.ByteArrayOutputStream; 6import java.io.IOException; 7import java.io.InputStream; 8import java.io.OutputStreamWriter; 9import java.security.NoSuchProviderException; 10import java.security.cert.CertPath; 11import java.security.cert.Certificate; 12import java.security.cert.CertificateEncodingException; 13import java.security.cert.CertificateException; 14import java.security.cert.CertificateFactory; 15import java.security.cert.X509Certificate; 16import java.util.ArrayList; 17import java.util.Collections; 18import java.util.Enumeration; 19import java.util.Iterator; 20import java.util.List; 21import java.util.ListIterator; 22 23import javax.security.auth.x500.X500Principal; 24 25import org.bouncycastle.asn1.ASN1Encodable; 26import org.bouncycastle.asn1.ASN1EncodableVector; 27import org.bouncycastle.asn1.ASN1Encoding; 28import org.bouncycastle.asn1.ASN1InputStream; 29import org.bouncycastle.asn1.ASN1Integer; 30import org.bouncycastle.asn1.ASN1Primitive; 31import org.bouncycastle.asn1.ASN1Sequence; 32import org.bouncycastle.asn1.DERSequence; 33import org.bouncycastle.asn1.DERSet; 34import org.bouncycastle.asn1.pkcs.ContentInfo; 35import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 36import org.bouncycastle.asn1.pkcs.SignedData; 37import org.bouncycastle.jce.provider.BouncyCastleProvider; 38import 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 **/ 47public 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