1 module hunt.security.x509.X500Name;
2 
3 import hunt.security.x500.X500Principal;
4 import hunt.security.x509.GeneralNameInterface;
5 import hunt.security.Principal;
6 import hunt.security.util.DerValue;
7 import hunt.security.util.DerOutputStream;
8 
9 import hunt.collection;
10 
11 import hunt.Exceptions;
12 import hunt.text.Common;
13 
14 import std.conv;
15 
16 /**
17  * Note:  As of 1.4, the class,
18  * javax.security.auth.x500.X500Principal,
19  * should be used when parsing, generating, and comparing X.500 DNs.
20  * This class contains other useful methods for checking name constraints
21  * and retrieving DNs by keyword.
22  *
23  * <p> X.500 names are used to identify entities, such as those which are
24  * identified by X.509 certificates.  They are world-wide, hierarchical,
25  * and descriptive.  Entities can be identified by attributes, and in
26  * some systems can be searched for according to those attributes.
27  * <p>
28  * The ASN.1 for this is:
29  * <pre>
30  * GeneralName ::= CHOICE {
31  * ....
32  *     directoryName                   [4]     Name,
33  * ....
34  * Name ::= CHOICE {
35  *   RDNSequence }
36  *
37  * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
38  *
39  * RelativeDistinguishedName ::=
40  *   SET OF AttributeTypeAndValue
41  *
42  * AttributeTypeAndValue ::= SEQUENCE {
43  *   type     AttributeType,
44  *   value    AttributeValue }
45  *
46  * AttributeType ::= OBJECT IDENTIFIER
47  *
48  * AttributeValue ::= ANY DEFINED BY AttributeType
49  * ....
50  * DirectoryString ::= CHOICE {
51  *       teletexString           TeletexString (SIZE (1..MAX)),
52  *       printableString         PrintableString (SIZE (1..MAX)),
53  *       universalString         UniversalString (SIZE (1..MAX)),
54  *       utf8String              UTF8String (SIZE (1.. MAX)),
55  *       bmpString               BMPString (SIZE (1..MAX)) }
56  * </pre>
57  * <p>
58  * This specification requires only a subset of the name comparison
59  * functionality specified in the X.500 series of specifications.  The
60  * requirements for conforming implementations are as follows:
61  * <ol TYPE=a>
62  * <li>attribute values encoded in different types (e.g.,
63  *    PrintableString and BMPString) may be assumed to represent
64  *    different strings;
65  * <p>
66  * <li>attribute values in types other than PrintableString are case
67  *    sensitive (this permits matching of attribute values as binary
68  *    objects);
69  * <p>
70  * <li>attribute values in PrintableString are not case sensitive
71  *    (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
72  * <p>
73  * <li>attribute values in PrintableString are compared after
74  *    removing leading and trailing white space and converting internal
75  *    substrings of one or more consecutive white space characters to a
76  *    single space.
77  * </ol>
78  * <p>
79  * These name comparison rules permit a certificate user to validate
80  * certificates issued using languages or encodings unfamiliar to the
81  * certificate user.
82  * <p>
83  * In addition, implementations of this specification MAY use these
84  * comparison rules to process unfamiliar attribute types for name
85  * chaining. This allows implementations to process certificates with
86  * unfamiliar attributes in the issuer name.
87  * <p>
88  * Note that the comparison rules defined in the X.500 series of
89  * specifications indicate that the character sets used to encode data
90  * in distinguished names are irrelevant.  The characters themselves are
91  * compared without regard to encoding. Implementations of the profile
92  * are permitted to use the comparison algorithm defined in the X.500
93  * series.  Such an implementation will recognize a superset of name
94  * matches recognized by the algorithm specified above.
95  * <p>
96  * Note that instances of this class are immutable.
97  *
98  * @author David Brownell
99  * @author Amit Kapoor
100  * @author Hemma Prafullchandra
101  * @see GeneralName
102  * @see GeneralNames
103  * @see GeneralNameInterface
104  */
105 
106 class X500Name : GeneralNameInterface, Principal {
107 
108     private string dn; // roughly RFC 1779 DN, or null
109     private string rfc1779Dn; // RFC 1779 compliant DN, or null
110     private string rfc2253Dn; // RFC 2253 DN, or null
111     private string canonicalDn; // canonical RFC 2253 DN or null
112     // private RDN[] names;        // RDNs (never null)
113     private X500Principal x500Principal;
114     private byte[] encoded;
115 
116     // cached immutable list of the RDNs and all the AVAs
117     // private List!RDN rdnList;
118     // private List!AVA allAvaList;
119 
120     /**
121      * Constructs a name from a conventionally formatted string, such
122      * as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US".
123      * (RFC 1779, 2253, or 4514 style).
124      *
125      * @param dname the X.500 Distinguished Name
126      */
127     this(string dname) {
128         this(dname, Collections.emptyMap!(string, string)());
129     }
130 
131     /**
132      * Constructs a name from a conventionally formatted string, such
133      * as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US".
134      * (RFC 1779, 2253, or 4514 style).
135      *
136      * @param dname the X.500 Distinguished Name
137      * @param keywordMap an additional keyword/OID map
138      */
139     this(string dname, Map!(string, string) keywordMap) {
140         parseDN(dname, keywordMap);
141     }
142 
143     /**
144      * Constructs a name from a string formatted according to format.
145      * Currently, the formats DEFAULT and RFC2253 are supported.
146      * DEFAULT is the default format used by the X500Name(string)
147      * constructor. RFC2253 is the format strictly according to RFC2253
148      * without extensions.
149      *
150      * @param dname the X.500 Distinguished Name
151      * @param format the specified format of the string DN
152      */
153     this(string dname, string format) {
154         if (dname is null) {
155             throw new NullPointerException("Name must not be null");
156         }
157 
158         implementationMissing();
159         // if (format.equalsIgnoreCase("RFC2253")) {
160         //     parseRFC2253DN(dname);
161         // } else if (format.equalsIgnoreCase("DEFAULT")) {
162         //     parseDN(dname, Collections.emptyMap!(string, string)());
163         // } else {
164         //     throw new IOException("Unsupported format " ~ format);
165         // }
166     }
167 
168     /**
169      * Constructs a name from fields common in enterprise application
170      * environments.
171      *
172      * <P><EM><STRONG>NOTE:</STRONG>  The behaviour when any of
173      * these strings contain characters outside the ASCII range
174      * is unspecified in currently relevant standards.</EM>
175      *
176      * @param commonName common name of a person, e.g. "Vivette Davis"
177      * @param organizationUnit small organization name, e.g. "Purchasing"
178      * @param organizationName large organization name, e.g. "Onizuka, Inc."
179      * @param country two letter country code, e.g. "CH"
180      */
181     this(string commonName, string organizationUnit,
182                      string organizationName, string country)
183     {
184         implementationMissing();
185         // names = new RDN[4];
186         // /*
187         //  * NOTE:  it's only on output that little-endian
188         //  * ordering is used.
189         //  */
190         // names[3] = new RDN(1);
191         // names[3].assertion[0] = new AVA(commonName_oid,
192         //         new DerValue(commonName));
193         // names[2] = new RDN(1);
194         // names[2].assertion[0] = new AVA(orgUnitName_oid,
195         //         new DerValue(organizationUnit));
196         // names[1] = new RDN(1);
197         // names[1].assertion[0] = new AVA(orgName_oid,
198         //         new DerValue(organizationName));
199         // names[0] = new RDN(1);
200         // names[0].assertion[0] = new AVA(countryName_oid,
201         //         new DerValue(country));
202     }
203 
204     /**
205      * Constructs a name from fields common in Internet application
206      * environments.
207      *
208      * <P><EM><STRONG>NOTE:</STRONG>  The behaviour when any of
209      * these strings contain characters outside the ASCII range
210      * is unspecified in currently relevant standards.</EM>
211      *
212      * @param commonName common name of a person, e.g. "Vivette Davis"
213      * @param organizationUnit small organization name, e.g. "Purchasing"
214      * @param organizationName large organization name, e.g. "Onizuka, Inc."
215      * @param localityName locality (city) name, e.g. "Palo Alto"
216      * @param stateName state name, e.g. "California"
217      * @param country two letter country code, e.g. "CH"
218      */
219     this(string commonName, string organizationUnit,
220                     string organizationName, string localityName,
221                     string stateName, string country)
222     {
223 
224         implementationMissing();
225         // names = new RDN[6];
226         // /*
227         //  * NOTE:  it's only on output that little-endian
228         //  * ordering is used.
229         //  */
230         // names[5] = new RDN(1);
231         // names[5].assertion[0] = new AVA(commonName_oid,
232         //         new DerValue(commonName));
233         // names[4] = new RDN(1);
234         // names[4].assertion[0] = new AVA(orgUnitName_oid,
235         //         new DerValue(organizationUnit));
236         // names[3] = new RDN(1);
237         // names[3].assertion[0] = new AVA(orgName_oid,
238         //         new DerValue(organizationName));
239         // names[2] = new RDN(1);
240         // names[2].assertion[0] = new AVA(localityName_oid,
241         //         new DerValue(localityName));
242         // names[1] = new RDN(1);
243         // names[1].assertion[0] = new AVA(stateName_oid,
244         //         new DerValue(stateName));
245         // names[0] = new RDN(1);
246         // names[0].assertion[0] = new AVA(countryName_oid,
247         //         new DerValue(country));
248     }
249 
250     /**
251      * Constructs a name from an array of relative distinguished names
252      *
253      * @param rdnArray array of relative distinguished names
254      * @on error
255      */
256     // this(RDN[] rdnArray) {
257     //     if (rdnArray is null) {
258     //         names = new RDN[0];
259     //     } else {
260     //         names = rdnArray.clone();
261     //         for (int i = 0; i < names.length; i++) {
262     //             if (names[i] is null) {
263     //                 throw new IOException("Cannot create an X500Name");
264     //             }
265     //         }
266     //     }
267     // }
268 
269     /**
270      * Constructs a name from an ASN.1 encoded value.  The encoding
271      * of the name in the stream uses DER (a BER/1 subset).
272      *
273      * @param value a DER-encoded value holding an X.500 name.
274      */
275     this(DerValue value) {
276         //Note that toDerInputStream uses only the buffer (data) and not
277         //the tag, so an empty SEQUENCE (OF) will yield an empty DerInputStream
278         // this(value.toDerInputStream());
279 
280         implementationMissing();
281     }
282 
283     /**
284      * Constructs a name from an ASN.1 encoded input stream.  The encoding
285      * of the name in the stream uses DER (a BER/1 subset).
286      *
287      * @param in DER-encoded data holding an X.500 name.
288      */
289     // this(DerInputStream input) {
290     //     parseDER(input);
291     // }
292 
293     /**
294      *  Constructs a name from an ASN.1 encoded byte array.
295      *
296      * @param name DER-encoded byte array holding an X.500 name.
297      */
298     this(byte[] name) {
299         // DerInputStream input = new DerInputStream(name);
300         // parseDER(input);
301         implementationMissing();
302     }
303 
304     /**
305      * Return an immutable List of all RDNs in this X500Name.
306      */
307     // List!RDN rdns() {
308     //     List!RDN list = rdnList;
309     //     if (list is null) {
310     //         list = Collections.unmodifiableList(Arrays.asList(names));
311     //         rdnList = list;
312     //     }
313     //     return list;
314     // }
315 
316     /**
317      * Return the number of RDNs in this X500Name.
318      */
319     int size() {
320         // return cast(int)names.length;
321         implementationMissing();
322         return 0;
323     }
324 
325     /**
326      * Return an immutable List of the the AVAs contained in all the
327      * RDNs of this X500Name.
328      */
329     // List!AVA allAvas() {
330     //     List!AVA list = allAvaList;
331     //     if (list is null) {
332     //         list = new ArrayList!AVA();
333     //         for (int i = 0; i < names.length; i++) {
334     //             list.addAll(names[i].avas());
335     //         }
336     //         list = Collections.unmodifiableList(list);
337     //         allAvaList = list;
338     //     }
339     //     return list;
340     // }
341 
342     /**
343      * Return the total number of AVAs contained in all the RDNs of
344      * this X500Name.
345      */
346     int avaSize() {
347         // return allAvas().size();
348         implementationMissing();
349         return 0;
350     }
351 
352     /**
353      * Return whether this X500Name is empty. An X500Name is not empty
354      * if it has at least one RDN containing at least one AVA.
355      */
356     bool isEmpty() {
357 
358         implementationMissing();
359         // int n = names.length;
360         // for (int i = 0; i < n; i++) {
361         //     if (names[i].assertion.length != 0) {
362         //         return false;
363         //     }
364         // }
365         return true;
366     }
367 
368     /**
369      * Calculates a hash code value for the object.  Objects
370      * which are equal will also have the same hashcode.
371      */
372     override size_t toHash() @trusted nothrow {
373         try
374         {
375             string s = getRFC2253CanonicalName();
376             return hashOf(s);
377         }
378         catch(Exception ex)
379         {
380             return super.toHash();
381         }
382     }
383 
384     /**
385      * Compares this name with another, for equality.
386      *
387      * @return true iff the names are identical.
388      */
389     override bool opEquals(Object obj) {
390         if (this is obj) {
391             return true;
392         }
393         if (typeid(obj) != typeid(X500Name)) {
394             return false;
395         }
396 
397         implementationMissing();
398         return false;
399         // X500Name other = cast(X500Name)obj;
400         // // if we already have the canonical forms, compare now
401         // if ((this.canonicalDn !is null) && (other.canonicalDn !is null)) {
402         //     return this.canonicalDn.equals(other.canonicalDn);
403         // }
404         // // quick check that number of RDNs and AVAs match before canonicalizing
405         // int n = this.names.length;
406         // if (n != other.names.length) {
407         //     return false;
408         // }
409         // for (int i = 0; i < n; i++) {
410         //     RDN r1 = this.names[i];
411         //     RDN r2 = other.names[i];
412         //     if (r1.assertion.length != r2.assertion.length) {
413         //         return false;
414         //     }
415         // }
416         // // definite check via canonical form
417         // string thisCanonical = this.getRFC2253CanonicalName();
418         // string otherCanonical = other.getRFC2253CanonicalName();
419         // return thisCanonical.equals(otherCanonical);
420     }
421 
422     /*
423      * Returns the name component as a Java string, regardless of its
424      * encoding restrictions.
425      */
426     private string getString(DerValue attribute) {
427         if (attribute is null)
428             return null;
429         string  value = attribute.getAsString();
430 
431         if (value is null)
432             throw new IOException("not a DER string encoding, "
433                     ~ attribute.tag.to!string());
434         else
435             return value;
436     }
437 
438     /**
439      * Return type of GeneralName.
440      */
441     int getType() {
442         return (GeneralNameInterface.NAME_DIRECTORY);
443     }
444 
445     /**
446      * Returns a "Country" name component.  If more than one
447      * such attribute exists, the topmost one is returned.
448      *
449      * @return "C=" component of the name, if any.
450      */
451     // string getCountry() {
452     //     DerValue attr = findAttribute(countryName_oid);
453 
454     //     return getString(attr);
455     // }
456 
457 
458     // /**
459     //  * Returns an "Organization" name component.  If more than
460     //  * one such attribute exists, the topmost one is returned.
461     //  *
462     //  * @return "O=" component of the name, if any.
463     //  */
464     // string getOrganization() {
465     //     DerValue attr = findAttribute(orgName_oid);
466 
467     //     return getString(attr);
468     // }
469 
470 
471     // /**
472     //  * Returns an "Organizational Unit" name component.  If more
473     //  * than one such attribute exists, the topmost one is returned.
474     //  *
475     //  * @return "OU=" component of the name, if any.
476     //  */
477     // string getOrganizationalUnit() {
478     //     DerValue attr = findAttribute(orgUnitName_oid);
479 
480     //     return getString(attr);
481     // }
482 
483 
484     // /**
485     //  * Returns a "Common Name" component.  If more than one such
486     //  * attribute exists, the topmost one is returned.
487     //  *
488     //  * @return "CN=" component of the name, if any.
489     //  */
490     // string getCommonName() {
491     //     DerValue attr = findAttribute(commonName_oid);
492 
493     //     return getString(attr);
494     // }
495 
496 
497     // /**
498     //  * Returns a "Locality" name component.  If more than one
499     //  * such component exists, the topmost one is returned.
500     //  *
501     //  * @return "L=" component of the name, if any.
502     //  */
503     // string getLocality() {
504     //     DerValue attr = findAttribute(localityName_oid);
505 
506     //     return getString(attr);
507     // }
508 
509     // /**
510     //  * Returns a "State" name component.  If more than one
511     //  * such component exists, the topmost one is returned.
512     //  *
513     //  * @return "S=" component of the name, if any.
514     //  */
515     // string getState() {
516     //   DerValue attr = findAttribute(stateName_oid);
517 
518     //     return getString(attr);
519     // }
520 
521     // /**
522     //  * Returns a "Domain" name component.  If more than one
523     //  * such component exists, the topmost one is returned.
524     //  *
525     //  * @return "DC=" component of the name, if any.
526     //  */
527     // string getDomain() {
528     //     DerValue attr = findAttribute(DOMAIN_COMPONENT_OID);
529 
530     //     return getString(attr);
531     // }
532 
533     // /**
534     //  * Returns a "DN Qualifier" name component.  If more than one
535     //  * such component exists, the topmost one is returned.
536     //  *
537     //  * @return "DNQ=" component of the name, if any.
538     //  */
539     // string getDNQualifier() {
540     //     DerValue attr = findAttribute(DNQUALIFIER_OID);
541 
542     //     return getString(attr);
543     // }
544 
545     // /**
546     //  * Returns a "Surname" name component.  If more than one
547     //  * such component exists, the topmost one is returned.
548     //  *
549     //  * @return "SURNAME=" component of the name, if any.
550     //  */
551     // string getSurname() {
552     //     DerValue attr = findAttribute(SURNAME_OID);
553 
554     //     return getString(attr);
555     // }
556 
557     // /**
558     //  * Returns a "Given Name" name component.  If more than one
559     //  * such component exists, the topmost one is returned.
560     //  *
561     //  * @return "GIVENNAME=" component of the name, if any.
562     //  */
563     // string getGivenName() {
564     //    DerValue attr = findAttribute(GIVENNAME_OID);
565 
566     //    return getString(attr);
567     // }
568 
569     // /**
570     //  * Returns an "Initials" name component.  If more than one
571     //  * such component exists, the topmost one is returned.
572     //  *
573     //  * @return "INITIALS=" component of the name, if any.
574     //  */
575     // string getInitials() {
576     //     DerValue attr = findAttribute(INITIALS_OID);
577 
578     //     return getString(attr);
579     //  }
580 
581     //  /**
582     //   * Returns a "Generation Qualifier" name component.  If more than one
583     //   * such component exists, the topmost one is returned.
584     //   *
585     //   * @return "GENERATION=" component of the name, if any.
586     //   */
587     // string getGeneration() {
588     //     DerValue attr = findAttribute(GENERATIONQUALIFIER_OID);
589 
590     //     return getString(attr);
591     // }
592 
593     // /**
594     //  * Returns an "IP address" name component.  If more than one
595     //  * such component exists, the topmost one is returned.
596     //  *
597     //  * @return "IP=" component of the name, if any.
598     //  */
599     // string getIP() {
600     //     DerValue attr = findAttribute(ipAddress_oid);
601 
602     //     return getString(attr);
603     // }
604 
605     /**
606      * Returns a string form of the X.500 distinguished name.
607      * The format of the string is from RFC 1779. The returned string
608      * may contain non-standardised keywords for more readability
609      * (keywords from RFCs 1779, 2253, and 3280).
610      */
611     override string toString() {
612         if (dn is null) {
613             generateDN();
614         }
615         return dn;
616     }
617 
618     /**
619      * Returns a string form of the X.500 distinguished name
620      * using the algorithm defined in RFC 1779. Only standard attribute type
621      * keywords defined in RFC 1779 are emitted.
622      */
623     string getRFC1779Name() {
624         return getRFC1779Name(Collections.emptyMap!(string, string)());
625     }
626 
627     /**
628      * Returns a string form of the X.500 distinguished name
629      * using the algorithm defined in RFC 1779. Attribute type
630      * keywords defined in RFC 1779 are emitted, as well as additional
631      * keywords contained in the OID/keyword map.
632      */
633     string getRFC1779Name(Map!(string, string) oidMap) {
634         implementationMissing();
635         // if (oidMap.isEmpty()) {
636         //     // return cached result
637         //     if (rfc1779Dn !is null) {
638         //         return rfc1779Dn;
639         //     } else {
640         //         rfc1779Dn = generateRFC1779DN(oidMap);
641         //         return rfc1779Dn;
642         //     }
643         // }
644         return generateRFC1779DN(oidMap);
645     }
646 
647     /**
648      * Returns a string form of the X.500 distinguished name
649      * using the algorithm defined in RFC 2253. Only standard attribute type
650      * keywords defined in RFC 2253 are emitted.
651      */
652     string getRFC2253Name() {
653         return getRFC2253Name(Collections.emptyMap!(string, string)());
654     }
655 
656     /**
657      * Returns a string form of the X.500 distinguished name
658      * using the algorithm defined in RFC 2253. Attribute type
659      * keywords defined in RFC 2253 are emitted, as well as additional
660      * keywords contained in the OID/keyword map.
661      */
662     string getRFC2253Name(Map!(string, string) oidMap) {
663         /* check for and return cached name */
664         if (oidMap.isEmpty()) {
665             if (rfc2253Dn !is null) {
666                 return rfc2253Dn;
667             } else {
668                 rfc2253Dn = generateRFC2253DN(oidMap);
669                 return rfc2253Dn;
670             }
671         }
672         return generateRFC2253DN(oidMap);
673     }
674 
675     private string generateRFC2253DN(Map!(string, string) oidMap) {
676         implementationMissing();
677         return "";
678         // /*
679         //  * Section 2.1 : if the RDNSequence is an empty sequence
680         //  * the result is the empty or zero length string.
681         //  */
682         // if (names.length == 0) {
683         //     return "";
684         // }
685 
686         // /*
687         //  * 2.1 (continued) : Otherwise, the output consists of the string
688         //  * encodings of each RelativeDistinguishedName in the RDNSequence
689         //  * (according to 2.2), starting with the last element of the sequence
690         //  * and moving backwards toward the first.
691         //  *
692         //  * The encodings of adjoining RelativeDistinguishedNames are separated
693         //  * by a comma character (',' ASCII 44).
694         //  */
695         // StringBuilder fullname = new StringBuilder(48);
696         // for (int i = names.length - 1; i >= 0; i--) {
697         //     if (i < names.length - 1) {
698         //         fullname.append(',');
699         //     }
700         //     fullname.append(names[i].toRFC2253String(oidMap));
701         // }
702         // return fullname.toString();
703     }
704 
705     string getRFC2253CanonicalName() {
706         implementationMissing();
707         return "";
708         // /* check for and return cached name */
709         // if (canonicalDn !is null) {
710         //     return canonicalDn;
711         // }
712         // /*
713         //  * Section 2.1 : if the RDNSequence is an empty sequence
714         //  * the result is the empty or zero length string.
715         //  */
716         // if (names.length == 0) {
717         //     canonicalDn = "";
718         //     return canonicalDn;
719         // }
720 
721         // /*
722         //  * 2.1 (continued) : Otherwise, the output consists of the string
723         //  * encodings of each RelativeDistinguishedName in the RDNSequence
724         //  * (according to 2.2), starting with the last element of the sequence
725         //  * and moving backwards toward the first.
726         //  *
727         //  * The encodings of adjoining RelativeDistinguishedNames are separated
728         //  * by a comma character (',' ASCII 44).
729         //  */
730         // StringBuilder fullname = new StringBuilder(48);
731         // for (int i = names.length - 1; i >= 0; i--) {
732         //     if (i < names.length - 1) {
733         //         fullname.append(',');
734         //     }
735         //     fullname.append(names[i].toRFC2253String(true));
736         // }
737         // canonicalDn = fullname.toString();
738         // return canonicalDn;
739     }
740 
741     /**
742      * Returns the value of toString().  This call is needed to
743      * implement the java.security.Principal interface.
744      */
745     string getName() { return toString(); }
746 
747     /**
748      * Find the first instance of this attribute in a "top down"
749      * search of all the attributes in the name.
750      */
751     // private DerValue findAttribute(ObjectIdentifier attribute) {
752     //     if (names !is null) {
753     //         for (int i = 0; i < names.length; i++) {
754     //             DerValue value = names[i].findAttribute(attribute);
755     //             if (value !is null) {
756     //                 return value;
757     //             }
758     //         }
759     //     }
760     //     return null;
761     // }
762 
763     /**
764      * Find the most specific ("last") attribute of the given
765      * type.
766      */
767     // DerValue findMostSpecificAttribute(ObjectIdentifier attribute) {
768     //     if (names !is null) {
769     //         for (int i = names.length - 1; i >= 0; i--) {
770     //             DerValue value = names[i].findAttribute(attribute);
771     //             if (value !is null) {
772     //                 return value;
773     //             }
774     //         }
775     //     }
776     //     return null;
777     // }
778 
779     /****************************************************************/
780 
781     // private void parseDER(DerInputStream inputStream) {
782     //     //
783     //     // X.500 names are a "SEQUENCE OF" RDNs, which means zero or
784     //     // more and order matters.  We scan them in order, which
785     //     // conventionally is big-endian.
786     //     //
787     //     DerValue[] nameseq = null;
788     //     byte[] derBytes = inputStream.toByteArray();
789 
790     //     try {
791     //         nameseq = inputStream.getSequence(5);
792     //     } catch (IOException ioe) {
793     //         if (derBytes is null) {
794     //             nameseq = null;
795     //         } else {
796     //             DerValue derVal = new DerValue(DerValue.tag_Sequence,
797     //                                        derBytes);
798     //             derBytes = derVal.toByteArray();
799     //             nameseq = new DerInputStream(derBytes).getSequence(5);
800     //         }
801     //     }
802 
803     //     if (nameseq is null) {
804     //         names = new RDN[0];
805     //     } else {
806     //         names = new RDN[nameseq.length];
807     //         for (int i = 0; i < nameseq.length; i++) {
808     //             names[i] = new RDN(nameseq[i]);
809     //         }
810     //     }
811     // }
812 
813     /**
814      * Encodes the name in DER-encoded form.
815      *
816      * @deprecated Use encode() instead
817      * @param out where to put the DER-encoded X.500 name
818      */
819     // void emit(DerOutputStream ot) {
820     //     encode(ot);
821     // }
822 
823     /**
824      * Encodes the name in DER-encoded form.
825      *
826      * @param out where to put the DER-encoded X.500 name
827      */
828     void encode(DerOutputStream ot) {
829         // DerOutputStream tmp = new DerOutputStream();
830         // for (size_t i = 0; i < names.length; i++) {
831         //     names[i].encode(tmp);
832         // }
833         // ot.write(DerValue.tag_Sequence, tmp);
834         implementationMissing();
835     }
836 
837     /**
838      * Returned the encoding as an uncloned byte array. Callers must
839      * guarantee that they neither modify it not expose it to untrusted
840      * code.
841      */
842     byte[] getEncodedInternal() {
843         if (encoded is null) {
844             // DerOutputStream     outStream = new DerOutputStream();
845             // DerOutputStream     tmp = new DerOutputStream();
846             // for (int i = 0; i < names.length; i++) {
847             //     names[i].encode(tmp);
848             // }
849             // outStream.write(DerValue.tag_Sequence, tmp);
850             // encoded = outStream.toByteArray();
851             implementationMissing();
852         }
853         return encoded;
854     }
855 
856     /**
857      * Gets the name in DER-encoded form.
858      *
859      * @return the DER encoded byte array of this name.
860      */
861     byte[] getEncoded() {
862         return getEncodedInternal().dup;
863     }
864 
865     /*
866      * Parses a Distinguished Name (DN) in printable representation.
867      *
868      * According to RFC 1779, RDNs in a DN are separated by comma.
869      * The following examples show both methods of quoting a comma, so that it
870      * is not considered a separator:
871      *
872      *     O="Sue, Grabbit and Runn" or
873      *     O=Sue\, Grabbit and Runn
874      *
875      * This method can parse RFC 1779, 2253 or 4514 DNs and non-standard 3280
876      * keywords. Additional keywords can be specified in the keyword/OID map.
877      */
878     private void parseDN(string input, Map!(string, string) keywordMap) {
879         // if (input is null || input.length() == 0) {
880         //     names = new RDN[0];
881         //     return;
882         // }
883 
884         // List!RDN dnVector = new ArrayList!RDN();
885         // int dnOffset = 0;
886         // int rdnEnd;
887         // string rdnString;
888         // int quoteCount = 0;
889 
890         // string dnString = input;
891 
892         // int searchOffset = 0;
893         // int nextComma = dnString.indexOf(',');
894         // int nextSemiColon = dnString.indexOf(';');
895         // while (nextComma >=0 || nextSemiColon >=0) {
896 
897         //     if (nextSemiColon < 0) {
898         //         rdnEnd = nextComma;
899         //     } else if (nextComma < 0) {
900         //         rdnEnd = nextSemiColon;
901         //     } else {
902         //         rdnEnd = Math.min(nextComma, nextSemiColon);
903         //     }
904         //     quoteCount += countQuotes(dnString, searchOffset, rdnEnd);
905 
906         //     /*
907         //      * We have encountered an RDN delimiter (comma or a semicolon).
908         //      * If the comma or semicolon in the RDN under consideration is
909         //      * preceded by a backslash (escape), or by a double quote, it
910         //      * is part of the RDN. Otherwise, it is used as a separator, to
911         //      * delimit the RDN under consideration from any subsequent RDNs.
912         //      */
913         //     if (rdnEnd >= 0 && quoteCount != 1 &&
914         //         !escaped(rdnEnd, searchOffset, dnString)) {
915 
916         //         /*
917         //          * Comma/semicolon is a separator
918         //          */
919         //         rdnString = dnString.substring(dnOffset, rdnEnd);
920 
921         //         // Parse RDN, and store it in vector
922         //         RDN rdn = new RDN(rdnString, keywordMap);
923         //         dnVector.add(rdn);
924 
925         //         // Increase the offset
926         //         dnOffset = rdnEnd + 1;
927 
928         //         // Set quote counter back to zero
929         //         quoteCount = 0;
930         //     }
931 
932         //     searchOffset = rdnEnd + 1;
933         //     nextComma = dnString.indexOf(',', searchOffset);
934         //     nextSemiColon = dnString.indexOf(';', searchOffset);
935         // }
936 
937         // // Parse last or only RDN, and store it in vector
938         // rdnString = dnString.substring(dnOffset);
939         // RDN rdn = new RDN(rdnString, keywordMap);
940         // dnVector.add(rdn);
941 
942         // /*
943         //  * Store the vector elements as an array of RDNs
944         //  * NOTE: It's only on output that little-endian ordering is used.
945         //  */
946         // Collections.reverse(dnVector);
947         // names = dnVector.toArray(new RDN[dnVector.size()]);
948         implementationMissing();
949     }
950 
951     private void parseRFC2253DN(string dnString) {
952 
953         implementationMissing();
954         // if (dnString.length() == 0) {
955         //     names = new RDN[0];
956         //     return;
957         //  }
958 
959         //  List!RDN dnVector = new ArrayList<>();
960         //  int dnOffset = 0;
961         //  string rdnString;
962         //  int searchOffset = 0;
963         //  int rdnEnd = dnString.indexOf(',');
964         //  while (rdnEnd >=0) {
965         //      /*
966         //       * We have encountered an RDN delimiter (comma).
967         //       * If the comma in the RDN under consideration is
968         //       * preceded by a backslash (escape), it
969         //       * is part of the RDN. Otherwise, it is used as a separator, to
970         //       * delimit the RDN under consideration from any subsequent RDNs.
971         //       */
972         //      if (rdnEnd > 0 && !escaped(rdnEnd, searchOffset, dnString)) {
973 
974         //          /*
975         //           * Comma is a separator
976         //           */
977         //          rdnString = dnString.substring(dnOffset, rdnEnd);
978 
979         //          // Parse RDN, and store it in vector
980         //          RDN rdn = new RDN(rdnString, "RFC2253");
981         //          dnVector.add(rdn);
982 
983         //          // Increase the offset
984         //          dnOffset = rdnEnd + 1;
985         //      }
986 
987         //      searchOffset = rdnEnd + 1;
988         //      rdnEnd = dnString.indexOf(',', searchOffset);
989         //  }
990 
991         //  // Parse last or only RDN, and store it in vector
992         //  rdnString = dnString.substring(dnOffset);
993         //  RDN rdn = new RDN(rdnString, "RFC2253");
994         //  dnVector.add(rdn);
995 
996         //  /*
997         //   * Store the vector elements as an array of RDNs
998         //   * NOTE: It's only on output that little-endian ordering is used.
999         //   */
1000         //  Collections.reverse(dnVector);
1001         //  names = dnVector.toArray(new RDN[dnVector.size()]);
1002     }
1003 
1004     /*
1005      * Counts double quotes in string.
1006      * Escaped quotes are ignored.
1007      */
1008     static int countQuotes(string string, int from, int to) {
1009         int count = 0;
1010 
1011         for (int i = from; i < to; i++) {
1012             if ((string.charAt(i) == '"' && i == from) ||
1013                 (string.charAt(i) == '"' && string.charAt(i-1) != '\\')) {
1014                 count++;
1015             }
1016         }
1017 
1018         return count;
1019     }
1020 
1021     private static bool escaped
1022                 (int rdnEnd, int searchOffset, string dnString) {
1023 
1024         if (rdnEnd == 1 && dnString.charAt(rdnEnd - 1) == '\\') {
1025 
1026             //  case 1:
1027             //  \,
1028 
1029             return true;
1030 
1031         } else if (rdnEnd > 1 && dnString.charAt(rdnEnd - 1) == '\\' &&
1032                 dnString.charAt(rdnEnd - 2) != '\\') {
1033 
1034             //  case 2:
1035             //  foo\,
1036 
1037             return true;
1038 
1039         } else if (rdnEnd > 1 && dnString.charAt(rdnEnd - 1) == '\\' &&
1040                 dnString.charAt(rdnEnd - 2) == '\\') {
1041 
1042             //  case 3:
1043             //  foo\\\\\,
1044 
1045             int count = 0;
1046             rdnEnd--;   // back up to last backSlash
1047             while (rdnEnd >= searchOffset) {
1048                 if (dnString.charAt(rdnEnd) == '\\') {
1049                     count++;    // count consecutive backslashes
1050                 }
1051                 rdnEnd--;
1052             }
1053 
1054             // if count is odd, then rdnEnd is escaped
1055             return (count % 2) != 0 ? true : false;
1056 
1057         } else {
1058             return false;
1059         }
1060     }
1061 
1062     /*
1063      * Dump the printable form of a distinguished name.  Each relative
1064      * name is separated from the next by a ",", and assertions in the
1065      * relative names have "label=value" syntax.
1066      *
1067      * Uses RFC 1779 syntax (i.e. little-endian, comma separators)
1068      */
1069     private void generateDN() {
1070         implementationMissing();
1071         // if (names.length == 1) {
1072         //     dn = names[0].toString();
1073         //     return;
1074         // }
1075 
1076         // StringBuilder sb = new StringBuilder(48);
1077         // if (names !is null) {
1078         //     for (int i = names.length - 1; i >= 0; i--) {
1079         //         if (i != names.length - 1) {
1080         //             sb.append(", ");
1081         //         }
1082         //         sb.append(names[i].toString());
1083         //     }
1084         // }
1085         // dn = sb.toString();
1086     }
1087 
1088     /*
1089      * Dump the printable form of a distinguished name.  Each relative
1090      * name is separated from the next by a ",", and assertions in the
1091      * relative names have "label=value" syntax.
1092      *
1093      * Uses RFC 1779 syntax (i.e. little-endian, comma separators)
1094      * Valid keywords from RFC 1779 are used. Additional keywords can be
1095      * specified in the OID/keyword map.
1096      */
1097     private string generateRFC1779DN(Map!(string, string) oidMap) {
1098         // if (names.length == 1) {
1099         //     return names[0].toRFC1779String(oidMap);
1100         // }
1101 
1102         // StringBuilder sb = new StringBuilder(48);
1103         // if (names !is null) {
1104         //     for (int i = names.length - 1; i >= 0; i--) {
1105         //         if (i != names.length - 1) {
1106         //             sb.append(", ");
1107         //         }
1108         //         sb.append(names[i].toRFC1779String(oidMap));
1109         //     }
1110         // }
1111         // return sb.toString();
1112         implementationMissing();
1113         return "";
1114     }
1115 
1116     /****************************************************************/
1117 
1118     /*
1119      * Maybe return a preallocated OID, to reduce storage costs
1120      * and speed recognition of common X.500 attributes.
1121      */
1122     // static ObjectIdentifier intern(ObjectIdentifier oid) {
1123     //     ObjectIdentifier interned = internedOIDs.putIfAbsent(oid, oid);
1124     //     return (interned is null) ? oid : interned;
1125     // }
1126 
1127     // private __gshared Map!(ObjectIdentifier,ObjectIdentifier) internedOIDs;  
1128 
1129     /*
1130      * Selected OIDs from X.520
1131      * Includes all those specified in RFC 3280 as MUST or SHOULD
1132      * be recognized
1133      */
1134     private enum int[] commonName_data = [ 2, 5, 4, 3 ];
1135     private enum int[] SURNAME_DATA = [ 2, 5, 4, 4 ];
1136     private enum int[] SERIALNUMBER_DATA = [ 2, 5, 4, 5 ];
1137     private enum int[] countryName_data = [ 2, 5, 4, 6 ];
1138     private enum int[] localityName_data = [ 2, 5, 4, 7 ];
1139     private enum int[] stateName_data = [ 2, 5, 4, 8 ];
1140     private enum int[] streetAddress_data = [ 2, 5, 4, 9 ];
1141     private enum int[] orgName_data = [ 2, 5, 4, 10 ];
1142     private enum int[] orgUnitName_data = [ 2, 5, 4, 11 ];
1143     private enum int[] title_data = [ 2, 5, 4, 12 ];
1144     private enum int[] GIVENNAME_DATA = [ 2, 5, 4, 42 ];
1145     private enum int[] INITIALS_DATA = [ 2, 5, 4, 43 ];
1146     private enum int[] GENERATIONQUALIFIER_DATA = [ 2, 5, 4, 44 ];
1147     private enum int[] DNQUALIFIER_DATA = [ 2, 5, 4, 46 ];
1148 
1149     private enum int[] ipAddress_data = [ 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 ];
1150     private enum int[] DOMAIN_COMPONENT_DATA =
1151         [ 0, 9, 2342, 19200300, 100, 1, 25 ];
1152     private enum int[] userid_data =
1153         [ 0, 9, 2342, 19200300, 100, 1, 1 ];
1154 
1155 
1156     // __ghsared ObjectIdentifier commonName_oid;
1157     // __ghsared ObjectIdentifier countryName_oid;
1158     // __ghsared ObjectIdentifier localityName_oid;
1159     // __ghsared ObjectIdentifier orgName_oid;
1160     // __ghsared ObjectIdentifier orgUnitName_oid;
1161     // __ghsared ObjectIdentifier stateName_oid;
1162     // __ghsared ObjectIdentifier streetAddress_oid;
1163     // __ghsared ObjectIdentifier title_oid;
1164     // __ghsared ObjectIdentifier DNQUALIFIER_OID;
1165     // __ghsared ObjectIdentifier SURNAME_OID;
1166     // __ghsared ObjectIdentifier GIVENNAME_OID;
1167     // __ghsared ObjectIdentifier INITIALS_OID;
1168     // __ghsared ObjectIdentifier GENERATIONQUALIFIER_OID;
1169     // __ghsared ObjectIdentifier ipAddress_oid;
1170     // __ghsared ObjectIdentifier DOMAIN_COMPONENT_OID;
1171     // __ghsared ObjectIdentifier userid_oid;
1172     // __ghsared ObjectIdentifier SERIALNUMBER_OID;
1173     
1174     // shared static this()
1175     // {
1176     //     internedOIDs = new HashMap!(ObjectIdentifier,ObjectIdentifier)();
1177 
1178     // /** OID for the "CN=" attribute, denoting a person's common name. */
1179     //     commonName_oid = intern(ObjectIdentifier.newInternal(commonName_data));
1180 
1181     // /** OID for the "SERIALNUMBER=" attribute, denoting a serial number for.
1182     //     a name. Do not confuse with PKCS#9 issuerAndSerialNumber or the
1183     //     certificate serial number. */
1184     //     SERIALNUMBER_OID = intern(ObjectIdentifier.newInternal(SERIALNUMBER_DATA));
1185 
1186     // /** OID for the "C=" attribute, denoting a country. */
1187     //     countryName_oid = intern(ObjectIdentifier.newInternal(countryName_data));
1188 
1189     // /** OID for the "L=" attribute, denoting a locality (such as a city) */
1190     //     localityName_oid = intern(ObjectIdentifier.newInternal(localityName_data));
1191 
1192     // /** OID for the "O=" attribute, denoting an organization name */
1193     //     orgName_oid = intern(ObjectIdentifier.newInternal(orgName_data));
1194 
1195     // /** OID for the "OU=" attribute, denoting an organizational unit name */
1196     //     orgUnitName_oid = intern(ObjectIdentifier.newInternal(orgUnitName_data));
1197 
1198     // /** OID for the "S=" attribute, denoting a state (such as Delaware) */
1199     //     stateName_oid = intern(ObjectIdentifier.newInternal(stateName_data));
1200 
1201     // /** OID for the "STREET=" attribute, denoting a street address. */
1202     //     streetAddress_oid = intern(ObjectIdentifier.newInternal(streetAddress_data));
1203 
1204     // /** OID for the "T=" attribute, denoting a person's title. */
1205     //     title_oid = intern(ObjectIdentifier.newInternal(title_data));
1206 
1207     // /** OID for the "DNQUALIFIER=" or "DNQ=" attribute, denoting DN
1208     //     disambiguating information.*/
1209     //     DNQUALIFIER_OID = intern(ObjectIdentifier.newInternal(DNQUALIFIER_DATA));
1210 
1211     // /** OID for the "SURNAME=" attribute, denoting a person's surname.*/
1212     //     SURNAME_OID = intern(ObjectIdentifier.newInternal(SURNAME_DATA));
1213 
1214     // /** OID for the "GIVENNAME=" attribute, denoting a person's given name.*/
1215     //     GIVENNAME_OID = intern(ObjectIdentifier.newInternal(GIVENNAME_DATA));
1216 
1217     // /** OID for the "INITIALS=" attribute, denoting a person's initials.*/
1218     //     INITIALS_OID = intern(ObjectIdentifier.newInternal(INITIALS_DATA));
1219 
1220     // /** OID for the "GENERATION=" attribute, denoting Jr., II, etc.*/
1221     //     GENERATIONQUALIFIER_OID =
1222     //         intern(ObjectIdentifier.newInternal(GENERATIONQUALIFIER_DATA));
1223 
1224     // /*
1225     //  * OIDs from other sources which show up in X.500 names we
1226     //  * expect to deal with often
1227     //  */
1228     // /** OID for "IP=" IP address attributes, used with SKIP. */
1229     //     ipAddress_oid = intern(ObjectIdentifier.newInternal(ipAddress_data));
1230 
1231     // /*
1232     //  * Domain component OID from RFC 1274, RFC 2247, RFC 3280
1233     //  */
1234 
1235     // /*
1236     //  * OID for "DC=" domain component attributes, used with DNS names in DN
1237     //  * format
1238     //  */
1239     //     DOMAIN_COMPONENT_OID =
1240     //         intern(ObjectIdentifier.newInternal(DOMAIN_COMPONENT_DATA));
1241 
1242     // /** OID for "UID=" denoting a user id, defined in RFCs 1274 & 2798. */
1243     //     userid_oid = intern(ObjectIdentifier.newInternal(userid_data));
1244     // }
1245 
1246     /**
1247      * Return constraint type:<ul>
1248      *   <li>NAME_DIFF_TYPE = -1: input name is different type from this name
1249      *       (i.e. does not constrain)
1250      *   <li>NAME_MATCH = 0: input name matches this name
1251      *   <li>NAME_NARROWS = 1: input name narrows this name
1252      *   <li>NAME_WIDENS = 2: input name widens this name
1253      *   <li>NAME_SAME_TYPE = 3: input name does not match or narrow this name,
1254      &       but is same type
1255      * </ul>.  These results are used in checking NameConstraints during
1256      * certification path verification.
1257      *
1258      * @param inputName to be checked for being constrained
1259      * @returns constraint type above
1260      * @throws UnsupportedOperationException if name is not exact match, but
1261      *         narrowing and widening are not supported for this name type.
1262      */
1263     int constrains(GeneralNameInterface inputName) {
1264         int constraintType;
1265         if (inputName is null) {
1266             constraintType = NAME_DIFF_TYPE;
1267         } else if (inputName.getType() != NAME_DIRECTORY) {
1268             constraintType = NAME_DIFF_TYPE;
1269         } else { // type == NAME_DIRECTORY
1270             // X500Name inputX500 = cast(X500Name)inputName;
1271             // if (inputX500 is this) {
1272             //     constraintType = NAME_MATCH;
1273             // } else if (inputX500.names.length == 0) {
1274             //     constraintType = NAME_WIDENS;
1275             // } else if (this.names.length == 0) {
1276             //     constraintType = NAME_NARROWS;
1277             // } else if (inputX500.isWithinSubtree(this)) {
1278             //     constraintType = NAME_NARROWS;
1279             // } else if (isWithinSubtree(inputX500)) {
1280             //     constraintType = NAME_WIDENS;
1281             // } else {
1282             //     constraintType = NAME_SAME_TYPE;
1283             // }
1284             implementationMissing();
1285         }
1286         return constraintType;
1287     }
1288 
1289     /**
1290      * Compares this name with another and determines if
1291      * it is within the subtree of the other. Useful for
1292      * checking against the name constraints extension.
1293      *
1294      * @return true iff this name is within the subtree of other.
1295      */
1296     private bool isWithinSubtree(X500Name other) {
1297         if (this is other) {
1298             return true;
1299         }
1300         if (other is null) {
1301             return false;
1302         }
1303 
1304         // if (other.names.length == 0) {
1305         //     return true;
1306         // }
1307         // if (this.names.length == 0) {
1308         //     return false;
1309         // }
1310         // if (names.length < other.names.length) {
1311         //     return false;
1312         // }
1313         // for (size_t i = 0; i < other.names.length; i++) {
1314         //     if (!names[i].equals(other.names[i])) {
1315         //         return false;
1316         //     }
1317         // }
1318         implementationMissing();
1319         return true;
1320     }
1321 
1322     /**
1323      * Return subtree depth of this name for purposes of determining
1324      * NameConstraints minimum and maximum bounds and for calculating
1325      * path lengths in name subtrees.
1326      *
1327      * @returns distance of name from root
1328      * @throws UnsupportedOperationException if not supported for this name type
1329      */
1330     int subtreeDepth() {
1331         // return cast(int)names.length;
1332         implementationMissing();
1333         return 0;
1334     }
1335 
1336     /**
1337      * Return lowest common ancestor of this name and other name
1338      *
1339      * @param other another X500Name
1340      * @return X500Name of lowest common ancestor; null if none
1341      */
1342     // X500Name commonAncestor(X500Name other) {
1343 
1344     //     if (other is null) {
1345     //         return null;
1346     //     }
1347     //     int otherLen = other.names.length;
1348     //     int thisLen = this.names.length;
1349     //     if (thisLen == 0 || otherLen == 0) {
1350     //         return null;
1351     //     }
1352     //     int minLen = (thisLen < otherLen) ? thisLen: otherLen;
1353 
1354     //     //Compare names from highest RDN down the naming tree
1355     //     //Note that these are stored in RDN[0]...
1356     //     int i=0;
1357     //     for (; i < minLen; i++) {
1358     //         if (!names[i].equals(other.names[i])) {
1359     //             if (i == 0) {
1360     //                 return null;
1361     //             } else {
1362     //                 break;
1363     //             }
1364     //         }
1365     //     }
1366 
1367     //     //Copy matching RDNs into new RDN array
1368     //     RDN[] ancestor = new RDN[i];
1369     //     for (int j=0; j < i; j++) {
1370     //         ancestor[j] = names[j];
1371     //     }
1372 
1373     //     X500Name commonAncestor = null;
1374     //     try {
1375     //         commonAncestor = new X500Name(ancestor);
1376     //     } catch (IOException ioe) {
1377     //         return null;
1378     //     }
1379     //     return commonAncestor;
1380     // }
1381 
1382     /**
1383      * Constructor object for use by asX500Principal().
1384      */
1385     // private static final Constructor<X500Principal> principalConstructor;
1386 
1387     /**
1388      * Field object for use by asX500Name().
1389      */
1390     // private static Field principalField;
1391 
1392     /**
1393      * Retrieve the Constructor and Field we need for reflective access
1394      * and make them accessible.
1395      */
1396     // static this() {
1397     //     PrivilegedExceptionAction!(Object[]) pa =
1398     //             new PrivilegedExceptionAction!(Object[])() {
1399     //         Object[] run() {
1400     //             Class<X500Principal> pClass = X500Principal.class;
1401     //             Class<?>[] args = new Class<?>[] { X500Name.class };
1402     //             Constructor<X500Principal> cons = pClass.getDeclaredConstructor(args);
1403     //             cons.setAccessible(true);
1404     //             Field field = pClass.getDeclaredField("thisX500Name");
1405     //             field.setAccessible(true);
1406     //             return new Object[] {cons, field};
1407     //         }
1408     //     };
1409     //     try {
1410     //         Object[] result = AccessController.doPrivileged(pa);
1411     //         @SuppressWarnings("unchecked")
1412     //         Constructor<X500Principal> constr =
1413     //                 (Constructor<X500Principal>)result[0];
1414     //         principalConstructor = constr;
1415     //         principalField = (Field)result[1];
1416     //     } catch (Exception e) {
1417     //         throw new InternalError("Could not obtain X500Principal access", e);
1418     //     }
1419     // }
1420 
1421     /**
1422      * Get an X500Principal backed by this X500Name.
1423      *
1424      * Note that we are using privileged reflection to access the hidden
1425      * package private constructor in X500Principal.
1426      */
1427     X500Principal asX500Principal() {
1428         if (x500Principal is null) {
1429             // try {
1430             //     Object[] args = cast(Object[])[this];
1431             //     x500Principal = principalConstructor.newInstance(args);
1432             // } catch (Exception e) {
1433             //     throw new RuntimeException("Unexpected exception", e);
1434             // }
1435             implementationMissing();
1436         }
1437         return x500Principal;
1438     }
1439 
1440     /**
1441      * Get the X500Name contained in the given X500Principal.
1442      *
1443      * Note that the X500Name is retrieved using reflection.
1444      */
1445     // static X500Name asX500Name(X500Principal p) {
1446     //     try {
1447     //         X500Name name = cast(X500Name)principalField.get(p);
1448     //         name.x500Principal = p;
1449     //         return name;
1450     //     } catch (Exception e) {
1451     //         throw new RuntimeException("Unexpected exception", e);
1452     //     }
1453     // }
1454 
1455 }