001: package org.bouncycastle.jce.provider;
002:
003: import java.io.BufferedInputStream;
004: import java.io.ByteArrayInputStream;
005: import java.io.ByteArrayOutputStream;
006: import java.io.IOException;
007: import java.io.InputStream;
008: import java.io.OutputStreamWriter;
009: import java.security.NoSuchProviderException;
010: import java.security.cert.CertPath;
011: import java.security.cert.Certificate;
012: import java.security.cert.CertificateEncodingException;
013: import java.security.cert.CertificateException;
014: import java.security.cert.CertificateFactory;
015: import java.security.cert.X509Certificate;
016: import java.util.ArrayList;
017: import java.util.Collections;
018: import java.util.Enumeration;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.ListIterator;
022:
023: import javax.security.auth.x500.X500Principal;
024:
025: import org.bouncycastle.asn1.ASN1Encodable;
026: import org.bouncycastle.asn1.ASN1EncodableVector;
027: import org.bouncycastle.asn1.ASN1InputStream;
028: import org.bouncycastle.asn1.ASN1Sequence;
029: import org.bouncycastle.asn1.DERInteger;
030: import org.bouncycastle.asn1.DERObject;
031: import org.bouncycastle.asn1.DEROutputStream;
032: import org.bouncycastle.asn1.DERSequence;
033: import org.bouncycastle.asn1.DERSet;
034: import org.bouncycastle.asn1.pkcs.ContentInfo;
035: import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
036: import org.bouncycastle.asn1.pkcs.SignedData;
037: import org.bouncycastle.openssl.PEMWriter;
038:
039: /**
040: * CertPath implementation for X.509 certificates.
041: * <br />
042: **/
043: public class PKIXCertPath extends CertPath {
044: static final List certPathEncodings;
045:
046: static {
047: List encodings = new ArrayList();
048: encodings.add("PkiPath");
049: encodings.add("PEM");
050: encodings.add("PKCS7");
051: certPathEncodings = Collections.unmodifiableList(encodings);
052: }
053:
054: private List certificates;
055:
056: /**
057: * @param certs
058: */
059: private List sortCerts(List certs) {
060: if (certs.size() < 2) {
061: return certs;
062: }
063:
064: X500Principal issuer = ((X509Certificate) certs.get(0))
065: .getIssuerX500Principal();
066: boolean okay = true;
067:
068: for (int i = 1; i != certs.size(); i++) {
069: X509Certificate cert = (X509Certificate) certs.get(i);
070:
071: if (issuer.equals(cert.getSubjectX500Principal())) {
072: issuer = ((X509Certificate) certs.get(i))
073: .getIssuerX500Principal();
074: } else {
075: okay = false;
076: break;
077: }
078: }
079:
080: if (okay) {
081: return certs;
082: }
083:
084: // find end-entity cert
085: List retList = new ArrayList(certs.size());
086:
087: for (int i = 0; i < certs.size(); i++) {
088: X509Certificate cert = (X509Certificate) certs.get(i);
089: boolean found = false;
090:
091: X500Principal subject = cert.getSubjectX500Principal();
092:
093: for (int j = 0; j != certs.size(); j++) {
094: X509Certificate c = (X509Certificate) certs.get(j);
095: if (c.getIssuerX500Principal().equals(subject)) {
096: found = true;
097: break;
098: }
099: }
100:
101: if (!found) {
102: retList.add(cert);
103: certs.remove(i);
104: }
105: }
106:
107: // can only have one end entity cert - something's wrong, give up.
108: if (retList.size() > 1) {
109: for (int i = 0; i != certs.size(); i++) {
110: retList.add(certs.get(i));
111: }
112:
113: return retList;
114: }
115:
116: for (int i = 0; i != retList.size(); i++) {
117: issuer = ((X509Certificate) retList.get(i))
118: .getIssuerX500Principal();
119:
120: for (int j = 0; j < certs.size(); j++) {
121: X509Certificate c = (X509Certificate) certs.get(j);
122: if (issuer.equals(c.getSubjectX500Principal())) {
123: retList.add(c);
124: certs.remove(j);
125: break;
126: }
127: }
128: }
129:
130: // make sure all certificates are accounted for.
131: for (int i = 0; i != certs.size(); i++) {
132: retList.add(certs.get(i));
133: }
134:
135: return retList;
136: }
137:
138: PKIXCertPath(List certificates) {
139: super ("X.509");
140: this .certificates = sortCerts(new ArrayList(certificates));
141: }
142:
143: /**
144: * Creates a CertPath of the specified type.
145: * This constructor is protected because most users should use
146: * a CertificateFactory to create CertPaths.
147: **/
148: PKIXCertPath(InputStream inStream, String encoding)
149: throws CertificateException {
150: super ("X.509");
151: try {
152: if (encoding.equalsIgnoreCase("PkiPath")) {
153: ASN1InputStream derInStream = new ASN1InputStream(
154: inStream);
155: DERObject derObject = derInStream.readObject();
156: if (!(derObject instanceof ASN1Sequence)) {
157: throw new CertificateException(
158: "input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
159: }
160: Enumeration e = ((ASN1Sequence) derObject).getObjects();
161: InputStream certInStream;
162: ByteArrayOutputStream outStream;
163: DEROutputStream derOutStream;
164: certificates = new ArrayList();
165: CertificateFactory certFactory = CertificateFactory
166: .getInstance("X.509", "BC");
167: while (e.hasMoreElements()) {
168: outStream = new ByteArrayOutputStream();
169: derOutStream = new DEROutputStream(outStream);
170:
171: derOutStream.writeObject(e.nextElement());
172: derOutStream.close();
173:
174: certInStream = new ByteArrayInputStream(outStream
175: .toByteArray());
176: certificates.add(0, certFactory
177: .generateCertificate(certInStream));
178: }
179: } else if (encoding.equalsIgnoreCase("PKCS7")
180: || encoding.equalsIgnoreCase("PEM")) {
181: inStream = new BufferedInputStream(inStream);
182: certificates = new ArrayList();
183: CertificateFactory certFactory = CertificateFactory
184: .getInstance("X.509", "BC");
185: Certificate cert;
186: while ((cert = certFactory
187: .generateCertificate(inStream)) != null) {
188: certificates.add(cert);
189: }
190: } else {
191: throw new CertificateException("unsupported encoding: "
192: + encoding);
193: }
194: } catch (IOException ex) {
195: throw new CertificateException(
196: "IOException throw while decoding CertPath:\n"
197: + ex.toString());
198: } catch (NoSuchProviderException ex) {
199: throw new CertificateException(
200: "BouncyCastle provider not found while trying to get a CertificateFactory:\n"
201: + ex.toString());
202: }
203:
204: this .certificates = sortCerts(certificates);
205: }
206:
207: /**
208: * Returns an iteration of the encodings supported by this
209: * certification path, with the default encoding
210: * first. Attempts to modify the returned Iterator via its
211: * remove method result in an UnsupportedOperationException.
212: *
213: * @return an Iterator over the names of the supported encodings (as Strings)
214: **/
215: public Iterator getEncodings() {
216: return certPathEncodings.iterator();
217: }
218:
219: /**
220: * Returns the encoded form of this certification path, using
221: * the default encoding.
222: *
223: * @return the encoded bytes
224: * @exception CertificateEncodingException if an encoding error occurs
225: **/
226: public byte[] getEncoded() throws CertificateEncodingException {
227: Iterator iter = getEncodings();
228: if (iter.hasNext()) {
229: Object enc = iter.next();
230: if (enc instanceof String) {
231: return getEncoded((String) enc);
232: }
233: }
234: return null;
235: }
236:
237: /**
238: * Returns the encoded form of this certification path, using
239: * the specified encoding.
240: *
241: * @param encoding the name of the encoding to use
242: * @return the encoded bytes
243: * @exception CertificateEncodingException if an encoding error
244: * occurs or the encoding requested is not supported
245: *
246: **/
247: public byte[] getEncoded(String encoding)
248: throws CertificateEncodingException {
249: if (encoding.equalsIgnoreCase("PkiPath")) {
250: ASN1EncodableVector v = new ASN1EncodableVector();
251:
252: ListIterator iter = certificates.listIterator(certificates
253: .size());
254: while (iter.hasPrevious()) {
255: v.add(toASN1Object((X509Certificate) iter.previous()));
256: }
257:
258: return toDEREncoded(new DERSequence(v));
259: } else if (encoding.equalsIgnoreCase("PKCS7")) {
260: ContentInfo encInfo = new ContentInfo(
261: PKCSObjectIdentifiers.data, null);
262:
263: ASN1EncodableVector v = new ASN1EncodableVector();
264: for (int i = 0; i != certificates.size(); i++) {
265: v.add(toASN1Object((X509Certificate) certificates
266: .get(i)));
267: }
268:
269: SignedData sd = new SignedData(new DERInteger(1),
270: new DERSet(), encInfo, new DERSet(v), null,
271: new DERSet());
272:
273: return toDEREncoded(new ContentInfo(
274: PKCSObjectIdentifiers.signedData, sd));
275: } else if (encoding.equalsIgnoreCase("PEM")) {
276: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
277: PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut));
278:
279: try {
280: for (int i = 0; i != certificates.size(); i++) {
281: pWrt.writeObject(certificates.get(i));
282: }
283:
284: pWrt.close();
285: } catch (Exception e) {
286: throw new CertificateEncodingException(
287: "can't encode certificate for PEM encoded path");
288: }
289:
290: return bOut.toByteArray();
291: } else {
292: throw new CertificateEncodingException(
293: "unsupported encoding: " + encoding);
294: }
295: }
296:
297: /**
298: * Returns the list of certificates in this certification
299: * path. The List returned must be immutable and thread-safe.
300: *
301: * @return an immutable List of Certificates (may be empty, but not null)
302: **/
303: public List getCertificates() {
304: return Collections
305: .unmodifiableList(new ArrayList(certificates));
306: }
307:
308: /**
309: * Return a DERObject containing the encoded certificate.
310: *
311: * @param cert the X509Certificate object to be encoded
312: *
313: * @return the DERObject
314: **/
315: private DERObject toASN1Object(X509Certificate cert)
316: throws CertificateEncodingException {
317: try {
318: return new ASN1InputStream(cert.getEncoded()).readObject();
319: } catch (Exception e) {
320: throw new CertificateEncodingException(
321: "Exception while encoding certificate: "
322: + e.toString());
323: }
324: }
325:
326: private byte[] toDEREncoded(ASN1Encodable obj)
327: throws CertificateEncodingException {
328: try {
329: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
330: DEROutputStream dOut = new DEROutputStream(bOut);
331:
332: dOut.writeObject(obj);
333: dOut.close();
334:
335: return bOut.toByteArray();
336: } catch (IOException e) {
337: throw new CertificateEncodingException("Exeption thrown: "
338: + e);
339: }
340: }
341: }
|