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 }