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 org.bouncycastle.jce.X509Principal; 24 import org.bouncycastle.jce.PrincipalUtil; 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 import org.bouncycastle.util.io.pem.PemWriter; 40 41 /** 42 * CertPath implementation for X.509 certificates. 43 * <br /> 44 **/ 45 public class PKIXCertPath 46 extends CertPath 47 { 48 static final List certPathEncodings; 49 50 static 51 { 52 List encodings = new ArrayList(); 53 encodings.add("PkiPath"); 54 encodings.add("PEM"); 55 encodings.add("PKCS7"); 56 certPathEncodings = Collections.unmodifiableList(encodings); 57 } 58 59 private List certificates; 60 61 /** 62 * @param certs 63 */ sortCerts( List certs)64 private List sortCerts( 65 List certs) 66 { 67 try 68 { 69 if (certs.size() < 2) 70 { 71 return certs; 72 } 73 74 X509Principal issuer = PrincipalUtil.getIssuerX509Principal(((X509Certificate)certs.get(0))); 75 boolean okay = true; 76 77 for (int i = 1; i != certs.size(); i++) 78 { 79 X509Certificate cert = (X509Certificate)certs.get(i); 80 81 if (issuer.equals(PrincipalUtil.getSubjectX509Principal(cert))) 82 { 83 issuer = PrincipalUtil.getIssuerX509Principal(((X509Certificate)certs.get(i))); 84 } 85 else 86 { 87 okay = false; 88 break; 89 } 90 } 91 92 if (okay) 93 { 94 return certs; 95 } 96 97 // find end-entity cert 98 List retList = new ArrayList(certs.size()); 99 List orig = new ArrayList(certs); 100 101 for (int i = 0; i < certs.size(); i++) 102 { 103 X509Certificate cert = (X509Certificate)certs.get(i); 104 boolean found = false; 105 106 X509Principal subject = PrincipalUtil.getSubjectX509Principal(cert); 107 108 for (int j = 0; j != certs.size(); j++) 109 { 110 X509Certificate c = (X509Certificate)certs.get(j); 111 if (PrincipalUtil.getIssuerX509Principal(c).equals(subject)) 112 { 113 found = true; 114 break; 115 } 116 } 117 118 if (!found) 119 { 120 retList.add(cert); 121 certs.remove(i); 122 } 123 } 124 125 // can only have one end entity cert - something's wrong, give up. 126 if (retList.size() > 1) 127 { 128 return orig; 129 } 130 131 for (int i = 0; i != retList.size(); i++) 132 { 133 issuer = PrincipalUtil.getIssuerX509Principal(((X509Certificate)retList.get(i))); 134 135 for (int j = 0; j < certs.size(); j++) 136 { 137 X509Certificate c = (X509Certificate)certs.get(j); 138 if (issuer.equals(PrincipalUtil.getSubjectX509Principal(c))) 139 { 140 retList.add(c); 141 certs.remove(j); 142 break; 143 } 144 } 145 } 146 147 // make sure all certificates are accounted for. 148 if (certs.size() > 0) 149 { 150 return orig; 151 } 152 153 return retList; 154 } 155 catch (Exception e) 156 { 157 return certs; 158 } 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 = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 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= CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); 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 else if (encoding.equalsIgnoreCase("PEM")) 309 { 310 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 311 PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut)); 312 313 try 314 { 315 for (int i = 0; i != certificates.size(); i++) 316 { 317 pWrt.writeObject(new PemObject("CERTIFICATE", ((X509Certificate)certificates.get(i)).getEncoded())); 318 } 319 320 pWrt.close(); 321 } 322 catch (Exception e) 323 { 324 throw new CertificateEncodingException("can't encode certificate for PEM encoded path"); 325 } 326 327 return bOut.toByteArray(); 328 } 329 else 330 { 331 throw new CertificateEncodingException("unsupported encoding: " + encoding); 332 } 333 } 334 335 /** 336 * Returns the list of certificates in this certification 337 * path. The List returned must be immutable and thread-safe. 338 * 339 * @return an immutable List of Certificates (may be empty, but not null) 340 **/ getCertificates()341 public List getCertificates() 342 { 343 return Collections.unmodifiableList(new ArrayList(certificates)); 344 } 345 346 /** 347 * Return a DERObject containing the encoded certificate. 348 * 349 * @param cert the X509Certificate object to be encoded 350 * 351 * @return the DERObject 352 **/ toASN1Object( X509Certificate cert)353 private ASN1Primitive toASN1Object( 354 X509Certificate cert) 355 throws CertificateEncodingException 356 { 357 try 358 { 359 return new ASN1InputStream(cert.getEncoded()).readObject(); 360 } 361 catch (Exception e) 362 { 363 throw new CertificateEncodingException("Exception while encoding certificate: " + e.toString()); 364 } 365 } 366 toDEREncoded(ASN1Encodable obj)367 private byte[] toDEREncoded(ASN1Encodable obj) 368 throws CertificateEncodingException 369 { 370 try 371 { 372 return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); 373 } 374 catch (IOException e) 375 { 376 throw new CertificateEncodingException("Exception thrown: " + e); 377 } 378 } 379 } 380