1 module hunt.security.x509.X509CertImpl;
2 
3 import hunt.security.cert.Certificate;
4 import hunt.security.cert.X509Certificate;
5 
6 import hunt.security.Key;
7 import hunt.security.Principal;
8 import hunt.security.Provider;
9 
10 import hunt.security.x500.X500Principal;
11 import hunt.security.x509.AlgorithmId;
12 import hunt.security.x509.CertificateExtensions;
13 import hunt.security.x509.CertificateSerialNumber;
14 import hunt.security.x509.CertificateValidity;
15 import hunt.security.x509.CertificateVersion;
16 import hunt.security.x509.CertificateX509Key;
17 import hunt.security.x509.Extension;
18 import hunt.security.x509.KeyIdentifier;
19 import hunt.security.x509.SerialNumber;
20 import hunt.security.x509.UniqueIdentity;
21 import hunt.security.x509.X509CertInfo;
22 import hunt.security.x509.X509Factory;
23 
24 import hunt.security.util.DerEncoder;
25 import hunt.security.util.DerValue;
26 import hunt.security.util.ObjectIdentifier;
27 
28 import hunt.collection;
29 import hunt.stream;
30 import hunt.Exceptions;
31 import hunt.text.Common;
32 import hunt.util.StringBuilder;
33 
34 import std.conv;
35 import std.datetime;
36 import std.bigint;
37 import std.format;
38 
39 alias BigInteger = BigInt;
40 
41 /**
42  * The X509CertImpl class represents an X.509 certificate. These certificates
43  * are widely used to support authentication and other functionality in
44  * Internet security systems.  Common applications include Privacy Enhanced
45  * Mail (PEM), Transport Layer Security (SSL), code signing for trusted
46  * software distribution, and Secure Electronic Transactions (SET).  There
47  * is a commercial infrastructure ready to manage large scale deployments
48  * of X.509 identity certificates.
49  *
50  * <P>These certificates are managed and vouched for by <em>Certificate
51  * Authorities</em> (CAs).  CAs are services which create certificates by
52  * placing data in the X.509 standard format and then digitally signing
53  * that data.  Such signatures are quite difficult to forge.  CAs act as
54  * trusted third parties, making introductions between agents who have no
55  * direct knowledge of each other.  CA certificates are either signed by
56  * themselves, or by some other CA such as a "root" CA.
57  *
58  * <P>RFC 1422 is very informative, though it does not describe much
59  * of the recent work being done with X.509 certificates.  That includes
60  * a 1996 version (X.509v3) and a variety of enhancements being made to
61  * facilitate an explosion of personal certificates used as "Internet
62  * Drivers' Licences", or with SET for credit card transactions.
63  *
64  * <P>More recent work includes the IETF PKIX Working Group efforts,
65  * especially RFC2459.
66  *
67  * @author Dave Brownell
68  * @author Amit Kapoor
69  * @author Hemma Prafullchandra
70  * @see X509CertInfo
71  */
72 class X509CertImpl : X509Certificate , DerEncoder {
73 
74     // private static final long serialVersionUID = -3457612960190864406L;
75 
76     private enum string DOT = ".";
77     /**
78      * Public attribute names.
79      */
80     enum string NAME = "x509";
81     enum string INFO = X509CertInfo.NAME;
82     enum string ALG_ID = "algorithm";
83     enum string SIGNATURE = "signature";
84     enum string SIGNED_CERT = "signed_cert";
85 
86     /**
87      * The following are defined for ease-of-use. These
88      * are the most frequently retrieved attributes.
89      */
90     // x509.info.subject.dname
91     enum string SUBJECT_DN = NAME ~ DOT ~ INFO ~ DOT ~
92                                X509CertInfo.SUBJECT ~ DOT ~ X509CertInfo.DN_NAME;
93     // x509.info.issuer.dname
94     enum string ISSUER_DN = NAME ~ DOT ~ INFO ~ DOT ~
95                                X509CertInfo.ISSUER ~ DOT ~ X509CertInfo.DN_NAME;
96     // x509.info.serialNumber.number
97     enum string SERIAL_ID = NAME ~ DOT ~ INFO ~ DOT ~
98                                X509CertInfo.SERIAL_NUMBER ~ DOT ~
99                                CertificateSerialNumber.NUMBER;
100     // x509.info.key.value
101     enum string PUBLIC_KEY = NAME ~ DOT ~ INFO ~ DOT ~
102                                X509CertInfo.KEY ~ DOT ~
103                                CertificateX509Key.KEY;
104 
105     // x509.info.version.value
106     enum string VERSION = NAME ~ DOT ~ INFO ~ DOT ~
107                                X509CertInfo.VERSION ~ DOT ~
108                                CertificateVersion.VERSION;
109 
110     // x509.algorithm
111     enum string SIG_ALG = NAME ~ DOT ~ ALG_ID;
112 
113     // x509.signature
114     enum string SIG = NAME ~ DOT ~ SIGNATURE;    
115 
116     // when we sign and decode we set this to true
117     // this is our means to make certificates immutable
118     private bool readOnly = false;
119 
120     // Certificate data, and its envelope
121     private byte[]              signedCert = null;
122     protected X509CertInfo      info = null;
123     protected AlgorithmId       algId = null;
124     protected byte[]            signature = null;
125 
126     // recognized extension OIDS
127     private enum string KEY_USAGE_OID = "2.5.29.15";
128     private enum string EXTENDED_KEY_USAGE_OID = "2.5.29.37";
129     private enum string BASIC_CONSTRAINT_OID = "2.5.29.19";
130     private enum string SUBJECT_ALT_NAME_OID = "2.5.29.17";
131     private enum string ISSUER_ALT_NAME_OID = "2.5.29.18";
132     private enum string AUTH_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.1";
133 
134     // number of standard key usage bits.
135     private enum int NUM_STANDARD_KEY_USAGE = 9;
136 
137     // SubjectAlterntativeNames cache
138     // private Collection<List<?>> subjectAlternativeNames;
139 
140     // IssuerAlternativeNames cache
141     // private Collection<List<?>> issuerAlternativeNames;
142 
143     // ExtendedKeyUsage cache
144     private List!string extKeyUsage;
145 
146     // AuthorityInformationAccess cache
147     // private Set!AccessDescription authInfoAccess;
148 
149     /**
150      * PublicKey that has previously been used to verify
151      * the signature of this certificate. Null if the certificate has not
152      * yet been verified.
153      */
154     private PublicKey verifiedPublicKey;
155     /**
156      * If verifiedPublicKey is not null, name of the provider used to
157      * successfully verify the signature of this certificate, or the
158      * empty string if no provider was explicitly specified.
159      */
160     private string verifiedProvider;
161     /**
162      * If verifiedPublicKey is not null, result of the verification using
163      * verifiedPublicKey and verifiedProvider. If true, verification was
164      * successful, if false, it failed.
165      */
166     private bool verificationResult;
167 
168     /**
169      * Default constructor.
170      */
171     this() { }
172 
173     /**
174      * Unmarshals a certificate from its encoded form, parsing the
175      * encoded bytes.  This form of constructor is used by agents which
176      * need to examine and use certificate contents.  That is, this is
177      * one of the more commonly used constructors.  Note that the buffer
178      * must include only a certificate, and no "garbage" may be left at
179      * the end.  If you need to ignore data at the end of a certificate,
180      * use another constructor.
181      *
182      * @param certData the encoded bytes, with no trailing padding.
183      * @exception CertificateException on parsing and initialization errors.
184      */
185     this(byte[] certData) {
186         try {
187             parse(new DerValue(certData));
188         } catch (IOException e) {
189             signedCert = null;
190             throw new CertificateException("Unable to initialize, " ~ e.msg, e);
191         }
192     }
193 
194     /**
195      * unmarshals an X.509 certificate from an input stream.  If the
196      * certificate is RFC1421 hex-encoded, then it must begin with
197      * the line X509Factory.BEGIN_CERT and end with the line
198      * X509Factory.END_CERT.
199      *
200      * @param in an input stream holding at least one certificate that may
201      *        be either DER-encoded or RFC1421 hex-encoded version of the
202      *        DER-encoded certificate.
203      * @exception CertificateException on parsing and initialization errors.
204      */
205     this(InputStream stream) {
206 
207         DerValue der = null;
208 
209         // BufferedInputStream inBuffered = new BufferedInputStream(stream);
210 
211         // // First try reading stream as HEX-encoded DER-encoded bytes,
212         // // since not mistakable for raw DER
213         // try {
214         //     inBuffered.mark(int.max);
215         //     der = readRFC1421Cert(inBuffered);
216         // } catch (IOException ioe) {
217         //     try {
218         //         // Next, try reading stream as raw DER-encoded bytes
219         //         inBuffered.reset();
220         //         der = new DerValue(inBuffered);
221         //     } catch (IOException ioe1) {
222         //         throw new CertificateException("Input stream must be " ~
223         //                                        "either DER-encoded bytes " ~
224         //                                        "or RFC1421 hex-encoded " ~
225         //                                        "DER-encoded bytes: " ~
226         //                                        ioe1.getMessage(), ioe1);
227         //     }
228         // }
229         // try {
230         //     parse(der);
231         // } catch (IOException ioe) {
232         //     signedCert = null;
233         //     throw new CertificateException("Unable to parse DER value of " ~
234         //                                    "certificate, " ~ ioe, ioe);
235         // }
236         implementationMissing();
237     }
238 
239     /**
240      * read input stream as HEX-encoded DER-encoded bytes
241      *
242      * @param stream InputStream to read
243      * @returns DerValue corresponding to decoded HEX-encoded bytes
244      * @throws IOException if stream can not be interpreted as RFC1421
245      *                     encoded bytes
246      */
247     private DerValue readRFC1421Cert(InputStream stream) {
248         // DerValue der = null;
249         // string line = null;
250         // BufferedReader certBufferedReader =
251         //     new BufferedReader(new InputStreamReader(stream, "ASCII"));
252         // try {
253         //     line = certBufferedReader.readLine();
254         // } catch (IOException ioe1) {
255         //     throw new IOException("Unable to read InputStream: " ~
256         //                           ioe1.getMessage());
257         // }
258         // if (line.equals(X509Factory.BEGIN_CERT)) {
259         //     /* stream appears to be hex-encoded bytes */
260         //     ByteArrayOutputStream decstream = new ByteArrayOutputStream();
261         //     try {
262         //         while ((line = certBufferedReader.readLine()) !is null) {
263         //             if (line.equals(X509Factory.END_CERT)) {
264         //                 der = new DerValue(decstream.toByteArray());
265         //                 break;
266         //             } else {
267         //                 decstream.write(Pem.decode(line));
268         //             }
269         //         }
270         //     } catch (IOException ioe2) {
271         //         throw new IOException("Unable to read InputStream: "
272         //                               + ioe2.getMessage());
273         //     }
274         // } else {
275         //     throw new IOException("InputStream is not RFC1421 hex-encoded " ~
276         //                           "DER bytes");
277         // }
278         // return der;
279                 implementationMissing();
280         return null;
281 
282     }
283 
284     /**
285      * Construct an initialized X509 Certificate. The certificate is stored
286      * in raw form and has to be signed to be useful.
287      *
288      * @params info the X509CertificateInfo which the Certificate is to be
289      *              created from.
290      */
291     this(X509CertInfo certInfo) {
292         this.info = certInfo;
293     }
294 
295     /**
296      * Unmarshal a certificate from its encoded form, parsing a DER value.
297      * This form of constructor is used by agents which need to examine
298      * and use certificate contents.
299      *
300      * @param derVal the der value containing the encoded cert.
301      * @exception CertificateException on parsing and initialization errors.
302      */
303     this(DerValue derVal) {
304         try {
305             parse(derVal);
306         } catch (IOException e) {
307             signedCert = null;
308             throw new CertificateException("Unable to initialize, " ~ e.msg, e);
309         }
310     }
311 
312     /**
313      * Appends the certificate to an output stream.
314      *
315      * @param out an input stream to which the certificate is appended.
316      * @exception CertificateEncodingException on encoding errors.
317      */
318     void encode(OutputStream outputStream)    {
319         if (signedCert is null)
320             throw new CertificateEncodingException(
321                           "Null certificate to encode");
322         try {
323             outputStream.write(signedCert.dup);
324         } catch (IOException e) {
325             throw new CertificateEncodingException(e.toString());
326         }
327     }
328 
329     /**
330      * DER encode this object onto an output stream.
331      * Implements the <code>DerEncoder</code> interface.
332      *
333      * @param outputStream the output stream on which to write the DER encoding.
334      *
335      * @exception IOException on encoding error.
336      */
337     void derEncode(OutputStream outputStream) {
338         if (signedCert is null)
339             throw new IOException("Null certificate to encode");
340         outputStream.write(signedCert.dup);
341     }
342 
343     /**
344      * Returns the encoded form of this certificate. It is
345      * assumed that each certificate type would have only a single
346      * form of encoding; for example, X.509 certificates would
347      * be encoded as ASN.1 DER.
348      *
349      * @exception CertificateEncodingException if an encoding error occurs.
350      */
351     override byte[] getEncoded() {
352         return getEncodedInternal().dup;
353     }
354 
355     /**
356      * Returned the encoding as an uncloned byte array. Callers must
357      * guarantee that they neither modify it nor expose it to untrusted
358      * code.
359      */
360     byte[] getEncodedInternal() {
361         if (signedCert is null) {
362             throw new CertificateEncodingException(
363                           "Null certificate to encode");
364         }
365         return signedCert;
366     }
367 
368     /**
369      * Throws an exception if the certificate was not signed using the
370      * verification key provided.  Successfully verifying a certificate
371      * does <em>not</em> indicate that one should trust the entity which
372      * it represents.
373      *
374      * @param key the key used for verification.
375      *
376      * @exception InvalidKeyException on incorrect key.
377      * @exception NoSuchAlgorithmException on unsupported signature
378      * algorithms.
379      * @exception NoSuchProviderException if there's no default provider.
380      * @exception SignatureException on signature errors.
381      * @exception CertificateException on encoding errors.
382      */
383     override void verify(PublicKey key){
384         verify(key, "");
385     }
386 
387     /**
388      * Throws an exception if the certificate was not signed using the
389      * verification key provided.  Successfully verifying a certificate
390      * does <em>not</em> indicate that one should trust the entity which
391      * it represents.
392      *
393      * @param key the key used for verification.
394      * @param sigProvider the name of the provider.
395      *
396      * @exception NoSuchAlgorithmException on unsupported signature
397      * algorithms.
398      * @exception InvalidKeyException on incorrect key.
399      * @exception NoSuchProviderException on incorrect provider.
400      * @exception SignatureException on signature errors.
401      * @exception CertificateException on encoding errors.
402      */
403     override void verify(PublicKey key, string sigProvider) {
404         // if (sigProvider is null) {
405         //     sigProvider = "";
406         // }
407         // if ((verifiedPublicKey !is null) && verifiedPublicKey.equals(key)) {
408         //     // this certificate has already been verified using
409         //     // this key. Make sure providers match, too.
410         //     if (sigProvider.equals(verifiedProvider)) {
411         //         if (verificationResult) {
412         //             return;
413         //         } else {
414         //             throw new SignatureException("Signature does not match.");
415         //         }
416         //     }
417         // }
418         // if (signedCert is null) {
419         //     throw new CertificateEncodingException("Uninitialized certificate");
420         // }
421         // // Verify the signature ...
422         // Signature sigVerf = null;
423         // if (sigProvider.length() == 0) {
424         //     sigVerf = Signature.getInstance(algId.getName());
425         // } else {
426         //     sigVerf = Signature.getInstance(algId.getName(), sigProvider);
427         // }
428         // sigVerf.initVerify(key);
429 
430         // byte[] rawCert = info.getEncodedInfo();
431         // sigVerf.update(rawCert, 0, rawCert.length);
432 
433         // // verify may throw SignatureException for invalid encodings, etc.
434         // verificationResult = sigVerf.verify(signature);
435         // verifiedPublicKey = key;
436         // verifiedProvider = sigProvider;
437 
438         // if (verificationResult == false) {
439         //     throw new SignatureException("Signature does not match.");
440         // }
441         implementationMissing();
442     }
443 
444     /**
445      * Throws an exception if the certificate was not signed using the
446      * verification key provided.  This method uses the signature verification
447      * engine supplied by the specified provider. Note that the specified
448      * Provider object does not have to be registered in the provider list.
449      * Successfully verifying a certificate does <em>not</em> indicate that one
450      * should trust the entity which it represents.
451      *
452      * @param key the key used for verification.
453      * @param sigProvider the provider.
454      *
455      * @exception NoSuchAlgorithmException on unsupported signature
456      * algorithms.
457      * @exception InvalidKeyException on incorrect key.
458      * @exception SignatureException on signature errors.
459      * @exception CertificateException on encoding errors.
460      */
461     override void verify(PublicKey key, Provider sigProvider){
462         // if (signedCert is null) {
463         //     throw new CertificateEncodingException("Uninitialized certificate");
464         // }
465         // // Verify the signature ...
466         // Signature sigVerf = null;
467         // if (sigProvider is null) {
468         //     sigVerf = Signature.getInstance(algId.getName());
469         // } else {
470         //     sigVerf = Signature.getInstance(algId.getName(), sigProvider);
471         // }
472         // sigVerf.initVerify(key);
473 
474         // byte[] rawCert = info.getEncodedInfo();
475         // sigVerf.update(rawCert, 0, rawCert.length);
476 
477         // // verify may throw SignatureException for invalid encodings, etc.
478         // verificationResult = sigVerf.verify(signature);
479         // verifiedPublicKey = key;
480 
481         // if (verificationResult == false) {
482         //     throw new SignatureException("Signature does not match.");
483         // }
484         implementationMissing();
485     }
486 
487      /**
488      * This static method is the default implementation of the
489      * verify(PublicKey key, Provider sigProvider) method in X509Certificate.
490      * Called from java.security.cert.X509Certificate.verify(PublicKey key,
491      * Provider sigProvider)
492      */
493     static void verify(X509Certificate cert, PublicKey key,
494             Provider sigProvider) {
495         cert.verify(key, sigProvider);
496     }
497 
498     /**
499      * Creates an X.509 certificate, and signs it using the given key
500      * (associating a signature algorithm and an X.500 name).
501      * This operation is used to implement the certificate generation
502      * functionality of a certificate authority.
503      *
504      * @param key the private key used for signing.
505      * @param algorithm the name of the signature algorithm used.
506      *
507      * @exception InvalidKeyException on incorrect key.
508      * @exception NoSuchAlgorithmException on unsupported signature
509      * algorithms.
510      * @exception NoSuchProviderException if there's no default provider.
511      * @exception SignatureException on signature errors.
512      * @exception CertificateException on encoding errors.
513      */
514     void sign(PrivateKey key, string algorithm) {
515         sign(key, algorithm, null);
516     }
517 
518     /**
519      * Creates an X.509 certificate, and signs it using the given key
520      * (associating a signature algorithm and an X.500 name).
521      * This operation is used to implement the certificate generation
522      * functionality of a certificate authority.
523      *
524      * @param key the private key used for signing.
525      * @param algorithm the name of the signature algorithm used.
526      * @param provider the name of the provider.
527      *
528      * @exception NoSuchAlgorithmException on unsupported signature
529      * algorithms.
530      * @exception InvalidKeyException on incorrect key.
531      * @exception NoSuchProviderException on incorrect provider.
532      * @exception SignatureException on signature errors.
533      * @exception CertificateException on encoding errors.
534      */
535     void sign(PrivateKey key, string algorithm, string provider) {
536     //     try {
537     //         if (readOnly)
538     //             throw new CertificateEncodingException(
539     //                           "cannot over-write existing certificate");
540     //         Signature sigEngine = null;
541     //         if ((provider is null) || (provider.length() == 0))
542     //             sigEngine = Signature.getInstance(algorithm);
543     //         else
544     //             sigEngine = Signature.getInstance(algorithm, provider);
545 
546     //         sigEngine.initSign(key);
547 
548     //                             // in case the name is reset
549     //         algId = AlgorithmId.get(sigEngine.getAlgorithm());
550 
551     //         DerOutputStream outputStream = new DerOutputStream();
552     //         DerOutputStream tmp = new DerOutputStream();
553 
554     //         // encode certificate info
555     //         info.encode(tmp);
556     //         byte[] rawCert = tmp.toByteArray();
557 
558     //         // encode algorithm identifier
559     //         algId.encode(tmp);
560 
561     //         // Create and encode the signature itself.
562     //         sigEngine.update(rawCert, 0, rawCert.length);
563     //         signature = sigEngine.sign();
564     //         tmp.putBitString(signature);
565 
566     //         // Wrap the signed data in a SEQUENCE { data, algorithm, sig }
567     //         outputStream.write(DerValue.tag_Sequence, tmp);
568     //         signedCert = outputStream.toByteArray();
569     //         readOnly = true;
570 
571     //     } catch (IOException e) {
572     //         throw new CertificateEncodingException(e.toString());
573     //   }
574     implementationMissing();
575     }
576 
577     /**
578      * Checks that the certificate is currently valid, i.e. the current
579      * time is within the specified validity period.
580      *
581      * @exception CertificateExpiredException if the certificate has expired.
582      * @exception CertificateNotYetValidException if the certificate is not
583      * yet valid.
584      */
585     override void checkValidity() {
586         Date date =  cast(Date)Clock.currTime;
587         checkValidity(date);
588     }
589 
590     /**
591      * Checks that the specified date is within the certificate's
592      * validity period, or basically if the certificate would be
593      * valid at the specified date/time.
594      *
595      * @param date the Date to check against to see if this certificate
596      *        is valid at that date/time.
597      *
598      * @exception CertificateExpiredException if the certificate has expired
599      * with respect to the <code>date</code> supplied.
600      * @exception CertificateNotYetValidException if the certificate is not
601      * yet valid with respect to the <code>date</code> supplied.
602      */
603     override void checkValidity(Date date) {
604         CertificateValidity interval = null;
605         try {
606             interval = cast(CertificateValidity)info.get(CertificateValidity.NAME);
607         } catch (Exception e) {
608             throw new CertificateNotYetValidException("Incorrect validity period");
609         }
610         if (interval is null)
611             throw new CertificateNotYetValidException("Null validity period");
612         interval.valid(date);
613     }
614 
615     /**
616      * Return the requested attribute from the certificate.
617      *
618      * Note that the X509CertInfo is not cloned for performance reasons.
619      * Callers must ensure that they do not modify it. All other
620      * attributes are cloned.
621      *
622      * @param name the name of the attribute.
623      * @exception CertificateParsingException on invalid attribute identifier.
624      */
625     Object get(string name)
626     {
627         // X509AttributeName attr = new X509AttributeName(name);
628         // string id = attr.getPrefix();
629         // if (!(id.equalsIgnoreCase(NAME))) {
630         //     throw new CertificateParsingException("Invalid root of "
631         //                   ~ "attribute name, expected [" ~ NAME +
632         //                   "], received " ~ "[" ~ id ~ "]");
633         // }
634         // attr = new X509AttributeName(attr.getSuffix());
635         // id = attr.getPrefix();
636 
637         // if (id.equalsIgnoreCase(INFO)) {
638         //     if (info is null) {
639         //         return null;
640         //     }
641         //     if (attr.getSuffix() !is null) {
642         //         try {
643         //             return info.get(attr.getSuffix());
644         //         } catch (IOException e) {
645         //             throw new CertificateParsingException(e.toString());
646         //         } catch (CertificateException e) {
647         //             throw new CertificateParsingException(e.toString());
648         //         }
649         //     } else {
650         //         return info;
651         //     }
652         // } else if (id.equalsIgnoreCase(ALG_ID)) {
653         //     return(algId);
654         // } else if (id.equalsIgnoreCase(SIGNATURE)) {
655         //     if (signature !is null)
656         //         return signature.dup;
657         //     else
658         //         return null;
659         // } else if (id.equalsIgnoreCase(SIGNED_CERT)) {
660         //     if (signedCert !is null)
661         //         return signedCert.dup;
662         //     else
663         //         return null;
664         // } else {
665         //     throw new CertificateParsingException("Attribute name not "
666         //          ~ "recognized or get() not allowed for the same: " ~ id);
667         // }
668                 implementationMissing();
669         return null;
670 
671     }
672 
673     /**
674      * Set the requested attribute in the certificate.
675      *
676      * @param name the name of the attribute.
677      * @param obj the value of the attribute.
678      * @exception CertificateException on invalid attribute identifier.
679      * @exception IOException on encoding error of attribute.
680      */
681     void set(string name, Object obj) {
682         // check if immutable
683         if (readOnly)
684             throw new CertificateException("cannot over-write existing"
685                                            ~ " certificate");
686 
687         // X509AttributeName attr = new X509AttributeName(name);
688         // string id = attr.getPrefix();
689         // if (!(id.equalsIgnoreCase(NAME))) {
690         //     throw new CertificateException("Invalid root of attribute name,"
691         //                    ~ " expected [" ~ NAME ~ "], received " ~ id);
692         // }
693         // attr = new X509AttributeName(attr.getSuffix());
694         // id = attr.getPrefix();
695 
696         // if (id.equalsIgnoreCase(INFO)) {
697         //     if (attr.getSuffix() is null) {
698         //         if (!(obj instanceof X509CertInfo)) {
699         //             throw new CertificateException("Attribute value should"
700         //                             ~ " be of type X509CertInfo.");
701         //         }
702         //         info = (X509CertInfo)obj;
703         //         signedCert = null;  //reset this as certificate data has changed
704         //     } else {
705         //         info.set(attr.getSuffix(), obj);
706         //         signedCert = null;  //reset this as certificate data has changed
707         //     }
708         // } else {
709         //     throw new CertificateException("Attribute name not recognized or " ~
710         //                       "set() not allowed for the same: " ~ id);
711         // }
712         implementationMissing();
713     }
714 
715     /**
716      * Delete the requested attribute from the certificate.
717      *
718      * @param name the name of the attribute.
719      * @exception CertificateException on invalid attribute identifier.
720      * @exception IOException on other errors.
721      */
722     void remove(string name) {
723         // check if immutable
724         // if (readOnly)
725         //     throw new CertificateException("cannot over-write existing"
726         //                                    ~ " certificate");
727 
728         // X509AttributeName attr = new X509AttributeName(name);
729         // string id = attr.getPrefix();
730         // if (!(id.equalsIgnoreCase(NAME))) {
731         //     throw new CertificateException("Invalid root of attribute name,"
732         //                            ~ " expected ["
733         //                            + NAME ~ "], received " ~ id);
734         // }
735         // attr = new X509AttributeName(attr.getSuffix());
736         // id = attr.getPrefix();
737 
738         // if (id.equalsIgnoreCase(INFO)) {
739         //     if (attr.getSuffix() !is null) {
740         //         info = null;
741         //     } else {
742         //         info.remove(attr.getSuffix());
743         //     }
744         // } else if (id.equalsIgnoreCase(ALG_ID)) {
745         //     algId = null;
746         // } else if (id.equalsIgnoreCase(SIGNATURE)) {
747         //     signature = null;
748         // } else if (id.equalsIgnoreCase(SIGNED_CERT)) {
749         //     signedCert = null;
750         // } else {
751         //     throw new CertificateException("Attribute name not recognized or " ~
752         //                       "remove() not allowed for the same: " ~ id);
753         // }
754         implementationMissing();
755     }
756 
757     /**
758      * Return an enumeration of names of attributes existing within this
759      * attribute.
760      */
761     Enumeration!string getElements() {
762         // AttributeNameEnumeration elements = new AttributeNameEnumeration();
763         // elements.addElement(NAME ~ DOT ~ INFO);
764         // elements.addElement(NAME ~ DOT ~ ALG_ID);
765         // elements.addElement(NAME ~ DOT ~ SIGNATURE);
766         // elements.addElement(NAME ~ DOT ~ SIGNED_CERT);
767 
768         // return elements.elements();
769         implementationMissing();
770         return null;
771     }
772 
773     /**
774      * Return the name of this attribute.
775      */
776     string getName() {
777         return(NAME);
778     }
779 
780     /**
781      * Returns a printable representation of the certificate.  This does not
782      * contain all the information available to distinguish this from any
783      * other certificate.  The certificate must be fully constructed
784      * before this function may be called.
785      */
786     override string toString() {
787         if (info is null || algId is null || signature is null)
788             return "";
789 
790         StringBuilder sb = new StringBuilder();
791 
792         sb.append("[\n");
793         sb.append(info.toString() ~ "\n");
794         sb.append("  Algorithm: [" ~ algId.to!string() ~ "]\n");
795 
796         // HexDumpEncoder encoder = new HexDumpEncoder();
797         sb.append("  Signature:\n" ~ format("%(%02X%)", signature));
798         sb.append("\n]");
799 
800         return sb.toString();
801     }
802 
803     // the strongly typed gets, as per java.security.cert.X509Certificate
804 
805     /**
806      * Gets the publickey from this certificate.
807      *
808      * @return the publickey.
809      */
810     override PublicKey getPublicKey() {
811         if (info is null)
812             return null;
813         try {
814             PublicKey key = cast(PublicKey)info.get(CertificateX509Key.NAME
815                                 ~ DOT ~ CertificateX509Key.KEY);
816             return key;
817         } catch (Exception e) {
818             return null;
819         }
820     }
821 
822     /**
823      * Gets the version number from the certificate.
824      *
825      * @return the version number, i.e. 1, 2 or 3.
826      */
827     override int getVersion() {
828         if (info is null)
829             return -1;
830         try {
831             // int vers = ((Integer)info.get(CertificateVersion.NAME
832             //             ~ DOT ~ CertificateVersion.VERSION)).intValue();
833             // return vers+1;
834             implementationMissing();
835             return 0;
836         } catch (Exception e) {
837             return -1;
838         }
839     }
840 
841     /**
842      * Gets the serial number from the certificate.
843      *
844      * @return the serial number.
845      */
846     override BigInteger getSerialNumber() {
847         SerialNumber ser = getSerialNumberObject();
848 
849         return ser !is null ? ser.getNumber() : BigInteger.init;
850     }
851 
852     /**
853      * Gets the serial number from the certificate as
854      * a SerialNumber object.
855      *
856      * @return the serial number.
857      */
858     SerialNumber getSerialNumberObject() {
859         if (info is null)
860             return null;
861         try {
862             SerialNumber ser = cast(SerialNumber)info.get(
863                               CertificateSerialNumber.NAME ~ DOT ~
864                               CertificateSerialNumber.NUMBER);
865            return ser;
866         } catch (Exception e) {
867             return null;
868         }
869     }
870 
871 
872     /**
873      * Gets the subject distinguished name from the certificate.
874      *
875      * @return the subject name.
876      */
877     override Principal getSubjectDN() {
878         if (info is null)
879             return null;
880         try {
881             Principal subject = cast(Principal)info.get(X509CertInfo.SUBJECT ~ DOT ~
882                                                     X509CertInfo.DN_NAME);
883             return subject;
884         } catch (Exception e) {
885             return null;
886         }
887     }
888 
889     /**
890      * Get subject name as X500Principal. Overrides implementation in
891      * X509Certificate with a slightly more efficient version that is
892      * also aware of X509CertImpl mutability.
893      */
894     override X500Principal getSubjectX500Principal() {
895         if (info is null) {
896             return null;
897         }
898         try {
899             X500Principal subject = cast(X500Principal)info.get(
900                                             X509CertInfo.SUBJECT ~ DOT ~
901                                             "x500principal");
902             return subject;
903         } catch (Exception e) {
904             return null;
905         }
906     }
907 
908     /**
909      * Gets the issuer distinguished name from the certificate.
910      *
911      * @return the issuer name.
912      */
913     override Principal getIssuerDN() {
914         if (info is null)
915             return null;
916         try {
917             Principal issuer = cast(Principal)info.get(X509CertInfo.ISSUER ~ DOT ~
918                                                    X509CertInfo.DN_NAME);
919             return issuer;
920         } catch (Exception e) {
921             return null;
922         }
923     }
924 
925     /**
926      * Get issuer name as X500Principal. Overrides implementation in
927      * X509Certificate with a slightly more efficient version that is
928      * also aware of X509CertImpl mutability.
929      */
930     override X500Principal getIssuerX500Principal() {
931         if (info is null) {
932             return null;
933         }
934         try {
935             X500Principal issuer = cast(X500Principal)info.get(
936                                             X509CertInfo.ISSUER ~ DOT ~
937                                             "x500principal");
938             return issuer;
939         } catch (Exception e) {
940             return null;
941         }
942     }
943 
944     /**
945      * Gets the notBefore date from the validity period of the certificate.
946      *
947      * @return the start date of the validity period.
948      */
949     override Date getNotBefore() {
950         // if (info is null)
951         //     return null;
952         // try {
953         //     Date d = (Date) info.get(CertificateValidity.NAME ~ DOT ~
954         //                                 CertificateValidity.NOT_BEFORE);
955         //     return d;
956         // } catch (Exception e) {
957         //     return null;
958         // }
959 
960         implementationMissing();
961         return Date.init;
962     }
963 
964     /**
965      * Gets the notAfter date from the validity period of the certificate.
966      *
967      * @return the end date of the validity period.
968      */
969     override Date getNotAfter() {
970         // if (info is null)
971         //     return null;
972         // try {
973         //     Date d = info.get(CertificateValidity.NAME ~ DOT ~
974         //                              CertificateValidity.NOT_AFTER);
975         //     return d;
976         // } catch (Exception e) {
977         //     return null;
978         // }
979         implementationMissing();
980         return Date.init;
981     }
982 
983     /**
984      * Gets the DER encoded certificate informations, the
985      * <code>tbsCertificate</code> from this certificate.
986      * This can be used to verify the signature independently.
987      *
988      * @return the DER encoded certificate information.
989      * @exception CertificateEncodingException if an encoding error occurs.
990      */
991     override byte[] getTBSCertificate() {
992         if (info !is null) {
993             return info.getEncodedInfo();
994         } else
995             throw new CertificateEncodingException("Uninitialized certificate");
996     }
997 
998     /**
999      * Gets the raw Signature bits from the certificate.
1000      *
1001      * @return the signature.
1002      */
1003     override byte[] getSignature() {
1004         if (signature is null)
1005             return null;
1006         return signature.dup;
1007     }
1008 
1009     /**
1010      * Gets the signature algorithm name for the certificate
1011      * signature algorithm.
1012      * For example, the string "SHA-1/DSA" or "DSS".
1013      *
1014      * @return the signature algorithm name.
1015      */
1016     override string getSigAlgName() {
1017         if (algId is null)
1018             return null;
1019         return (algId.getName());
1020     }
1021 
1022     /**
1023      * Gets the signature algorithm OID string from the certificate.
1024      * For example, the string "1.2.840.10040.4.3"
1025      *
1026      * @return the signature algorithm oid string.
1027      */
1028     override string getSigAlgOID() {
1029         if (algId is null)
1030             return null;
1031         ObjectIdentifier oid = algId.getOID();
1032         return (oid.toString());
1033     }
1034 
1035     /**
1036      * Gets the DER encoded signature algorithm parameters from this
1037      * certificate's signature algorithm.
1038      *
1039      * @return the DER encoded signature algorithm parameters, or
1040      *         null if no parameters are present.
1041      */
1042     override byte[] getSigAlgParams() {
1043         if (algId is null)
1044             return null;
1045         try {
1046             return algId.getEncodedParams();
1047         } catch (IOException e) {
1048             return null;
1049         }
1050     }
1051 
1052     /**
1053      * Gets the Issuer Unique Identity from the certificate.
1054      *
1055      * @return the Issuer Unique Identity.
1056      */
1057     override bool[] getIssuerUniqueID() {
1058         if (info is null)
1059             return null;
1060         try {
1061             UniqueIdentity id = cast(UniqueIdentity)info.get(
1062                                  X509CertInfo.ISSUER_ID);
1063             if (id is null)
1064                 return null;
1065             else
1066                 return (id.getId());
1067         } catch (Exception e) {
1068             return null;
1069         }
1070     }
1071 
1072     /**
1073      * Gets the Subject Unique Identity from the certificate.
1074      *
1075      * @return the Subject Unique Identity.
1076      */
1077     override bool[] getSubjectUniqueID() {
1078         if (info is null)
1079             return null;
1080         try {
1081             UniqueIdentity id = cast(UniqueIdentity)info.get(
1082                                  X509CertInfo.SUBJECT_ID);
1083             if (id is null)
1084                 return null;
1085             else
1086                 return (id.getId());
1087         } catch (Exception e) {
1088             return null;
1089         }
1090     }
1091 
1092     KeyIdentifier getAuthKeyId() {
1093         // AuthorityKeyIdentifierExtension aki
1094         //     = getAuthorityKeyIdentifierExtension();
1095         // if (aki !is null) {
1096         //     try {
1097         //         return cast(KeyIdentifier)aki.get(
1098         //             AuthorityKeyIdentifierExtension.KEY_ID);
1099         //     } catch (IOException ioe) {} // not possible
1100         // }
1101         implementationMissing();
1102         return null;
1103     }
1104 
1105     /**
1106      * Returns the subject's key identifier, or null
1107      */
1108     KeyIdentifier getSubjectKeyId() {
1109         // SubjectKeyIdentifierExtension ski = getSubjectKeyIdentifierExtension();
1110         // if (ski !is null) {
1111         //     try {
1112         //         return cast(KeyIdentifier)ski.get(
1113         //             SubjectKeyIdentifierExtension.KEY_ID);
1114         //     } catch (IOException ioe) {} // not possible
1115         // }
1116         // return null;
1117                 implementationMissing();
1118         return null;
1119 
1120     }
1121 
1122     // /**
1123     //  * Get AuthorityKeyIdentifier extension
1124     //  * @return AuthorityKeyIdentifier object or null (if no such object
1125     //  * in certificate)
1126     //  */
1127     // AuthorityKeyIdentifierExtension getAuthorityKeyIdentifierExtension()
1128     // {
1129     //     return cast(AuthorityKeyIdentifierExtension)
1130     //         getExtension(PKIXExtensions.AuthorityKey_Id);
1131     // }
1132 
1133     // /**
1134     //  * Get BasicConstraints extension
1135     //  * @return BasicConstraints object or null (if no such object in
1136     //  * certificate)
1137     //  */
1138     // BasicConstraintsExtension getBasicConstraintsExtension() {
1139     //     return cast(BasicConstraintsExtension)
1140     //         getExtension(PKIXExtensions.BasicConstraints_Id);
1141     // }
1142 
1143     // /**
1144     //  * Get CertificatePoliciesExtension
1145     //  * @return CertificatePoliciesExtension or null (if no such object in
1146     //  * certificate)
1147     //  */
1148     // CertificatePoliciesExtension getCertificatePoliciesExtension() {
1149     //     return cast(CertificatePoliciesExtension)
1150     //         getExtension(PKIXExtensions.CertificatePolicies_Id);
1151     // }
1152 
1153     // /**
1154     //  * Get ExtendedKeyUsage extension
1155     //  * @return ExtendedKeyUsage extension object or null (if no such object
1156     //  * in certificate)
1157     //  */
1158     // ExtendedKeyUsageExtension getExtendedKeyUsageExtension() {
1159     //     return cast(ExtendedKeyUsageExtension)
1160     //         getExtension(PKIXExtensions.ExtendedKeyUsage_Id);
1161     // }
1162 
1163     // /**
1164     //  * Get IssuerAlternativeName extension
1165     //  * @return IssuerAlternativeName object or null (if no such object in
1166     //  * certificate)
1167     //  */
1168     // IssuerAlternativeNameExtension getIssuerAlternativeNameExtension() {
1169     //     return cast(IssuerAlternativeNameExtension)
1170     //         getExtension(PKIXExtensions.IssuerAlternativeName_Id);
1171     // }
1172 
1173     // /**
1174     //  * Get NameConstraints extension
1175     //  * @return NameConstraints object or null (if no such object in certificate)
1176     //  */
1177     // NameConstraintsExtension getNameConstraintsExtension() {
1178     //     return cast(NameConstraintsExtension)
1179     //         getExtension(PKIXExtensions.NameConstraints_Id);
1180     // }
1181 
1182     // /**
1183     //  * Get PolicyConstraints extension
1184     //  * @return PolicyConstraints object or null (if no such object in
1185     //  * certificate)
1186     //  */
1187     // PolicyConstraintsExtension getPolicyConstraintsExtension() {
1188     //     return cast(PolicyConstraintsExtension)
1189     //         getExtension(PKIXExtensions.PolicyConstraints_Id);
1190     // }
1191 
1192     // /**
1193     //  * Get PolicyMappingsExtension extension
1194     //  * @return PolicyMappingsExtension object or null (if no such object
1195     //  * in certificate)
1196     //  */
1197     // PolicyMappingsExtension getPolicyMappingsExtension() {
1198     //     return cast(PolicyMappingsExtension)
1199     //         getExtension(PKIXExtensions.PolicyMappings_Id);
1200     // }
1201 
1202     // /**
1203     //  * Get PrivateKeyUsage extension
1204     //  * @return PrivateKeyUsage object or null (if no such object in certificate)
1205     //  */
1206     // PrivateKeyUsageExtension getPrivateKeyUsageExtension() {
1207     //     return cast(PrivateKeyUsageExtension)
1208     //         getExtension(PKIXExtensions.PrivateKeyUsage_Id);
1209     // }
1210 
1211     // /**
1212     //  * Get SubjectAlternativeName extension
1213     //  * @return SubjectAlternativeName object or null (if no such object in
1214     //  * certificate)
1215     //  */
1216     // SubjectAlternativeNameExtension getSubjectAlternativeNameExtension()
1217     // {
1218     //     return cast(SubjectAlternativeNameExtension)
1219     //         getExtension(PKIXExtensions.SubjectAlternativeName_Id);
1220     // }
1221 
1222     // /**
1223     //  * Get SubjectKeyIdentifier extension
1224     //  * @return SubjectKeyIdentifier object or null (if no such object in
1225     //  * certificate)
1226     //  */
1227     // SubjectKeyIdentifierExtension getSubjectKeyIdentifierExtension() {
1228     //     return cast(SubjectKeyIdentifierExtension)
1229     //         getExtension(PKIXExtensions.SubjectKey_Id);
1230     // }
1231 
1232     // /**
1233     //  * Get CRLDistributionPoints extension
1234     //  * @return CRLDistributionPoints object or null (if no such object in
1235     //  * certificate)
1236     //  */
1237     // CRLDistributionPointsExtension getCRLDistributionPointsExtension() {
1238     //     return cast(CRLDistributionPointsExtension)
1239     //         getExtension(PKIXExtensions.CRLDistributionPoints_Id);
1240     // }
1241 
1242     /**
1243      * Return true if a critical extension is found that is
1244      * not supported, otherwise return false.
1245      */
1246     bool hasUnsupportedCriticalExtension() {
1247         if (info is null)
1248             return false;
1249         try {
1250             CertificateExtensions exts = cast(CertificateExtensions)info.get(
1251                                          CertificateExtensions.NAME);
1252             if (exts is null)
1253                 return false;
1254             return exts.hasUnsupportedCriticalExtension();
1255         } catch (Exception e) {
1256             return false;
1257         }
1258     }
1259 
1260     /**
1261      * Gets a Set of the extension(s) marked CRITICAL in the
1262      * certificate. In the returned set, each extension is
1263      * represented by its OID string.
1264      *
1265      * @return a set of the extension oid strings in the
1266      * certificate that are marked critical.
1267      */
1268     Set!string getCriticalExtensionOIDs() {
1269         if (info is null) {
1270             return null;
1271         }
1272         try {
1273             CertificateExtensions exts = cast(CertificateExtensions)info.get(
1274                                          CertificateExtensions.NAME);
1275             if (exts is null) {
1276                 return null;
1277             }
1278             Set!string extSet = new TreeSet!string();
1279             foreach (Extension ex ; exts.getAllExtensions()) {
1280                 if (ex.isCritical()) {
1281                     extSet.add(ex.getExtensionId().toString());
1282                 }
1283             }
1284             return extSet;
1285         } catch (Exception e) {
1286             return null;
1287         }
1288     }
1289 
1290     /**
1291      * Gets a Set of the extension(s) marked NON-CRITICAL in the
1292      * certificate. In the returned set, each extension is
1293      * represented by its OID string.
1294      *
1295      * @return a set of the extension oid strings in the
1296      * certificate that are NOT marked critical.
1297      */
1298     Set!string getNonCriticalExtensionOIDs() {
1299         // if (info is null) {
1300         //     return null;
1301         // }
1302         // try {
1303         //     CertificateExtensions exts = cast(CertificateExtensions)info.get(
1304         //                                  CertificateExtensions.NAME);
1305         //     if (exts is null) {
1306         //         return null;
1307         //     }
1308         //     Set!string extSet = new TreeSet!string();
1309         //     foreach (Extension ex ; exts.getAllExtensions()) {
1310         //         if (!ex.isCritical()) {
1311         //             extSet.add(ex.getExtensionId().toString());
1312         //         }
1313         //     }
1314         //     extSet.addAll(exts.getUnparseableExtensions().keySet());
1315         //     return extSet;
1316         // } catch (Exception e) {
1317         //     return null;
1318         // }
1319                 implementationMissing();
1320         return null;
1321 
1322     }
1323 
1324     /**
1325      * Gets the extension identified by the given ObjectIdentifier
1326      *
1327      * @param oid the Object Identifier value for the extension.
1328      * @return Extension or null if certificate does not contain this
1329      *         extension
1330      */
1331     Extension getExtension(ObjectIdentifier oid) {
1332         if (info is null) {
1333             return null;
1334         }
1335         try {
1336             CertificateExtensions extensions;
1337             try {
1338                 extensions = cast(CertificateExtensions)info.get(CertificateExtensions.NAME);
1339             } catch (CertificateException ce) {
1340                 return null;
1341             }
1342             if (extensions is null) {
1343                 return null;
1344             } else {
1345                 Extension ex = extensions.getExtension(oid.toString());
1346                 if (ex !is null) {
1347                     return ex;
1348                 }
1349                 foreach (Extension ex2; extensions.getAllExtensions()) {
1350                     if (ex2.getExtensionId().opEquals(cast(Object)oid)) {
1351                         //XXXX May want to consider cloning this
1352                         return ex2;
1353                     }
1354                 }
1355                 /* no such extension in this certificate */
1356                 return null;
1357             }
1358         } catch (IOException ioe) {
1359             return null;
1360         }
1361     }
1362 
1363     Extension getUnparseableExtension(ObjectIdentifier oid) {
1364         if (info is null) {
1365             return null;
1366         }
1367         try {
1368             CertificateExtensions extensions;
1369             try {
1370                 extensions = cast(CertificateExtensions)info.get(CertificateExtensions.NAME);
1371             } catch (CertificateException ce) {
1372                 return null;
1373             }
1374             if (extensions is null) {
1375                 return null;
1376             } else {
1377                 return extensions.getUnparseableExtensions().get(oid.toString());
1378             }
1379         } catch (IOException ioe) {
1380             return null;
1381         }
1382     }
1383 
1384     /**
1385      * Gets the DER encoded extension identified by the given
1386      * oid string.
1387      *
1388      * @param oid the Object Identifier value for the extension.
1389      */
1390     byte[] getExtensionValue(string oid) {
1391                 implementationMissing();
1392         return null;
1393 
1394         // try {
1395         //     ObjectIdentifier findOID = new ObjectIdentifier(oid);
1396         //     string extAlias = OIDMap.getName(findOID);
1397         //     Extension certExt = null;
1398         //     CertificateExtensions exts = (CertificateExtensions)info.get(
1399         //                              CertificateExtensions.NAME);
1400 
1401         //     if (extAlias is null) { // may be unknown
1402         //         // get the extensions, search thru' for this oid
1403         //         if (exts is null) {
1404         //             return null;
1405         //         }
1406 
1407         //         foreach (Extension ex ; exts.getAllExtensions()) {
1408         //             ObjectIdentifier inCertOID = ex.getExtensionId();
1409         //             if (inCertOID == findOID)) {
1410         //                 certExt = ex;
1411         //                 break;
1412         //             }
1413         //         }
1414         //     } else { // there's sub-class that can handle this extension
1415         //         try {
1416         //             certExt = cast(Extension)this.get(extAlias);
1417         //         } catch (CertificateException e) {
1418         //             // get() throws an Exception instead of returning null, ignore
1419         //         }
1420         //     }
1421         //     if (certExt is null) {
1422         //         if (exts !is null) {
1423         //             certExt = exts.getUnparseableExtensions().get(oid);
1424         //         }
1425         //         if (certExt is null) {
1426         //             return null;
1427         //         }
1428         //     }
1429         //     byte[] extData = certExt.getExtensionValue();
1430         //     if (extData is null) {
1431         //         return null;
1432         //     }
1433         //     DerOutputStream outputStream = new DerOutputStream();
1434         //     outputStream.putOctetString(extData);
1435         //     return outputStream.toByteArray();
1436         // } catch (Exception e) {
1437         //     return null;
1438         // }
1439     }
1440 
1441     /**
1442      * Get a bool array representing the bits of the KeyUsage extension,
1443      * (oid = 2.5.29.15).
1444      * @return the bit values of this extension as an array of booleans.
1445      */
1446     override bool[] getKeyUsage() {
1447         // try {
1448         //     string extAlias = OIDMap.getName(PKIXExtensions.KeyUsage_Id);
1449         //     if (extAlias is null)
1450         //         return null;
1451 
1452         //     KeyUsageExtension certExt = cast(KeyUsageExtension)this.get(extAlias);
1453         //     if (certExt is null)
1454         //         return null;
1455 
1456         //     bool[] ret = certExt.getBits();
1457         //     if (ret.length < NUM_STANDARD_KEY_USAGE) {
1458         //         bool[] usageBits = new bool[NUM_STANDARD_KEY_USAGE];
1459         //         System.arraycopy(ret, 0, usageBits, 0, ret.length);
1460         //         ret = usageBits;
1461         //     }
1462         //     return ret;
1463         // } catch (Exception e) {
1464         //     return null;
1465         // }
1466                implementationMissing();
1467         return null;
1468 
1469     }
1470 
1471     /**
1472      * This method are the overridden implementation of
1473      * getExtendedKeyUsage method in X509Certificate in the Sun
1474      * provider. It is better performance-wise since it returns cached
1475      * values.
1476      */
1477     List!string getExtendedKeyUsage() {
1478         // if (readOnly && extKeyUsage !is null) {
1479         //     return extKeyUsage;
1480         // } else {
1481         //     ExtendedKeyUsageExtension ext = getExtendedKeyUsageExtension();
1482         //     if (ext is null) {
1483         //         return null;
1484         //     }
1485         //     extKeyUsage =
1486         //         Collections.unmodifiableList(ext.getExtendedKeyUsage());
1487         //     return extKeyUsage;
1488         // }        
1489         implementationMissing();
1490         return null;
1491 
1492     }
1493 
1494     /**
1495      * This static method is the default implementation of the
1496      * getExtendedKeyUsage method in X509Certificate. A
1497      * X509Certificate provider generally should overwrite this to
1498      * provide among other things caching for better performance.
1499      */
1500     static List!string getExtendedKeyUsage(X509Certificate cert) {
1501         // try {
1502         //     byte[] ext = cert.getExtensionValue(EXTENDED_KEY_USAGE_OID);
1503         //     if (ext is null)
1504         //         return null;
1505         //     DerValue val = new DerValue(ext);
1506         //     byte[] data = val.getOctetString();
1507 
1508         //     ExtendedKeyUsageExtension ekuExt =
1509         //         new ExtendedKeyUsageExtension(false, data);
1510         //     return Collections.unmodifiableList(ekuExt.getExtendedKeyUsage());
1511         // } catch (IOException ioe) {
1512         //     throw new CertificateParsingException(ioe);
1513         // }
1514                 implementationMissing();
1515         return null;
1516 
1517     }
1518 
1519     /**
1520      * Get the certificate constraints path length from the
1521      * the critical BasicConstraints extension, (oid = 2.5.29.19).
1522      * @return the length of the constraint.
1523      */
1524     override int getBasicConstraints() {
1525                 implementationMissing();
1526         return 0;
1527 
1528         // try {
1529         //     string extAlias = OIDMap.getName(PKIXExtensions.BasicConstraints_Id);
1530         //     if (extAlias is null)
1531         //         return -1;
1532         //     BasicConstraintsExtension certExt =
1533         //                 cast(BasicConstraintsExtension)this.get(extAlias);
1534         //     if (certExt is null)
1535         //         return -1;
1536 
1537         //     if (((bool)certExt.get(BasicConstraintsExtension.IS_CA)
1538         //          ).booleanValue() == true)
1539         //         return ((Integer)certExt.get(
1540         //                 BasicConstraintsExtension.PATH_LEN)).intValue();
1541         //     else
1542         //         return -1;
1543         // } catch (Exception e) {
1544         //     return -1;
1545         // }
1546     }
1547 
1548     /**
1549      * Converts a GeneralNames structure into an immutable Collection of
1550      * alternative names (subject or issuer) in the form required by
1551      * {@link #getSubjectAlternativeNames} or
1552      * {@link #getIssuerAlternativeNames}.
1553      *
1554      * @param names the GeneralNames to be converted
1555      * @return an immutable Collection of alternative names
1556      */
1557     // private static Collection<List<?>> makeAltNames(GeneralNames names) {
1558     //     if (names.isEmpty()) {
1559     //         return Collections.<List<?>>emptySet();
1560     //     }
1561     //     List<List<?>> newNames = new ArrayList<>();
1562     //     for (GeneralName gname : names.names()) {
1563     //         GeneralNameInterface name = gname.getName();
1564     //         List<Object> nameEntry = new ArrayList<>(2);
1565     //         nameEntry.add(Integer.valueOf(name.getType()));
1566     //         switch (name.getType()) {
1567     //         case GeneralNameInterface.NAME_RFC822:
1568     //             nameEntry.add(((RFC822Name) name).getName());
1569     //             break;
1570     //         case GeneralNameInterface.NAME_DNS:
1571     //             nameEntry.add(((DNSName) name).getName());
1572     //             break;
1573     //         case GeneralNameInterface.NAME_DIRECTORY:
1574     //             nameEntry.add(((X500Name) name).getRFC2253Name());
1575     //             break;
1576     //         case GeneralNameInterface.NAME_URI:
1577     //             nameEntry.add(((URIName) name).getName());
1578     //             break;
1579     //         case GeneralNameInterface.NAME_IP:
1580     //             try {
1581     //                 nameEntry.add(((IPAddressName) name).getName());
1582     //             } catch (IOException ioe) {
1583     //                 // IPAddressName in cert is bogus
1584     //                 throw new RuntimeException("IPAddress cannot be parsed",
1585     //                     ioe);
1586     //             }
1587     //             break;
1588     //         case GeneralNameInterface.NAME_OID:
1589     //             nameEntry.add(((OIDName) name).getOID().toString());
1590     //             break;
1591     //         default:
1592     //             // add DER encoded form
1593     //             DerOutputStream derOut = new DerOutputStream();
1594     //             try {
1595     //                 name.encode(derOut);
1596     //             } catch (IOException ioe) {
1597     //                 // should not occur since name has already been decoded
1598     //                 // from cert (this would indicate a bug in our code)
1599     //                 throw new RuntimeException("name cannot be encoded", ioe);
1600     //             }
1601     //             nameEntry.add(derOut.toByteArray());
1602     //             break;
1603     //         }
1604     //         newNames.add(Collections.unmodifiableList(nameEntry));
1605     //     }
1606     //     return Collections.unmodifiableCollection(newNames);
1607     // }
1608 
1609     /**
1610      * Checks a Collection of altNames and clones any name entries of type
1611      * byte [].
1612      */ // only partially generified due to javac bug
1613     // private static Collection<List<?>> cloneAltNames(Collection<List<?>> altNames) {
1614     //     bool mustClone = false;
1615     //     for (List<?> nameEntry : altNames) {
1616     //         if (nameEntry.get(1) instanceof byte[]) {
1617     //             // must clone names
1618     //             mustClone = true;
1619     //         }
1620     //     }
1621     //     if (mustClone) {
1622     //         List<List<?>> namesCopy = new ArrayList<>();
1623     //         for (List<?> nameEntry : altNames) {
1624     //             Object nameObject = nameEntry.get(1);
1625     //             if (nameObject instanceof byte[]) {
1626     //                 List<Object> nameEntryCopy =
1627     //                                     new ArrayList<>(nameEntry);
1628     //                 nameEntryCopy.set(1, ((byte[])nameObject).dup);
1629     //                 namesCopy.add(Collections.unmodifiableList(nameEntryCopy));
1630     //             } else {
1631     //                 namesCopy.add(nameEntry);
1632     //             }
1633     //         }
1634     //         return Collections.unmodifiableCollection(namesCopy);
1635     //     } else {
1636     //         return altNames;
1637     //     }
1638     // }
1639 
1640     /**
1641      * This method are the overridden implementation of
1642      * getSubjectAlternativeNames method in X509Certificate in the Sun
1643      * provider. It is better performance-wise since it returns cached
1644      * values.
1645      */
1646     // Collection<List<?>> getSubjectAlternativeNames()
1647     //     {
1648     //     // return cached value if we can
1649     //     if (readOnly && subjectAlternativeNames !is null)  {
1650     //         return cloneAltNames(subjectAlternativeNames);
1651     //     }
1652     //     SubjectAlternativeNameExtension subjectAltNameExt =
1653     //         getSubjectAlternativeNameExtension();
1654     //     if (subjectAltNameExt is null) {
1655     //         return null;
1656     //     }
1657     //     GeneralNames names;
1658     //     try {
1659     //         names = subjectAltNameExt.get(
1660     //                 SubjectAlternativeNameExtension.SUBJECT_NAME);
1661     //     } catch (IOException ioe) {
1662     //         // should not occur
1663     //         return Collections.<List<?>>emptySet();
1664     //     }
1665     //     subjectAlternativeNames = makeAltNames(names);
1666     //     return subjectAlternativeNames;
1667     // }
1668 
1669     /**
1670      * This static method is the default implementation of the
1671      * getSubjectAlternaitveNames method in X509Certificate. A
1672      * X509Certificate provider generally should overwrite this to
1673      * provide among other things caching for better performance.
1674      */
1675     // static Collection<List<?>> getSubjectAlternativeNames(X509Certificate cert)
1676     //     {
1677     //     try {
1678     //         byte[] ext = cert.getExtensionValue(SUBJECT_ALT_NAME_OID);
1679     //         if (ext is null) {
1680     //             return null;
1681     //         }
1682     //         DerValue val = new DerValue(ext);
1683     //         byte[] data = val.getOctetString();
1684 
1685     //         SubjectAlternativeNameExtension subjectAltNameExt =
1686     //             new SubjectAlternativeNameExtension(false,
1687     //                                                 data);
1688 
1689     //         GeneralNames names;
1690     //         try {
1691     //             names = subjectAltNameExt.get(
1692     //                     SubjectAlternativeNameExtension.SUBJECT_NAME);
1693     //         }  catch (IOException ioe) {
1694     //             // should not occur
1695     //             return Collections.<List<?>>emptySet();
1696     //         }
1697     //         return makeAltNames(names);
1698     //     } catch (IOException ioe) {
1699     //         throw new CertificateParsingException(ioe);
1700     //     }
1701     // }
1702 
1703     /**
1704      * This method are the overridden implementation of
1705      * getIssuerAlternativeNames method in X509Certificate in the Sun
1706      * provider. It is better performance-wise since it returns cached
1707      * values.
1708      */
1709     // Collection<List<?>> getIssuerAlternativeNames()
1710     //     {
1711     //     // return cached value if we can
1712     //     if (readOnly && issuerAlternativeNames !is null) {
1713     //         return cloneAltNames(issuerAlternativeNames);
1714     //     }
1715     //     IssuerAlternativeNameExtension issuerAltNameExt =
1716     //         getIssuerAlternativeNameExtension();
1717     //     if (issuerAltNameExt is null) {
1718     //         return null;
1719     //     }
1720     //     GeneralNames names;
1721     //     try {
1722     //         names = issuerAltNameExt.get(
1723     //                 IssuerAlternativeNameExtension.ISSUER_NAME);
1724     //     } catch (IOException ioe) {
1725     //         // should not occur
1726     //         return Collections.<List<?>>emptySet();
1727     //     }
1728     //     issuerAlternativeNames = makeAltNames(names);
1729     //     return issuerAlternativeNames;
1730     // }
1731 
1732     /**
1733      * This static method is the default implementation of the
1734      * getIssuerAlternaitveNames method in X509Certificate. A
1735      * X509Certificate provider generally should overwrite this to
1736      * provide among other things caching for better performance.
1737      */
1738     // static Collection<List<?>> getIssuerAlternativeNames(X509Certificate cert)
1739     //     {
1740     //     try {
1741     //         byte[] ext = cert.getExtensionValue(ISSUER_ALT_NAME_OID);
1742     //         if (ext is null) {
1743     //             return null;
1744     //         }
1745 
1746     //         DerValue val = new DerValue(ext);
1747     //         byte[] data = val.getOctetString();
1748 
1749     //         IssuerAlternativeNameExtension issuerAltNameExt =
1750     //             new IssuerAlternativeNameExtension(false,
1751     //                                                 data);
1752     //         GeneralNames names;
1753     //         try {
1754     //             names = issuerAltNameExt.get(
1755     //                     IssuerAlternativeNameExtension.ISSUER_NAME);
1756     //         }  catch (IOException ioe) {
1757     //             // should not occur
1758     //             return Collections.<List<?>>emptySet();
1759     //         }
1760     //         return makeAltNames(names);
1761     //     } catch (IOException ioe) {
1762     //         throw new CertificateParsingException(ioe);
1763     //     }
1764     // }
1765 
1766     // AuthorityInfoAccessExtension getAuthorityInfoAccessExtension() {
1767     //     return (AuthorityInfoAccessExtension)
1768     //         getExtension(PKIXExtensions.AuthInfoAccess_Id);
1769     // }
1770 
1771     /************************************************************/
1772 
1773     /*
1774      * Cert is a SIGNED ASN.1 macro, a three elment sequence:
1775      *
1776      *  - Data to be signed (ToBeSigned) -- the "raw" cert
1777      *  - Signature algorithm (SigAlgId)
1778      *  - The signature bits
1779      *
1780      * This routine unmarshals the certificate, saving the signature
1781      * parts away for later verification.
1782      */
1783     private void parse(DerValue val) {
1784         // check if can over write the certificate
1785         if (readOnly)
1786             throw new CertificateParsingException(
1787                       "cannot over-write existing certificate");
1788 
1789     //     if (val.data is null || val.tag != DerValue.tag_Sequence)
1790     //         throw new CertificateParsingException(
1791     //                   "invalid DER-encoded certificate data");
1792 
1793     //     signedCert = val.toByteArray();
1794     //     DerValue[] seq = new DerValue[3];
1795 
1796     //     seq[0] = val.data.getDerValue();
1797     //     seq[1] = val.data.getDerValue();
1798     //     seq[2] = val.data.getDerValue();
1799 
1800     //     if (val.data.available() != 0) {
1801     //         throw new CertificateParsingException("signed overrun, bytes = "
1802     //                                  + val.data.available());
1803     //     }
1804     //     if (seq[0].tag != DerValue.tag_Sequence) {
1805     //         throw new CertificateParsingException("signed fields invalid");
1806     //     }
1807 
1808     //     algId = AlgorithmId.parse(seq[1]);
1809     //     signature = seq[2].getBitString();
1810 
1811     //     if (seq[1].data.available() != 0) {
1812     //         throw new CertificateParsingException("algid field overrun");
1813     //     }
1814     //     if (seq[2].data.available() != 0)
1815     //         throw new CertificateParsingException("signed fields overrun");
1816 
1817     //     // The CertificateInfo
1818     //     info = new X509CertInfo(seq[0]);
1819 
1820     //     // the "inner" and "outer" signature algorithms must match
1821     //     AlgorithmId infoSigAlg = (AlgorithmId)info.get(
1822     //                                           CertificateAlgorithmId.NAME
1823     //                                           ~ DOT ~
1824     //                                           CertificateAlgorithmId.ALGORITHM);
1825     //     if (! algId.equals(infoSigAlg))
1826     //         throw new CertificateException("Signature algorithm mismatch");
1827     //     readOnly = true;
1828 
1829             implementationMissing();
1830     }
1831 
1832     /**
1833      * Extract the subject or issuer X500Principal from an X509Certificate.
1834      * Parses the encoded form of the cert to preserve the principal's
1835      * ASN.1 encoding.
1836      */
1837     private static X500Principal getX500Principal(X509Certificate cert,
1838             bool getIssuer) {
1839         // byte[] encoded = cert.getEncoded();
1840         // DerInputStream derIn = new DerInputStream(encoded);
1841         // DerValue tbsCert = derIn.getSequence(3)[0];
1842         // DerInputStream tbsIn = tbsCert.data;
1843         // DerValue tmp;
1844         // tmp = tbsIn.getDerValue();
1845         // // skip version number if present
1846         // if (tmp.isContextSpecific(cast(byte)0)) {
1847         //   tmp = tbsIn.getDerValue();
1848         // }
1849         // // tmp always contains serial number now
1850         // tmp = tbsIn.getDerValue();              // skip signature
1851         // tmp = tbsIn.getDerValue();              // issuer
1852         // if (!getIssuer) {
1853         //     tmp = tbsIn.getDerValue();          // skip validity
1854         //     tmp = tbsIn.getDerValue();          // subject
1855         // }
1856         // byte[] principalBytes = tmp.toByteArray();
1857         // return new X500Principal(principalBytes);
1858             implementationMissing();
1859         return null;
1860 
1861     }
1862 
1863     /**
1864      * Extract the subject X500Principal from an X509Certificate.
1865      * Called from java.security.cert.X509Certificate.getSubjectX500Principal().
1866      */
1867     static X500Principal getSubjectX500Principal(X509Certificate cert) {
1868         try {
1869             return getX500Principal(cert, false);
1870         } catch (Exception e) {
1871             throw new RuntimeException("Could not parse subject", e);
1872         }
1873     }
1874 
1875     /**
1876      * Extract the issuer X500Principal from an X509Certificate.
1877      * Called from java.security.cert.X509Certificate.getIssuerX500Principal().
1878      */
1879     static X500Principal getIssuerX500Principal(X509Certificate cert) {
1880         try {
1881             return getX500Principal(cert, true);
1882         } catch (Exception e) {
1883             throw new RuntimeException("Could not parse issuer", e);
1884         }
1885     }
1886 
1887     /**
1888      * Returned the encoding of the given certificate for internal use.
1889      * Callers must guarantee that they neither modify it nor expose it
1890      * to untrusted code. Uses getEncodedInternal() if the certificate
1891      * is instance of X509CertImpl, getEncoded() otherwise.
1892      */
1893     static byte[] getEncodedInternal(Certificate cert) {
1894         X509CertImpl impl = cast(X509CertImpl)cert;
1895         if (impl !is null) {
1896             return impl.getEncodedInternal();
1897         } else {
1898             return cert.getEncoded();
1899         }
1900     }
1901 
1902     /**
1903      * Utility method to convert an arbitrary instance of X509Certificate
1904      * to a X509CertImpl. Does a cast if possible, otherwise reparses
1905      * the encoding.
1906      */
1907     static X509CertImpl toImpl(X509Certificate cert) {
1908         X509CertImpl impl = cast(X509CertImpl)cert;
1909         if (impl !is null) {
1910             return impl;
1911         } else {
1912             return X509Factory.intern(cert);
1913         }
1914     }
1915 
1916     /**
1917      * Utility method to test if a certificate is self-issued. This is
1918      * the case iff the subject and issuer X500Principals are equal.
1919      */
1920     static bool isSelfIssued(X509Certificate cert) {
1921         X500Principal subject = cert.getSubjectX500Principal();
1922         X500Principal issuer = cert.getIssuerX500Principal();
1923         return subject == issuer;
1924     }
1925 
1926     /**
1927      * Utility method to test if a certificate is self-signed. This is
1928      * the case iff the subject and issuer X500Principals are equal
1929      * AND the certificate's subject key can be used to verify
1930      * the certificate. In case of exception, returns false.
1931      */
1932     static bool isSelfSigned(X509Certificate cert,
1933         string sigProvider) {
1934         if (isSelfIssued(cert)) {
1935             // try {
1936             //     if (sigProvider is null) {
1937             //         cert.verify(cert.getPublicKey());
1938             //     } else {
1939             //         cert.verify(cert.getPublicKey(), sigProvider);
1940             //     }
1941             //     return true;
1942             // } catch (Exception e) {
1943             //     // In case of exception, return false
1944             // }
1945                     implementationMissing();
1946 
1947         }
1948         return false;
1949     }
1950 
1951     // private ConcurrentHashMap<string,string> fingerprints =
1952     //         new ConcurrentHashMap<>(2);
1953 
1954     string getFingerprint(string algorithm) {
1955         // return fingerprints.computeIfAbsent(algorithm,
1956         //         x -> getFingerprint(x, this));
1957                 implementationMissing();
1958         return null;
1959 
1960     }
1961 
1962     /**
1963      * Gets the requested finger print of the certificate. The result
1964      * only contains 0-9 and A-F. No small case, no colon.
1965      */
1966     static string getFingerprint(string algorithm,
1967             X509Certificate cert) {
1968         // string fingerPrint = "";
1969         // try {
1970         //     byte[] encCertInfo = cert.getEncoded();
1971         //     MessageDigest md = MessageDigest.getInstance(algorithm);
1972         //     byte[] digest = md.digest(encCertInfo);
1973         //     StringBuffer buf = new StringBuffer();
1974         //     for (int i = 0; i < digest.length; i++) {
1975         //         byte2hex(digest[i], buf);
1976         //     }
1977         //     fingerPrint = buf.toString();
1978         // } catch (Exception e) {
1979         //     // ignored
1980         // }
1981         // return fingerPrint;
1982                 implementationMissing();
1983         return null;
1984 
1985     }
1986 
1987     /**
1988      * Converts a byte to hex digit and writes to the supplied buffer
1989      */
1990     // private static void byte2hex(byte b, StringBuffer buf) {
1991     //     char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
1992     //             '9', 'A', 'B', 'C', 'D', 'E', 'F' };
1993     //     int high = ((b & 0xf0) >> 4);
1994     //     int low = (b & 0x0f);
1995     //     buf.append(hexChars[high]);
1996     //     buf.append(hexChars[low]);
1997     // }
1998 }