1 module hunt.security.x500.X500Principal;
2 
3 import hunt.security.x509.X500Name;
4 import hunt.security.Principal;
5 
6 import hunt.collection;
7 import hunt.Exceptions;
8 import hunt.text.Common;
9 
10 import std.array;
11 
12 
13 /**
14  * <p> This class represents an X.500 {@code Principal}.
15  * {@code X500Principal}s are represented by distinguished names such as
16  * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US".
17  *
18  * <p> This class can be instantiated by using a string representation
19  * of the distinguished name, or by using the ASN.1 DER encoded byte
20  * representation of the distinguished name.  The current specification
21  * for the string representation of a distinguished name is defined in
22  * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253: Lightweight
23  * Directory Access Protocol (v3): UTF-8 string Representation of
24  * Distinguished Names</a>. This class, however, accepts string formats from
25  * both RFC 2253 and <a href="http://www.ietf.org/rfc/rfc1779.txt">RFC 1779:
26  * A string Representation of Distinguished Names</a>, and also recognizes
27  * attribute type keywords whose OIDs (Object Identifiers) are defined in
28  * <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
29  * Public Key Infrastructure Certificate and CRL Profile</a>.
30  *
31  * <p> The string representation for this {@code X500Principal}
32  * can be obtained by calling the {@code getName} methods.
33  *
34  * <p> Note that the {@code getSubjectX500Principal} and
35  * {@code getIssuerX500Principal} methods of
36  * {@code X509Certificate} return X500Principals representing the
37  * issuer and subject fields of the certificate.
38  *
39  * @see java.security.cert.X509Certificate
40  * @since 1.4
41  */
42 final class X500Principal : Principal {
43 
44     // private static final long serialVersionUID = -500463348111345721L;
45 
46     /**
47      * RFC 1779 string format of Distinguished Names.
48      */
49     enum string RFC1779 = "RFC1779";
50     /**
51      * RFC 2253 string format of Distinguished Names.
52      */
53     enum string RFC2253 = "RFC2253";
54     /**
55      * Canonical string format of Distinguished Names.
56      */
57     enum string CANONICAL = "CANONICAL";
58 
59     /**
60      * The X500Name representing this principal.
61      *
62      * NOTE: this field is reflectively accessed from within X500Name.
63      */
64     private X500Name thisX500Name;
65 
66     /**
67      * Creates an X500Principal by wrapping an X500Name.
68      *
69      * NOTE: The constructor is package private. It is intended to be accessed
70      * using privileged reflection from classes in sun.security.*.
71      * Currently referenced from sun.security.x509.X500Name.asX500Principal().
72      */
73     this(X500Name x500Name) {
74         thisX500Name = x500Name;
75     }
76 
77     /**
78      * Creates an {@code X500Principal} from a string representation of
79      * an X.500 distinguished name (ex:
80      * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US").
81      * The distinguished name must be specified using the grammar defined in
82      * RFC 1779 or RFC 2253 (either format is acceptable).
83      *
84      * <p>This constructor recognizes the attribute type keywords
85      * defined in RFC 1779 and RFC 2253
86      * (and listed in {@link #getName(string format) getName(string format)}),
87      * as well as the T, DNQ or DNQUALIFIER, SURNAME, GIVENNAME, INITIALS,
88      * GENERATION, EMAILADDRESS, and SERIALNUMBER keywords whose Object
89      * Identifiers (OIDs) are defined in RFC 3280 and its successor.
90      * Any other attribute type must be specified as an OID.
91      *
92      * <p>This implementation enforces a more restrictive OID syntax than
93      * defined in RFC 1779 and 2253. It uses the more correct syntax defined in
94      * <a href="http://www.ietf.org/rfc/rfc4512.txt">RFC 4512</a>, which
95      * specifies that OIDs contain at least 2 digits:
96      *
97      * <p>{@code numericoid = number 1*( DOT number ) }
98      *
99      * @param name an X.500 distinguished name in RFC 1779 or RFC 2253 format
100      * @exception NullPointerException if the {@code name}
101      *                  is {@code null}
102      * @exception IllegalArgumentException if the {@code name}
103      *                  is improperly specified
104      */
105     this(string name) {
106         this(name, Collections.emptyMap!(string, string)());
107     }
108 
109     /**
110      * Creates an {@code X500Principal} from a string representation of
111      * an X.500 distinguished name (ex:
112      * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US").
113      * The distinguished name must be specified using the grammar defined in
114      * RFC 1779 or RFC 2253 (either format is acceptable).
115      *
116      * <p> This constructor recognizes the attribute type keywords specified
117      * in {@link #X500Principal(string)} and also recognizes additional
118      * keywords that have entries in the {@code keywordMap} parameter.
119      * Keyword entries in the keywordMap take precedence over the default
120      * keywords recognized by {@code X500Principal(string)}. Keywords
121      * MUST be specified in all upper-case, otherwise they will be ignored.
122      * Improperly specified keywords are ignored; however if a keyword in the
123      * name maps to an improperly specified Object Identifier (OID), an
124      * {@code IllegalArgumentException} is thrown. It is permissible to
125      * have 2 different keywords that map to the same OID.
126      *
127      * <p>This implementation enforces a more restrictive OID syntax than
128      * defined in RFC 1779 and 2253. It uses the more correct syntax defined in
129      * <a href="http://www.ietf.org/rfc/rfc4512.txt">RFC 4512</a>, which
130      * specifies that OIDs contain at least 2 digits:
131      *
132      * <p>{@code numericoid = number 1*( DOT number ) }
133      *
134      * @param name an X.500 distinguished name in RFC 1779 or RFC 2253 format
135      * @param keywordMap an attribute type keyword map, where each key is a
136      *   keyword string that maps to a corresponding object identifier in string
137      *   form (a sequence of nonnegative integers separated by periods). The map
138      *   may be empty but never {@code null}.
139      * @exception NullPointerException if {@code name} or
140      *   {@code keywordMap} is {@code null}
141      * @exception IllegalArgumentException if the {@code name} is
142      *   improperly specified or a keyword in the {@code name} maps to an
143      *   OID that is not in the correct form
144      * @since 1.6
145      */
146     this(string name, Map!(string, string) keywordMap) {
147         if (name.empty) {
148             throw new NullPointerException("provided.null.name");
149         }
150         if (keywordMap is null) {
151             throw new NullPointerException("provided.null.keyword.map");
152         }
153 
154         try {
155             thisX500Name = new X500Name(name, keywordMap);
156         } catch (Exception e) {
157             IllegalArgumentException iae = new IllegalArgumentException
158                         ("improperly specified input name: " ~ name, e);
159             throw iae;
160         }
161     }
162 
163     /**
164      * Creates an {@code X500Principal} from a distinguished name in
165      * ASN.1 DER encoded form. The ASN.1 notation for this structure is as
166      * follows.
167      * <pre>{@code
168      * Name ::= CHOICE {
169      *   RDNSequence }
170      *
171      * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
172      *
173      * RelativeDistinguishedName ::=
174      *   SET SIZE (1 .. MAX) OF AttributeTypeAndValue
175      *
176      * AttributeTypeAndValue ::= SEQUENCE {
177      *   type     AttributeType,
178      *   value    AttributeValue }
179      *
180      * AttributeType ::= OBJECT IDENTIFIER
181      *
182      * AttributeValue ::= ANY DEFINED BY AttributeType
183      * ....
184      * DirectoryString ::= CHOICE {
185      *       teletexString           TeletexString (SIZE (1..MAX)),
186      *       printableString         PrintableString (SIZE (1..MAX)),
187      *       universalString         UniversalString (SIZE (1..MAX)),
188      *       utf8String              UTF8String (SIZE (1.. MAX)),
189      *       bmpString               BMPString (SIZE (1..MAX)) }
190      * }</pre>
191      *
192      * @param name a byte array containing the distinguished name in ASN.1
193      * DER encoded form
194      * @throws IllegalArgumentException if an encoding error occurs
195      *          (incorrect form for DN)
196      */
197     this(byte[] name) {
198         try {
199             thisX500Name = new X500Name(name);
200         } catch (Exception e) {
201             IllegalArgumentException iae = new IllegalArgumentException
202                         ("improperly specified input name", e);
203             throw iae;
204         }
205     }
206 
207     /**
208      * Creates an {@code X500Principal} from an {@code InputStream}
209      * containing the distinguished name in ASN.1 DER encoded form.
210      * The ASN.1 notation for this structure is supplied in the
211      * documentation for
212      * {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.
213      *
214      * <p> The read position of the input stream is positioned
215      * to the next available byte after the encoded distinguished name.
216      *
217      * @param is an {@code InputStream} containing the distinguished
218      *          name in ASN.1 DER encoded form
219      *
220      * @exception NullPointerException if the {@code InputStream}
221      *          is {@code null}
222      * @exception IllegalArgumentException if an encoding error occurs
223      *          (incorrect form for DN)
224      */
225     // this(InputStream inputStream) {
226     //     if (inputStream is null) {
227     //         throw new NullPointerException("provided null input stream");
228     //     }
229 
230     //     try {
231     //         if (inputStream.markSupported())
232     //             inputStream.mark(inputStream.available() + 1);
233     //         DerValue der = new DerValue(inputStream);
234     //         thisX500Name = new X500Name(der.data);
235     //     } catch (Exception e) {
236     //         if (inputStream.markSupported()) {
237     //             try {
238     //                 inputStream.reset();
239     //             } catch (IOException ioe) {
240     //                 IllegalArgumentException iae = new IllegalArgumentException
241     //                     ("improperly specified input stream " ~
242     //                     ("and unable to reset input stream"), e);
243     //                 throw iae;
244     //             }
245     //         }
246     //         IllegalArgumentException iae = new IllegalArgumentException
247     //                     ("improperly specified input stream", e);
248     //         throw iae;
249     //     }
250     // }
251 
252     /**
253      * Returns a string representation of the X.500 distinguished name using
254      * the format defined in RFC 2253.
255      *
256      * <p>This method is equivalent to calling
257      * {@code getName(X500Principal.RFC2253)}.
258      *
259      * @return the distinguished name of this {@code X500Principal}
260      */
261     string getName() {
262         return getName(X500Principal.RFC2253);
263     }
264 
265     /**
266      * Returns a string representation of the X.500 distinguished name
267      * using the specified format. Valid values for the format are
268      * "RFC1779", "RFC2253", and "CANONICAL" (case insensitive).
269      *
270      * <p> If "RFC1779" is specified as the format,
271      * this method emits the attribute type keywords defined in
272      * RFC 1779 (CN, L, ST, O, OU, C, STREET).
273      * Any other attribute type is emitted as an OID.
274      *
275      * <p> If "RFC2253" is specified as the format,
276      * this method emits the attribute type keywords defined in
277      * RFC 2253 (CN, L, ST, O, OU, C, STREET, DC, UID).
278      * Any other attribute type is emitted as an OID.
279      * Under a strict reading, RFC 2253 only specifies a UTF-8 string
280      * representation. The string returned by this method is the
281      * Unicode string achieved by decoding this UTF-8 representation.
282      *
283      * <p> If "CANONICAL" is specified as the format,
284      * this method returns an RFC 2253 conformant string representation
285      * with the following additional canonicalizations:
286      *
287      * <ol>
288      * <li> Leading zeros are removed from attribute types
289      *          that are encoded as dotted decimal OIDs
290      * <li> DirectoryString attribute values of type
291      *          PrintableString and UTF8String are not
292      *          output in hexadecimal format
293      * <li> DirectoryString attribute values of types
294      *          other than PrintableString and UTF8String
295      *          are output in hexadecimal format
296      * <li> Leading and trailing white space characters
297      *          are removed from non-hexadecimal attribute values
298      *          (unless the value consists entirely of white space characters)
299      * <li> Internal substrings of one or more white space characters are
300      *          converted to a single space in non-hexadecimal
301      *          attribute values
302      * <li> Relative Distinguished Names containing more than one
303      *          Attribute Value Assertion (AVA) are output in the
304      *          following order: an alphabetical ordering of AVAs
305      *          containing standard keywords, followed by a numeric
306      *          ordering of AVAs containing OID keywords.
307      * <li> The only characters in attribute values that are escaped are
308      *          those which section 2.4 of RFC 2253 states must be escaped
309      *          (they are escaped using a preceding backslash character)
310      * <li> The entire name is converted to upper case
311      *          using {@code string.toUpperCase(Locale.US)}
312      * <li> The entire name is converted to lower case
313      *          using {@code string.toLowerCase(Locale.US)}
314      * <li> The name is finally normalized using normalization form KD,
315      *          as described in the Unicode Standard and UAX #15
316      * </ol>
317      *
318      * <p> Additional standard formats may be introduced in the future.
319      *
320      * @param format the format to use
321      *
322      * @return a string representation of this {@code X500Principal}
323      *          using the specified format
324      * @throws IllegalArgumentException if the specified format is invalid
325      *          or null
326      */
327     string getName(string format) {
328         if (format !is null) {
329             if (format.equalsIgnoreCase(RFC1779)) {
330                 return thisX500Name.getRFC1779Name();
331             } else if (format.equalsIgnoreCase(RFC2253)) {
332                 return thisX500Name.getRFC2253Name();
333             } else if (format.equalsIgnoreCase(CANONICAL)) {
334                 return thisX500Name.getRFC2253CanonicalName();
335             }
336         }
337         throw new IllegalArgumentException("invalid format specified");
338     }
339 
340     /**
341      * Returns a string representation of the X.500 distinguished name
342      * using the specified format. Valid values for the format are
343      * "RFC1779" and "RFC2253" (case insensitive). "CANONICAL" is not
344      * permitted and an {@code IllegalArgumentException} will be thrown.
345      *
346      * <p>This method returns Strings in the format as specified in
347      * {@link #getName(string)} and also emits additional attribute type
348      * keywords for OIDs that have entries in the {@code oidMap}
349      * parameter. OID entries in the oidMap take precedence over the default
350      * OIDs recognized by {@code getName(string)}.
351      * Improperly specified OIDs are ignored; however if an OID
352      * in the name maps to an improperly specified keyword, an
353      * {@code IllegalArgumentException} is thrown.
354      *
355      * <p> Additional standard formats may be introduced in the future.
356      *
357      * <p> Warning: additional attribute type keywords may not be recognized
358      * by other implementations; therefore do not use this method if
359      * you are unsure if these keywords will be recognized by other
360      * implementations.
361      *
362      * @param format the format to use
363      * @param oidMap an OID map, where each key is an object identifier in
364      *  string form (a sequence of nonnegative integers separated by periods)
365      *  that maps to a corresponding attribute type keyword string.
366      *  The map may be empty but never {@code null}.
367      * @return a string representation of this {@code X500Principal}
368      *          using the specified format
369      * @throws IllegalArgumentException if the specified format is invalid,
370      *  null, or an OID in the name maps to an improperly specified keyword
371      * @throws NullPointerException if {@code oidMap} is {@code null}
372      * @since 1.6
373      */
374     string getName(string format, Map!(string, string) oidMap) {
375         if (oidMap is null) {
376             throw new NullPointerException("provided.null.OID.map");
377         }
378         if (format !is null) {
379             if (format.equalsIgnoreCase(RFC1779)) {
380                 return thisX500Name.getRFC1779Name(oidMap);
381             } else if (format.equalsIgnoreCase(RFC2253)) {
382                 return thisX500Name.getRFC2253Name(oidMap);
383             }
384         }
385         throw new IllegalArgumentException("invalid format specified");
386     }
387 
388     /**
389      * Returns the distinguished name in ASN.1 DER encoded form. The ASN.1
390      * notation for this structure is supplied in the documentation for
391      * {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.
392      *
393      * <p>Note that the byte array returned is cloned to protect against
394      * subsequent modifications.
395      *
396      * @return a byte array containing the distinguished name in ASN.1 DER
397      * encoded form
398      */
399     byte[] getEncoded() {
400         try {
401             return thisX500Name.getEncoded();
402         } catch (IOException e) {
403             throw new RuntimeException("unable to get encoding", e);
404         }
405     }
406 
407     /**
408      * Return a user-friendly string representation of this
409      * {@code X500Principal}.
410      *
411      * @return a string representation of this {@code X500Principal}
412      */
413     override string toString() {
414         return thisX500Name.toString();
415     }
416 
417     /**
418      * Compares the specified {@code Object} with this
419      * {@code X500Principal} for equality.
420      *
421      * <p> Specifically, this method returns {@code true} if
422      * the {@code Object} <i>o</i> is an {@code X500Principal}
423      * and if the respective canonical string representations
424      * (obtained via the {@code getName(X500Principal.CANONICAL)} method)
425      * of this object and <i>o</i> are equal.
426      *
427      * <p> This implementation is compliant with the requirements of RFC 3280.
428      *
429      * @param o Object to be compared for equality with this
430      *          {@code X500Principal}
431      *
432      * @return {@code true} if the specified {@code Object} is equal
433      *          to this {@code X500Principal}, {@code false} otherwise
434      */
435     override bool opEquals(Object o) {
436         if (this is o) {
437             return true;
438         }
439         if (typeid(o) != typeid(X500Principal)) {
440             return false;
441         }
442         X500Principal other = cast(X500Principal)o;
443         return this.thisX500Name == other.thisX500Name;
444     }
445 
446     /**
447      * Return a hash code for this {@code X500Principal}.
448      *
449      * <p> The hash code is calculated via:
450      * {@code getName(X500Principal.CANONICAL).hashCode()}
451      *
452      * @return a hash code for this {@code X500Principal}
453      */
454     override size_t toHash() @trusted nothrow {
455         return thisX500Name.toHash();
456     }
457 
458     // /**
459     //  * Save the X500Principal object to a stream.
460     //  *
461     //  * @serialData this {@code X500Principal} is serialized
462     //  *          by writing out its DER-encoded form
463     //  *          (the value of {@code getEncoded} is serialized).
464     //  */
465     // private void writeObject(java.io.ObjectOutputStream s) {
466     //     s.writeObject(thisX500Name.getEncodedInternal());
467     // }
468 
469     // /**
470     //  * Reads this object from a stream (i.e., deserializes it).
471     //  */
472     // private void readObject(java.io.ObjectInputStream s) {
473     //     // re-create thisX500Name
474     //     thisX500Name = new X500Name(cast(byte[])s.readObject());
475     // }
476 }