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