1 /* X509CertPath.java -- an X.509 certificate path.
2 Copyright (C) 2004 Free Software Fonudation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu
.java
.security
.x509
;
41 import gnu
.java
.security
.OID
;
42 import gnu
.java
.security
.der
.DER
;
43 import gnu
.java
.security
.der
.DEREncodingException
;
44 import gnu
.java
.security
.der
.DERReader
;
45 import gnu
.java
.security
.der
.DERValue
;
47 import java
.io
.ByteArrayInputStream
;
48 import java
.io
.ByteArrayOutputStream
;
49 import java
.io
.IOException
;
50 import java
.io
.InputStream
;
51 import java
.math
.BigInteger
;
52 import java
.security
.cert
.CertPath
;
53 import java
.security
.cert
.Certificate
;
54 import java
.security
.cert
.CertificateEncodingException
;
55 import java
.security
.cert
.CertificateException
;
56 import java
.util
.ArrayList
;
57 import java
.util
.Arrays
;
58 import java
.util
.Collections
;
59 import java
.util
.Iterator
;
60 import java
.util
.LinkedList
;
61 import java
.util
.List
;
64 * A certificate path (or certificate chain) of X509Certificates.
66 * @author Casey Marshall (rsdio@metastatic.org)
68 public class X509CertPath
extends CertPath
72 // -------------------------------------------------------------------------
74 public static final List ENCODINGS
= Collections
.unmodifiableList(
75 Arrays
.asList(new String
[] { "PkiPath", "PKCS7" }));
77 private static final OID PKCS7_SIGNED_DATA
= new OID("1.2.840.113549.1.7.2");
78 private static final OID PKCS7_DATA
= new OID("1.2.840.113549.1.7.1");
80 /** The certificate path. */
83 /** The cached PKCS #7 encoded bytes. */
84 private byte[] pkcs_encoded
;
86 /** The cached PkiPath encoded bytes. */
87 private byte[] pki_encoded
;
90 // -------------------------------------------------------------------------
92 public X509CertPath(List path
)
95 this.path
= Collections
.unmodifiableList(path
);
98 public X509CertPath(InputStream in
) throws CertificateEncodingException
100 this(in
, (String
) ENCODINGS
.get(0));
103 public X509CertPath(InputStream in
, String encoding
)
104 throws CertificateEncodingException
111 catch (IOException ioe
)
113 throw new CertificateEncodingException();
118 // -------------------------------------------------------------------------
120 public List
getCertificates()
122 return path
; // already unmodifiable
125 public byte[] getEncoded() throws CertificateEncodingException
127 return getEncoded((String
) ENCODINGS
.get(0));
130 public byte[] getEncoded(String encoding
) throws CertificateEncodingException
132 if (encoding
.equalsIgnoreCase("PkiPath"))
134 if (pki_encoded
== null)
138 pki_encoded
= encodePki();
140 catch (IOException ioe
)
142 throw new CertificateEncodingException();
145 return (byte[]) pki_encoded
.clone();
147 else if (encoding
.equalsIgnoreCase("PKCS7"))
149 if (pkcs_encoded
== null)
153 pkcs_encoded
= encodePKCS();
155 catch (IOException ioe
)
157 throw new CertificateEncodingException();
160 return (byte[]) pkcs_encoded
.clone();
163 throw new CertificateEncodingException("unknown encoding: " + encoding
);
166 public Iterator
getEncodings()
168 return ENCODINGS
.iterator(); // already unmodifiable
172 // -------------------------------------------------------------------------
174 private void parse(InputStream in
, String encoding
)
175 throws CertificateEncodingException
, IOException
177 DERReader der
= new DERReader(in
);
178 DERValue path
= null;
179 if (encoding
.equalsIgnoreCase("PkiPath"))
181 // PKI encoding is just a SEQUENCE of X.509 certificates.
183 if (!path
.isConstructed())
184 throw new DEREncodingException("malformed PkiPath");
186 else if (encoding
.equalsIgnoreCase("PKCS7"))
188 // PKCS #7 encoding means that the certificates are contained in a
189 // SignedData PKCS #7 type.
191 // ContentInfo ::= SEQUENCE {
192 // contentType ::= ContentType,
193 // content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
195 // ContentType ::= OBJECT IDENTIFIER
197 // SignedData ::= SEQUENCE {
199 // digestAlgorithms DigestAlgorithmIdentifiers,
200 // contentInfo ContentInfo,
201 // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates
203 // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
204 // signerInfos SignerInfos }
206 // Version ::= INTEGER
208 DERValue value
= der
.read();
209 if (!value
.isConstructed())
210 throw new DEREncodingException("malformed ContentInfo");
212 if (!(value
.getValue() instanceof OID
) ||
213 ((OID
) value
.getValue()).equals(PKCS7_SIGNED_DATA
))
214 throw new DEREncodingException("not a SignedData");
216 if (!value
.isConstructed() || value
.getTag() != 0)
217 throw new DEREncodingException("malformed content");
219 if (value
.getTag() != DER
.INTEGER
)
220 throw new DEREncodingException("malformed Version");
222 if (!value
.isConstructed() || value
.getTag() != DER
.SET
)
223 throw new DEREncodingException("malformed DigestAlgorithmIdentifiers");
224 der
.skip(value
.getLength());
226 if (!value
.isConstructed())
227 throw new DEREncodingException("malformed ContentInfo");
228 der
.skip(value
.getLength());
230 if (!path
.isConstructed() || path
.getTag() != 0)
231 throw new DEREncodingException("no certificates");
234 throw new CertificateEncodingException("unknown encoding: " + encoding
);
236 LinkedList certs
= new LinkedList();
238 while (len
< path
.getLength())
240 DERValue cert
= der
.read();
243 certs
.add(new X509Certificate(new ByteArrayInputStream(cert
.getEncoded())));
245 catch (CertificateException ce
)
247 throw new CertificateEncodingException(ce
.getMessage());
249 len
+= cert
.getEncodedLength();
250 der
.skip(cert
.getLength());
253 this.path
= Collections
.unmodifiableList(certs
);
256 private byte[] encodePki()
257 throws CertificateEncodingException
, IOException
261 ByteArrayOutputStream out
= new ByteArrayOutputStream();
262 for (Iterator i
= path
.iterator(); i
.hasNext(); )
264 out
.write(((Certificate
) i
.next()).getEncoded());
266 byte[] b
= out
.toByteArray();
267 DERValue val
= new DERValue(DER
.CONSTRUCTED
| DER
.SEQUENCE
,
269 return val
.getEncoded();
273 private byte[] encodePKCS()
274 throws CertificateEncodingException
, IOException
278 ArrayList signedData
= new ArrayList(5);
279 signedData
.add(new DERValue(DER
.INTEGER
, BigInteger
.ONE
));
280 signedData
.add(new DERValue(DER
.CONSTRUCTED
| DER
.SET
,
281 Collections
.EMPTY_SET
));
282 signedData
.add(new DERValue(DER
.CONSTRUCTED
| DER
.SEQUENCE
,
283 Collections
.singletonList(
284 new DERValue(DER
.OBJECT_IDENTIFIER
, PKCS7_DATA
))));
285 ByteArrayOutputStream out
= new ByteArrayOutputStream();
286 for (Iterator i
= path
.iterator(); i
.hasNext(); )
288 out
.write(((Certificate
) i
.next()).getEncoded());
290 byte[] b
= out
.toByteArray();
291 signedData
.add(new DERValue(DER
.CONSTRUCTED
| DER
.CONTEXT
,
293 DERValue sdValue
= new DERValue(DER
.CONSTRUCTED
| DER
.SEQUENCE
,
296 ArrayList contentInfo
= new ArrayList(2);
297 contentInfo
.add(new DERValue(DER
.OBJECT_IDENTIFIER
, PKCS7_SIGNED_DATA
));
298 contentInfo
.add(new DERValue(DER
.CONSTRUCTED
| DER
.CONTEXT
, sdValue
));
299 return new DERValue(DER
.CONSTRUCTED
| DER
.SEQUENCE
,
300 contentInfo
).getEncoded();