1 module hunt.security.cert.CertPath;
2 
3 import hunt.security.cert.Certificate;
4 
5 import hunt.collection;
6 
7 import std.array;
8 import std.conv;
9 
10 
11 /**
12  * An immutable sequence of certificates (a certification path).
13  * <p>
14  * This is an abstract class that defines the methods common to all
15  * {@code CertPath}s. Subclasses can handle different kinds of
16  * certificates (X.509, PGP, etc.).
17  * <p>
18  * All {@code CertPath} objects have a type, a list of
19  * {@code Certificate}s, and one or more supported encodings. Because the
20  * {@code CertPath} class is immutable, a {@code CertPath} cannot
21  * change in any externally visible way after being constructed. This
22  * stipulation applies to all fields and methods of this class and any
23  * added or overridden by subclasses.
24  * <p>
25  * The type is a {@code string} that identifies the type of
26  * {@code Certificate}s in the certification path. For each
27  * certificate {@code cert} in a certification path {@code certPath},
28  * {@code cert.getType().equals(certPath.getType())} must be
29  * {@code true}.
30  * <p>
31  * The list of {@code Certificate}s is an ordered {@code List} of
32  * zero or more {@code Certificate}s. This {@code List} and all
33  * of the {@code Certificate}s contained in it must be immutable.
34  * <p>
35  * Each {@code CertPath} object must support one or more encodings
36  * so that the object can be translated into a byte array for storage or
37  * transmission to other parties. Preferably, these encodings should be
38  * well-documented standards (such as PKCS#7). One of the encodings supported
39  * by a {@code CertPath} is considered the default encoding. This
40  * encoding is used if no encoding is explicitly requested (for the
41  * {@link #getEncoded() getEncoded()} method, for instance).
42  * <p>
43  * All {@code CertPath} objects are also {@code Serializable}.
44  * {@code CertPath} objects are resolved into an alternate
45  * {@link CertPathRep CertPathRep} object during serialization. This allows
46  * a {@code CertPath} object to be serialized into an equivalent
47  * representation regardless of its underlying implementation.
48  * <p>
49  * {@code CertPath} objects can be created with a
50  * {@code CertificateFactory} or they can be returned by other classes,
51  * such as a {@code CertPathBuilder}.
52  * <p>
53  * By convention, X.509 {@code CertPath}s (consisting of
54  * {@code X509Certificate}s), are ordered starting with the target
55  * certificate and ending with a certificate issued by the trust anchor. That
56  * is, the issuer of one certificate is the subject of the following one. The
57  * certificate representing the {@link TrustAnchor TrustAnchor} should not be
58  * included in the certification path. Unvalidated X.509 {@code CertPath}s
59  * may not follow these conventions. PKIX {@code CertPathValidator}s will
60  * detect any departure from these conventions that cause the certification
61  * path to be invalid and throw a {@code CertPathValidatorException}.
62  *
63  * <p> Every implementation of the Java platform is required to support the
64  * following standard {@code CertPath} encodings:
65  * <ul>
66  * <li>{@code PKCS7}</li>
67  * <li>{@code PkiPath}</li>
68  * </ul>
69  * These encodings are described in the <a href=
70  * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathEncodings">
71  * CertPath Encodings section</a> of the
72  * Java Cryptography Architecture Standard Algorithm Name Documentation.
73  * Consult the release documentation for your implementation to see if any
74  * other encodings are supported.
75  * <p>
76  * <b>Concurrent Access</b>
77  * <p>
78  * All {@code CertPath} objects must be thread-safe. That is, multiple
79  * threads may concurrently invoke the methods defined in this class on a
80  * single {@code CertPath} object (or more than one) with no
81  * ill effects. This is also true for the {@code List} returned by
82  * {@code CertPath.getCertificates}.
83  * <p>
84  * Requiring {@code CertPath} objects to be immutable and thread-safe
85  * allows them to be passed around to various pieces of code without worrying
86  * about coordinating access.  Providing this thread-safety is
87  * generally not difficult, since the {@code CertPath} and
88  * {@code List} objects in question are immutable.
89  *
90  * @see CertificateFactory
91  * @see CertPathBuilder
92  *
93  * @author      Yassir Elley
94  * @since       1.4
95  */
96 abstract class CertPath {
97 
98     // private static final long serialVersionUID = 6068470306649138683L;
99 
100     private string type;        // the type of certificates in this chain
101 
102     /**
103      * Creates a {@code CertPath} of the specified type.
104      * <p>
105      * This constructor is protected because most users should use a
106      * {@code CertificateFactory} to create {@code CertPath}s.
107      *
108      * @param type the standard name of the type of
109      * {@code Certificate}s in this path
110      */
111     protected this(string type) {
112         this.type = type;
113     }
114 
115     /**
116      * Returns the type of {@code Certificate}s in this certification
117      * path. This is the same string that would be returned by
118      * {@link java.security.cert.Certificate#getType() cert.getType()}
119      * for all {@code Certificate}s in the certification path.
120      *
121      * @return the type of {@code Certificate}s in this certification
122      * path (never null)
123      */
124     string getType() {
125         return type;
126     }
127 
128     /**
129      * Returns an iteration of the encodings supported by this certification
130      * path, with the default encoding first. Attempts to modify the returned
131      * {@code Iterator} via its {@code remove} method result in an
132      * {@code UnsupportedOperationException}.
133      *
134      * @return an {@code Iterator} over the names of the supported
135      *         encodings (as Strings)
136      */
137     abstract Iterator!string getEncodings();
138 
139     /**
140      * Compares this certification path for equality with the specified
141      * object. Two {@code CertPath}s are equal if and only if their
142      * types are equal and their certificate {@code List}s (and by
143      * implication the {@code Certificate}s in those {@code List}s)
144      * are equal. A {@code CertPath} is never equal to an object that is
145      * not a {@code CertPath}.
146      * <p>
147      * This algorithm is implemented by this method. If it is overridden,
148      * the behavior specified here must be maintained.
149      *
150      * @param other the object to test for equality with this certification path
151      * @return true if the specified object is equal to this certification path,
152      * false otherwise
153      */
154     override bool opEquals(Object other) {
155         if (this is other)
156             return true;
157 
158         if (other is null)
159             return false;
160 
161         CertPath otherCP = cast(CertPath) other;
162         if (otherCP is null || otherCP.getType() != type)
163             return false;
164 
165         List!Certificate thisCertList = this.getCertificates();
166         List!Certificate otherCertList = otherCP.getCertificates();
167         return(thisCertList == otherCertList);
168     }
169 
170     /**
171      * Returns the hashcode for this certification path. The hash code of
172      * a certification path is defined to be the result of the following
173      * calculation:
174      * <pre>{@code
175      *  hashCode = path.getType().hashCode();
176      *  hashCode = 31*hashCode + path.getCertificates().hashCode();
177      * }</pre>
178      * This ensures that {@code path1.equals(path2)} implies that
179      * {@code path1.hashCode()==path2.hashCode()} for any two certification
180      * paths, {@code path1} and {@code path2}, as required by the
181      * general contract of {@code Object.hashCode}.
182      *
183      * @return the hashcode value for this certification path
184      */
185     override size_t toHash() @trusted nothrow {
186         size_t hashCode = hashOf(type);
187         hashCode = 31*hashCode + getCertificates().toHash();
188         return hashCode;
189     }
190 
191     /**
192      * Returns a string representation of this certification path.
193      * This calls the {@code toString} method on each of the
194      * {@code Certificate}s in the path.
195      *
196      * @return a string representation of this certification path
197      */
198     override string toString() {
199         Appender!string sb;
200         List!Certificate certificates = getCertificates();
201 
202         sb.put("\n" ~ type ~ " Cert Path: length = "
203             ~ certificates.size().to!string() ~ ".\n");
204         sb.put("[\n");
205         int i = 1;
206         foreach(Certificate stringCert; certificates) {
207             sb.put("=========================================="
208                 ~ "===============Certificate " ~ i.to!string() ~ " start.\n");
209             sb.put(stringCert.toString());
210             sb.put("\n========================================"
211                 ~ "=================Certificate " ~ i.to!string() ~ " end.\n\n\n");
212             i++;
213         }
214 
215         sb.put("\n]");
216         return sb.data;
217     }
218 
219     /**
220      * Returns the encoded form of this certification path, using the default
221      * encoding.
222      *
223      * @return the encoded bytes
224      * @exception CertificateEncodingException if an encoding error occurs
225      */
226     abstract byte[] getEncoded();
227 
228     /**
229      * Returns the encoded form of this certification path, using the
230      * specified encoding.
231      *
232      * @param encoding the name of the encoding to use
233      * @return the encoded bytes
234      * @exception CertificateEncodingException if an encoding error occurs or
235      *   the encoding requested is not supported
236      */
237     abstract byte[] getEncoded(string encoding);
238 
239     /**
240      * Returns the list of certificates in this certification path.
241      * The {@code List} returned must be immutable and thread-safe.
242      *
243      * @return an immutable {@code List} of {@code Certificate}s
244      *         (may be empty, but not null)
245      */
246     abstract List!Certificate getCertificates() @trusted nothrow;
247 
248     /**
249      * Replaces the {@code CertPath} to be serialized with a
250      * {@code CertPathRep} object.
251      *
252      * @return the {@code CertPathRep} to be serialized
253      *
254      * @throws ObjectStreamException if a {@code CertPathRep} object
255      * representing this certification path could not be created
256      */
257     // protected Object writeReplace() {
258     //     try {
259     //         return new CertPathRep(type, getEncoded());
260     //     } catch (CertificateException ce) {
261     //         NotSerializableException nse =
262     //             new NotSerializableException
263     //                 ("java.security.cert.CertPath: " ~ type);
264     //         nse.initCause(ce);
265     //         throw nse;
266     //     }
267     // }
268 
269     /**
270      * Alternate {@code CertPath} class for serialization.
271      * @since 1.4
272      */
273     protected static class CertPathRep {
274 
275         // private static final long serialVersionUID = 3015633072427920915L;
276 
277         /** The Certificate type */
278         private string type;
279         /** The encoded form of the cert path */
280         private byte[] data;
281 
282         /**
283          * Creates a {@code CertPathRep} with the specified
284          * type and encoded form of a certification path.
285          *
286          * @param type the standard name of a {@code CertPath} type
287          * @param data the encoded form of the certification path
288          */
289         protected this(string type, byte[] data) {
290             this.type = type;
291             this.data = data;
292         }
293 
294         /**
295          * Returns a {@code CertPath} constructed from the type and data.
296          *
297          * @return the resolved {@code CertPath} object
298          *
299          * @throws ObjectStreamException if a {@code CertPath} could not
300          * be constructed
301          */
302         // protected Object readResolve() {
303         //     try {
304         //         CertificateFactory cf = CertificateFactory.getInstance(type);
305         //         return cf.generateCertPath(new ByteArrayInputStream(data));
306         //     } catch (CertificateException ce) {
307         //         NotSerializableException nse =
308         //             new NotSerializableException
309         //                 ("java.security.cert.CertPath: " ~ type);
310         //         nse.initCause(ce);
311         //         throw nse;
312         //     }
313         // }
314     }
315 }