1 module hunt.security.x509.X509CRLImpl;
2 
3 import hunt.security.Key;
4 import hunt.security.Principal;
5 import hunt.security.Provider;
6 
7 import hunt.security.cert.Certificate;
8 import hunt.security.cert.X509CRL;
9 import hunt.security.cert.X509CRLEntry;
10 import hunt.security.cert.X509Certificate;
11 
12 import hunt.security.x500.X500Principal;
13 import hunt.security.x509.AlgorithmId;
14 import hunt.security.x509.CRLExtensions;
15 import hunt.security.x509.Extension;
16 import hunt.security.x509.KeyIdentifier;
17 import hunt.security.x509.X500Name;
18 import hunt.security.x509.X509CRLEntryImpl;
19 import hunt.security.x509.X509Factory;
20 
21 import hunt.security.util.DerEncoder;
22 import hunt.security.util.DerValue;
23 import hunt.security.util.DerInputStream;
24 import hunt.security.util.DerOutputStream;
25 import hunt.security.util.ObjectIdentifier;
26 
27 import hunt.collection;
28 import hunt.Exceptions;
29 import hunt.stream.Common;
30 import hunt.text.Common;
31 import hunt.util.Common;
32 import hunt.util.StringBuilder;
33 
34 import std.algorithm;
35 import std.bigint;
36 import std.conv;
37 import std.datetime;
38 
39 alias BigInteger = BigInt;
40 
41 /**
42  * <p>
43  * An implementation for X509 CRL (Certificate Revocation List).
44  * <p>
45  * The X.509 v2 CRL format is described below in ASN.1:
46  * <pre>
47  * CertificateList  ::=  SEQUENCE  {
48  *     tbsCertList          TBSCertList,
49  *     signatureAlgorithm   AlgorithmIdentifier,
50  *     signature            BIT STRING  }
51  * </pre>
52  * More information can be found in
53  * <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
54  * Public Key Infrastructure Certificate and CRL Profile</a>.
55  * <p>
56  * The ASN.1 definition of <code>tbsCertList</code> is:
57  * <pre>
58  * TBSCertList  ::=  SEQUENCE  {
59  *     version                 Version OPTIONAL,
60  *                             -- if present, must be v2
61  *     signature               AlgorithmIdentifier,
62  *     issuer                  Name,
63  *     thisUpdate              ChoiceOfTime,
64  *     nextUpdate              ChoiceOfTime OPTIONAL,
65  *     revokedCertificates     SEQUENCE OF SEQUENCE  {
66  *         userCertificate         CertificateSerialNumber,
67  *         revocationDate          ChoiceOfTime,
68  *         crlEntryExtensions      Extensions OPTIONAL
69  *                                 -- if present, must be v2
70  *         }  OPTIONAL,
71  *     crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
72  *                                  -- if present, must be v2
73  *     }
74  * </pre>
75  *
76  * @author Hemma Prafullchandra
77  * @see X509CRL
78  */
79 class X509CRLImpl : X509CRL , DerEncoder {
80 
81     // CRL data, and its envelope
82     private byte[]      signedCRL = null; // DER encoded crl
83     private byte[]      signature = null; // raw signature bits
84     private byte[]      tbsCertList = null; // DER encoded "to-be-signed" CRL
85     private AlgorithmId sigAlgId = null; // sig alg in CRL
86 
87     // crl information
88     private int              _version;
89     private AlgorithmId      infoSigAlgId; // sig alg in "to-be-signed" crl
90     private X500Name         issuer = null;
91     private X500Principal    issuerPrincipal = null;
92     private Date             thisUpdate;
93     private Date             nextUpdate;
94     private Map!(X509IssuerSerial,X509CRLEntry) revokedMap;
95     private List!X509CRLEntry revokedList;
96     private CRLExtensions    extensions = null;
97     private enum bool isExplicit = true;
98     private enum long YR_2050 = 2524636800000L;
99 
100     private bool readOnly = false;
101 
102     /**
103      * PublicKey that has previously been used to successfully verify
104      * the signature of this CRL. Null if the CRL has not
105      * yet been verified (successfully).
106      */
107     private PublicKey verifiedPublicKey;
108     /**
109      * If verifiedPublicKey is not null, name of the provider used to
110      * successfully verify the signature of this CRL, or the
111      * empty string if no provider was explicitly specified.
112      */
113     private string verifiedProvider;
114 
115     /**
116      * Not to be used. As it would lead to cases of uninitialized
117      * CRL objects.
118      */
119     private this() {
120         revokedMap = new TreeMap!(X509IssuerSerial,X509CRLEntry)();
121         revokedList = new LinkedList!X509CRLEntry();
122      }
123 
124     /**
125      * Unmarshals an X.509 CRL from its encoded form, parsing the encoded
126      * bytes.  This form of constructor is used by agents which
127      * need to examine and use CRL contents. Note that the buffer
128      * must include only one CRL, and no "garbage" may be left at
129      * the end.
130      *
131      * @param crlData the encoded bytes, with no trailing padding.
132      * @exception CRLException on parsing errors.
133      */
134     this(byte[] crlData) {
135         this();
136         try {
137             parse(new DerValue(crlData));
138         } catch (IOException e) {
139             signedCRL = null;
140             throw new CRLException("Parsing error: " ~ e.msg);
141         }
142     }
143 
144     /**
145      * Unmarshals an X.509 CRL from an DER value.
146      *
147      * @param val a DER value holding at least one CRL
148      * @exception CRLException on parsing errors.
149      */
150     this(DerValue val) {
151         this();
152         try {
153             parse(val);
154         } catch (IOException e) {
155             signedCRL = null;
156             throw new CRLException("Parsing error: " ~ e.msg);
157         }
158     }
159 
160     /**
161      * Unmarshals an X.509 CRL from an input stream. Only one CRL
162      * is expected at the end of the input stream.
163      *
164      * @param inStrm an input stream holding at least one CRL
165      * @exception CRLException on parsing errors.
166      */
167     this(InputStream inStrm) {
168         this();
169         try {
170             parse(new DerValue(inStrm));
171         } catch (IOException e) {
172             signedCRL = null;
173             throw new CRLException("Parsing error: " ~ e.msg);
174         }
175     }
176 
177     /**
178      * Initial CRL constructor, no revoked certs, and no extensions.
179      *
180      * @param issuer the name of the CA issuing this CRL.
181      * @param thisUpdate the Date of this issue.
182      * @param nextUpdate the Date of the next CRL.
183      */
184     this(X500Name issuer, Date thisDate, Date nextDate) {
185         this();
186         this.issuer = issuer;
187         this.thisUpdate = thisDate;
188         this.nextUpdate = nextDate;
189     }
190 
191     /**
192      * CRL constructor, revoked certs, no extensions.
193      *
194      * @param issuer the name of the CA issuing this CRL.
195      * @param thisUpdate the Date of this issue.
196      * @param nextUpdate the Date of the next CRL.
197      * @param badCerts the array of CRL entries.
198      *
199      * @exception CRLException on parsing/construction errors.
200      */
201     this(X500Name issuer, Date thisDate, Date nextDate,
202                        X509CRLEntry[] badCerts)
203     {
204         this();
205         this.issuer = issuer;
206         this.thisUpdate = thisDate;
207         this.nextUpdate = nextDate;
208         if (badCerts !is null) {
209             X500Principal crlIssuer = getIssuerX500Principal();
210             X500Principal badCertIssuer = crlIssuer;
211             for (int i = 0; i < badCerts.length; i++) {
212                 X509CRLEntryImpl badCert = cast(X509CRLEntryImpl)badCerts[i];
213                 try {
214                     badCertIssuer = getCertIssuer(badCert, badCertIssuer);
215                 } catch (IOException ioe) {
216                     throw new CRLException("", ioe);
217                 }
218                 badCert.setCertificateIssuer(crlIssuer, badCertIssuer);
219                 X509IssuerSerial issuerSerial = new X509IssuerSerial
220                     (badCertIssuer, badCert.getSerialNumber());
221                 this.revokedMap.put(issuerSerial, cast(X509CRLEntry)badCert);
222                 this.revokedList.add(cast(X509CRLEntry)badCert);
223                 if (badCert.hasExtensions()) {
224                     this._version = 1;
225                 }
226             }
227         }
228     }
229 
230     /**
231      * CRL constructor, revoked certs and extensions.
232      *
233      * @param issuer the name of the CA issuing this CRL.
234      * @param thisUpdate the Date of this issue.
235      * @param nextUpdate the Date of the next CRL.
236      * @param badCerts the array of CRL entries.
237      * @param crlExts the CRL extensions.
238      *
239      * @exception CRLException on parsing/construction errors.
240      */
241     this(X500Name issuer, Date thisDate, Date nextDate,
242                X509CRLEntry[] badCerts, CRLExtensions crlExts)       
243     {
244         this(issuer, thisDate, nextDate, badCerts);
245         if (crlExts !is null) {
246             this.extensions = crlExts;
247             this._version = 1;
248         }
249     }
250 
251 /**
252      * Compares this CRL for equality with the given
253      * object. If the {@code other} object is an
254      * {@code instanceof} {@code X509CRL}, then
255      * its encoded form is retrieved and compared with the
256      * encoded form of this CRL.
257      *
258      * @param other the object to test for equality with this CRL.
259      *
260      * @return true iff the encoded forms of the two CRLs
261      * match, false otherwise.
262      */
263     override bool opEquals(Object other) {
264         if (other is null) {
265             return false;
266         }
267         if (this is other) {
268             return true;
269         }
270         X509CRL ot = cast(X509CRL)other;
271         try {
272             byte[] thisCRL = X509CRLImpl.getEncodedInternal(this);
273             byte[] otherCRL = X509CRLImpl.getEncodedInternal(ot);
274 
275             return thisCRL == otherCRL;
276         } catch (CRLException e) {
277             return false;
278         }
279     }
280 
281     /**
282      * Returns a hashcode value for this CRL from its
283      * encoded form.
284      *
285      * @return the hashcode value.
286      */
287     override size_t toHash() @trusted nothrow {
288         size_t retval = 0;
289         try {
290             byte[] crlData;
291             crlData = X509CRLImpl.getEncodedInternal(this);
292             for (size_t i = 1; i < crlData.length; i++) {
293                  retval += crlData[i] * i;
294             }
295             return retval;
296         } catch (Exception e) {
297             return retval;
298         }
299     }      
300 
301     /**
302      * Returned the encoding as an uncloned byte array. Callers must
303      * guarantee that they neither modify it nor expose it to untrusted
304      * code.
305      */
306     byte[] getEncodedInternal() {
307         if (signedCRL is null) {
308             throw new CRLException("Null CRL to encode");
309         }
310         return signedCRL;
311     }
312 
313     /**
314      * Returns the ASN.1 DER encoded form of this CRL.
315      *
316      * @exception CRLException if an encoding error occurs.
317      */
318     override byte[] getEncoded() {
319         return getEncodedInternal().dup;
320     }
321 
322     /**
323      * Encodes the "to-be-signed" CRL to the OutputStream.
324      *
325      * @param out the OutputStream to write to.
326      * @exception CRLException on encoding errors.
327      */
328     void encodeInfo(OutputStream outputStream) {
329         try {
330             DerOutputStream tmp = new DerOutputStream();
331             DerOutputStream rCerts = new DerOutputStream();
332             DerOutputStream seq = new DerOutputStream();
333 
334             if (_version != 0) // v2 crl encode version
335                 tmp.putInteger(_version);
336             infoSigAlgId.encode(tmp);
337             if ((_version == 0) && (issuer.toString() is null))
338                 throw new CRLException("Null Issuer DN not allowed in v1 CRL");
339             issuer.encode(tmp);
340 
341             // if (thisUpdate.getTime() < YR_2050)
342             //     tmp.putUTCTime(thisUpdate);
343             // else
344             //     tmp.putGeneralizedTime(thisUpdate);
345 
346             // if (nextUpdate !is null) {
347             //     if (nextUpdate.getTime() < YR_2050)
348             //         tmp.putUTCTime(nextUpdate);
349             //     else
350             //         tmp.putGeneralizedTime(nextUpdate);
351             // }
352             implementationMissing();
353 
354             if (!revokedList.isEmpty()) {
355                 foreach (X509CRLEntry entry ; revokedList) {
356                     (cast(X509CRLEntryImpl)entry).encode(rCerts);
357                 }
358                 tmp.write(DerValue.tag_Sequence, rCerts);
359             }
360 
361             if (extensions !is null)
362                 extensions.encode(tmp, isExplicit);
363 
364             seq.write(DerValue.tag_Sequence, tmp);
365 
366             tbsCertList = seq.toByteArray();
367             outputStream.write(tbsCertList);
368         } catch (IOException e) {
369              throw new CRLException("Encoding error: " ~ e.msg);
370         }
371     }
372 
373     /**
374      * Verifies that this CRL was signed using the
375      * private key that corresponds to the given key.
376      *
377      * @param key the PublicKey used to carry out the verification.
378      *
379      * @exception NoSuchAlgorithmException on unsupported signature
380      * algorithms.
381      * @exception InvalidKeyException on incorrect key.
382      * @exception NoSuchProviderException if there's no default provider.
383      * @exception SignatureException on signature errors.
384      * @exception CRLException on encoding errors.
385      */
386     override void verify(PublicKey key) {
387         verify(key, "");
388     }
389 
390     /**
391      * Verifies that this CRL was signed using the
392      * private key that corresponds to the given key,
393      * and that the signature verification was computed by
394      * the given provider.
395      *
396      * @param key the PublicKey used to carry out the verification.
397      * @param sigProvider the name of the signature provider.
398      *
399      * @exception NoSuchAlgorithmException on unsupported signature
400      * algorithms.
401      * @exception InvalidKeyException on incorrect key.
402      * @exception NoSuchProviderException on incorrect provider.
403      * @exception SignatureException on signature errors.
404      * @exception CRLException on encoding errors.
405      */
406     override void verify(PublicKey key, string sigProvider) {
407         if (sigProvider is null) {
408             sigProvider = "";
409         }
410         // if ((verifiedPublicKey !is null) && verifiedPublicKey.equals(key)) {
411         //     // this CRL has already been successfully verified using
412         //     // this key. Make sure providers match, too.
413         //     if (sigProvider.equals(verifiedProvider)) {
414         //         return;
415         //     }
416         // }
417         // if (signedCRL is null) {
418         //     throw new CRLException("Uninitialized CRL");
419         // }
420         // Signature   sigVerf = null;
421         // if (sigProvider.length == 0) {
422         //     sigVerf = Signature.getInstance(sigAlgId.getName());
423         // } else {
424         //     sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider);
425         // }
426         // sigVerf.initVerify(key);
427 
428         // if (tbsCertList is null) {
429         //     throw new CRLException("Uninitialized CRL");
430         // }
431 
432         // sigVerf.update(tbsCertList, 0, tbsCertList.length);
433 
434         // if (!sigVerf.verify(signature)) {
435         //     throw new SignatureException("Signature does not match.");
436         // }
437         // verifiedPublicKey = key;
438         // verifiedProvider = sigProvider;
439         implementationMissing();
440     }
441 
442     /**
443      * Verifies that this CRL was signed using the
444      * private key that corresponds to the given key,
445      * and that the signature verification was computed by
446      * the given provider. Note that the specified Provider object
447      * does not have to be registered in the provider list.
448      *
449      * @param key the PublicKey used to carry out the verification.
450      * @param sigProvider the signature provider.
451      *
452      * @exception NoSuchAlgorithmException on unsupported signature
453      * algorithms.
454      * @exception InvalidKeyException on incorrect key.
455      * @exception SignatureException on signature errors.
456      * @exception CRLException on encoding errors.
457      */
458     override void verify(PublicKey key, Provider sigProvider) {
459 
460         if (signedCRL is null) {
461             throw new CRLException("Uninitialized CRL");
462         }
463         // Signature sigVerf = null;
464         // if (sigProvider is null) {
465         //     sigVerf = Signature.getInstance(sigAlgId.getName());
466         // } else {
467         //     sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider);
468         // }
469         // sigVerf.initVerify(key);
470 
471         // if (tbsCertList is null) {
472         //     throw new CRLException("Uninitialized CRL");
473         // }
474 
475         // sigVerf.update(tbsCertList, 0, tbsCertList.length);
476 
477         // if (!sigVerf.verify(signature)) {
478         //     throw new SignatureException("Signature does not match.");
479         // }
480         implementationMissing();
481         verifiedPublicKey = key;
482     }
483 
484     /**
485      * This static method is the default implementation of the
486      * verify(PublicKey key, Provider sigProvider) method in X509CRL.
487      * Called from java.security.cert.X509CRL.verify(PublicKey key,
488      * Provider sigProvider)
489      */
490     static void verify(X509CRL crl, PublicKey key,
491             Provider sigProvider) {
492         crl.verify(key, sigProvider);
493     }
494 
495     /**
496      * Encodes an X.509 CRL, and signs it using the given key.
497      *
498      * @param key the private key used for signing.
499      * @param algorithm the name of the signature algorithm used.
500      *
501      * @exception NoSuchAlgorithmException on unsupported signature
502      * algorithms.
503      * @exception InvalidKeyException on incorrect key.
504      * @exception NoSuchProviderException on incorrect provider.
505      * @exception SignatureException on signature errors.
506      * @exception CRLException if any mandatory data was omitted.
507      */
508     void sign(PrivateKey key, string algorithm) {
509         sign(key, algorithm, null);
510     }
511 
512     /**
513      * Encodes an X.509 CRL, and signs it using the given key.
514      *
515      * @param key the private key used for signing.
516      * @param algorithm the name of the signature algorithm used.
517      * @param provider the name of the provider.
518      *
519      * @exception NoSuchAlgorithmException on unsupported signature
520      * algorithms.
521      * @exception InvalidKeyException on incorrect key.
522      * @exception NoSuchProviderException on incorrect provider.
523      * @exception SignatureException on signature errors.
524      * @exception CRLException if any mandatory data was omitted.
525      */
526     void sign(PrivateKey key, string algorithm, string provider) {
527         try {
528             if (readOnly)
529                 throw new CRLException("cannot over-write existing CRL");
530             // Signature sigEngine = null;
531             // if ((provider is null) || (provider.length == 0))
532             //     sigEngine = Signature.getInstance(algorithm);
533             // else
534             //     sigEngine = Signature.getInstance(algorithm, provider);
535 
536             // sigEngine.initSign(key);
537 
538             //                     // in case the name is reset
539             // sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm());
540             // infoSigAlgId = sigAlgId;
541 
542             // DerOutputStream ot = new DerOutputStream();
543             // DerOutputStream tmp = new DerOutputStream();
544 
545             // // encode crl info
546             // encodeInfo(tmp);
547 
548             // // encode algorithm identifier
549             // sigAlgId.encode(tmp);
550 
551             // // Create and encode the signature itself.
552             // sigEngine.update(tbsCertList, 0, tbsCertList.length);
553             // signature = sigEngine.sign();
554             // tmp.putBitString(signature);
555 
556             // // Wrap the signed data in a SEQUENCE { data, algorithm, sig }
557             // ot.write(DerValue.tag_Sequence, tmp);
558             // signedCRL = ot.toByteArray();
559             // readOnly = true;
560 
561         } catch (IOException e) {
562             throw new CRLException("Error while encoding data: " ~
563                                    e.msg);
564         }
565         implementationMissing();
566     }
567 
568     /**
569      * Returns a printable string of this CRL.
570      *
571      * @return value of this CRL in a printable form.
572      */
573     override string toString() {
574         StringBuilder sb = new StringBuilder();
575         sb.append("X.509 CRL v" ~ (_version+1).to!string() ~ "\n");
576         if (sigAlgId !is null)
577             sb.append("Signature Algorithm: " ~ sigAlgId.toString() ~
578                   ", OID=" ~ (sigAlgId.getOID()).toString() ~ "\n");
579         if (issuer !is null)
580             sb.append("Issuer: " ~ issuer.toString() ~ "\n");
581         if (thisUpdate != Date.init)
582             sb.append("\nThis Update: " ~ thisUpdate.toString() ~ "\n");
583         if (nextUpdate != Date.init)
584             sb.append("Next Update: " ~ nextUpdate.toString() ~ "\n");
585         if (revokedList.isEmpty())
586             sb.append("\nNO certificates have been revoked\n");
587         else {
588             sb.append("\nRevoked Certificates: " ~ revokedList.size().to!string());
589             int i = 1;
590             foreach (X509CRLEntry entry; revokedList) {
591                 sb.append("\n[" ~ to!string(i++) ~ "] " ~ entry.toString());
592             }
593         }
594         implementationMissing();
595         // if (extensions !is null) {
596         //     Collection!Extension allExts = extensions.getAllExtensions();
597         //     Object[] objs = allExts.toArray();
598         //     sb.append("\nCRL Extensions: " ~ objs.length);
599         //     for (int i = 0; i < objs.length; i++) {
600         //         sb.append("\n[" ~ (i+1) ~ "]: ");
601         //         Extension ext = cast(Extension)objs[i];
602         //         try {
603         //            if (OIDMap.getClass(ext.getExtensionId()) is null) {
604         //                sb.append(ext.toString());
605         //                byte[] extValue = ext.getExtensionValue();
606         //                if (extValue !is null) {
607         //                    DerOutputStream ot = new DerOutputStream();
608         //                    ot.putOctetString(extValue);
609         //                    extValue = ot.toByteArray();
610         //                    HexDumpEncoder enc = new HexDumpEncoder();
611         //                    sb.append("Extension unknown: "
612         //                              ~ "DER encoded OCTET string =\n"
613         //                              + enc.encodeBuffer(extValue) ~ "\n");
614         //               }
615         //            } else
616         //                sb.append(ext.toString()); // sub-class exists
617         //         } catch (Exception e) {
618         //             sb.append(", Error parsing this extension");
619         //         }
620         //     }
621         // }
622         // if (signature !is null) {
623         //     HexDumpEncoder encoder = new HexDumpEncoder();
624         //     sb.append("\nSignature:\n" ~ encoder.encodeBuffer(signature)
625         //               ~ "\n");
626         // } else
627         //     sb.append("NOT signed yet\n");
628         return sb.toString();
629     }
630 
631     /**
632      * Checks whether the given certificate is on this CRL.
633      *
634      * @param cert the certificate to check for.
635      * @return true if the given certificate is on this CRL,
636      * false otherwise.
637      */
638     override bool isRevoked(Certificate cert) {
639         if (revokedMap.isEmpty()) {
640             return false;
641         }
642         X509Certificate xcert = cast(X509Certificate) cert;
643         if(xcert is null)   return false;
644 
645         X509IssuerSerial issuerSerial = new X509IssuerSerial(xcert);
646         return revokedMap.containsKey(issuerSerial);
647     }
648 
649     /**
650      * Gets the version number from this CRL.
651      * The ASN.1 definition for this is:
652      * <pre>
653      * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
654      *             -- v3 does not apply to CRLs but appears for consistency
655      *             -- with definition of Version for certs
656      * </pre>
657      * @return the version number, i.e. 1 or 2.
658      */
659     override int getVersion() {
660         return _version+1;
661     }
662 
663     /**
664      * Gets the issuer distinguished name from this CRL.
665      * The issuer name identifies the entity who has signed (and
666      * issued the CRL). The issuer name field contains an
667      * X.500 distinguished name (DN).
668      * The ASN.1 definition for this is:
669      * <pre>
670      * issuer    Name
671      *
672      * Name ::= CHOICE { RDNSequence }
673      * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
674      * RelativeDistinguishedName ::=
675      *     SET OF AttributeValueAssertion
676      *
677      * AttributeValueAssertion ::= SEQUENCE {
678      *                               AttributeType,
679      *                               AttributeValue }
680      * AttributeType ::= OBJECT IDENTIFIER
681      * AttributeValue ::= ANY
682      * </pre>
683      * The Name describes a hierarchical name composed of attributes,
684      * such as country name, and corresponding values, such as US.
685      * The type of the component AttributeValue is determined by the
686      * AttributeType; in general it will be a directoryString.
687      * A directoryString is usually one of PrintableString,
688      * TeletexString or UniversalString.
689      * @return the issuer name.
690      */
691     override Principal getIssuerDN() {
692         return cast(Principal)issuer;
693     }
694 
695     /**
696      * Return the issuer as X500Principal. Overrides method in X509CRL
697      * to provide a slightly more efficient version.
698      */
699     override X500Principal getIssuerX500Principal() {
700         if (issuerPrincipal is null) {
701             issuerPrincipal = issuer.asX500Principal();
702         }
703         return issuerPrincipal;
704     }
705 
706     /**
707      * Gets the thisUpdate date from the CRL.
708      * The ASN.1 definition for this is:
709      *
710      * @return the thisUpdate date from the CRL.
711      */
712     override Date getThisUpdate() {
713         return (Date(thisUpdate.dayOfGregorianCal));
714     }
715 
716     /**
717      * Gets the nextUpdate date from the CRL.
718      *
719      * @return the nextUpdate date from the CRL, or null if
720      * not present.
721      */
722     override Date getNextUpdate() {
723         if (nextUpdate == Date.init)
724             return Date.init;
725         return (Date(nextUpdate.dayOfGregorianCal()));
726     }
727 
728     /**
729      * Gets the CRL entry with the given serial number from this CRL.
730      *
731      * @return the entry with the given serial number, or <code>null</code> if
732      * no such entry exists in the CRL.
733      * @see X509CRLEntry
734      */
735     override X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
736         if (revokedMap.isEmpty()) {
737             return null;
738         }
739         // assume this is a direct CRL entry (cert and CRL issuer are the same)
740         X509IssuerSerial issuerSerial = new X509IssuerSerial
741             (getIssuerX500Principal(), serialNumber);
742         return revokedMap.get(issuerSerial);
743     }
744 
745     /**
746      * Gets the CRL entry for the given certificate.
747      */
748     override X509CRLEntry getRevokedCertificate(X509Certificate cert) {
749         if (revokedMap.isEmpty()) {
750             return null;
751         }
752         X509IssuerSerial issuerSerial = new X509IssuerSerial(cert);
753         return revokedMap.get(issuerSerial);
754     }
755 
756     /**
757      * Gets all the revoked certificates from the CRL.
758      * A Set of X509CRLEntry.
759      *
760      * @return all the revoked certificates or <code>null</code> if there are
761      * none.
762      * @see X509CRLEntry
763      */
764     override Set!X509CRLEntry getRevokedCertificates() {
765         if (revokedList.isEmpty()) {
766             return null;
767         } else {
768             return new TreeSet!X509CRLEntry(revokedList);
769         }
770     }
771 
772     /**
773      * Gets the DER encoded CRL information, the
774      * <code>tbsCertList</code> from this CRL.
775      * This can be used to verify the signature independently.
776      *
777      * @return the DER encoded CRL information.
778      * @exception CRLException on encoding errors.
779      */
780     override byte[] getTBSCertList() {
781         if (tbsCertList is null)
782             throw new CRLException("Uninitialized CRL");
783         return tbsCertList.dup;
784     }
785 
786     /**
787      * Gets the raw Signature bits from the CRL.
788      *
789      * @return the signature.
790      */
791     override byte[] getSignature() {
792         if (signature is null)
793             return null;
794         return signature.dup;
795     }
796 
797     /**
798      * Gets the signature algorithm name for the CRL
799      * signature algorithm. For example, the string "SHA1withDSA".
800      * The ASN.1 definition for this is:
801      * <pre>
802      * AlgorithmIdentifier  ::=  SEQUENCE  {
803      *     algorithm               OBJECT IDENTIFIER,
804      *     parameters              ANY DEFINED BY algorithm OPTIONAL  }
805      *                             -- contains a value of the type
806      *                             -- registered for use with the
807      *                             -- algorithm object identifier value
808      * </pre>
809      *
810      * @return the signature algorithm name.
811      */
812     override string getSigAlgName() {
813         if (sigAlgId is null)
814             return null;
815         return sigAlgId.getName();
816     }
817 
818     /**
819      * Gets the signature algorithm OID string from the CRL.
820      * An OID is represented by a set of positive whole number separated
821      * by ".", that means,<br>
822      * &lt;positive whole number&gt;.&lt;positive whole number&gt;.&lt;...&gt;
823      * For example, the string "1.2.840.10040.4.3" identifies the SHA-1
824      * with DSA signature algorithm defined in
825      * <a href="http://www.ietf.org/rfc/rfc3279.txt">RFC 3279: Algorithms and
826      * Identifiers for the Internet X.509 Public Key Infrastructure Certificate
827      * and CRL Profile</a>.
828      *
829      * @return the signature algorithm oid string.
830      */
831     override string getSigAlgOID() {
832         if (sigAlgId is null)
833             return null;
834         ObjectIdentifier oid = sigAlgId.getOID();
835         return oid.toString();
836     }
837 
838     /**
839      * Gets the DER encoded signature algorithm parameters from this
840      * CRL's signature algorithm. In most cases, the signature
841      * algorithm parameters are null, the parameters are usually
842      * supplied with the Public Key.
843      *
844      * @return the DER encoded signature algorithm parameters, or
845      *         null if no parameters are present.
846      */
847     override byte[] getSigAlgParams() {
848         if (sigAlgId is null)
849             return null;
850         try {
851             return sigAlgId.getEncodedParams();
852         } catch (IOException e) {
853             return null;
854         }
855     }
856 
857     /**
858      * Gets the signature AlgorithmId from the CRL.
859      *
860      * @return the signature AlgorithmId
861      */
862     AlgorithmId getSigAlgId() {
863         return sigAlgId;
864     }
865 
866     /**
867      * return the AuthorityKeyIdentifier, if any.
868      *
869      * @returns AuthorityKeyIdentifier or null
870      *          (if no AuthorityKeyIdentifierExtension)
871      * @throws IOException on error
872      */
873     KeyIdentifier getAuthKeyId() {
874         // AuthorityKeyIdentifierExtension aki = getAuthKeyIdExtension();
875         // if (aki !is null) {
876         //     KeyIdentifier keyId = cast(KeyIdentifier)aki.get(AuthorityKeyIdentifierExtension.KEY_ID);
877         //     return keyId;
878         // } else {
879         //     return null;
880         // }
881         implementationMissing();
882         return null;
883     }
884 
885     /**
886      * return the AuthorityKeyIdentifierExtension, if any.
887      *
888      * @returns AuthorityKeyIdentifierExtension or null (if no such extension)
889      * @throws IOException on error
890      */
891     // AuthorityKeyIdentifierExtension getAuthKeyIdExtension() {
892     //     Object obj = getExtension(PKIXExtensions.AuthorityKey_Id);
893     //     return cast(AuthorityKeyIdentifierExtension)obj;
894     // }
895 
896     /**
897      * return the CRLNumberExtension, if any.
898      *
899      * @returns CRLNumberExtension or null (if no such extension)
900      * @throws IOException on error
901      */
902     // CRLNumberExtension getCRLNumberExtension() {
903     //     Object obj = getExtension(PKIXExtensions.CRLNumber_Id);
904     //     return cast(CRLNumberExtension)obj;
905     // }
906 
907     /**
908      * return the CRL number from the CRLNumberExtension, if any.
909      *
910      * @returns number or null (if no such extension)
911      * @throws IOException on error
912      */
913     // BigInteger getCRLNumber() {
914     //     CRLNumberExtension numExt = getCRLNumberExtension();
915     //     if (numExt !is null) {
916     //         BigInteger num = numExt.get(CRLNumberExtension.NUMBER);
917     //         return num;
918     //     } else {
919     //         return null;
920     //     }
921     // }
922 
923     /**
924      * return the DeltaCRLIndicatorExtension, if any.
925      *
926      * @returns DeltaCRLIndicatorExtension or null (if no such extension)
927      * @throws IOException on error
928      */
929     // DeltaCRLIndicatorExtension getDeltaCRLIndicatorExtension()
930     //     {
931     //     Object obj = getExtension(PKIXExtensions.DeltaCRLIndicator_Id);
932     //     return cast(DeltaCRLIndicatorExtension)obj;
933     // }
934 
935     /**
936      * return the base CRL number from the DeltaCRLIndicatorExtension, if any.
937      *
938      * @returns number or null (if no such extension)
939      * @throws IOException on error
940      */
941     // BigInteger getBaseCRLNumber() {
942     //     DeltaCRLIndicatorExtension dciExt = getDeltaCRLIndicatorExtension();
943     //     if (dciExt !is null) {
944     //         BigInteger num = dciExt.get(DeltaCRLIndicatorExtension.NUMBER);
945     //         return num;
946     //     } else {
947     //         return null;
948     //     }
949     // }
950 
951     /**
952      * return the IssuerAlternativeNameExtension, if any.
953      *
954      * @returns IssuerAlternativeNameExtension or null (if no such extension)
955      * @throws IOException on error
956      */
957     // IssuerAlternativeNameExtension getIssuerAltNameExtension() {
958     //     Object obj = getExtension(PKIXExtensions.IssuerAlternativeName_Id);
959     //     return cast(IssuerAlternativeNameExtension)obj;
960     // }
961 
962     /**
963      * return the IssuingDistributionPointExtension, if any.
964      *
965      * @returns IssuingDistributionPointExtension or null
966      *          (if no such extension)
967      * @throws IOException on error
968      */
969     // IssuingDistributionPointExtension getIssuingDistributionPointExtension() {
970     //     Object obj = getExtension(PKIXExtensions.IssuingDistributionPoint_Id);
971     //     return cast(IssuingDistributionPointExtension) obj;
972     // }
973 
974     /**
975      * Return true if a critical extension is found that is
976      * not supported, otherwise return false.
977      */
978     bool hasUnsupportedCriticalExtension() {
979         if (extensions is null)
980             return false;
981         return extensions.hasUnsupportedCriticalExtension();
982     }
983 
984     /**
985      * Gets a Set of the extension(s) marked CRITICAL in the
986      * CRL. In the returned set, each extension is represented by
987      * its OID string.
988      *
989      * @return a set of the extension oid strings in the
990      * CRL that are marked critical.
991      */
992     Set!string getCriticalExtensionOIDs() {
993         if (extensions is null) {
994             return null;
995         }
996         Set!string extSet = new TreeSet!string();
997         foreach (Extension ex ; extensions.getAllExtensions()) {
998             if (ex.isCritical()) {
999                 extSet.add(ex.getExtensionId().toString());
1000             }
1001         }
1002         return extSet;
1003     }
1004 
1005     /**
1006      * Gets a Set of the extension(s) marked NON-CRITICAL in the
1007      * CRL. In the returned set, each extension is represented by
1008      * its OID string.
1009      *
1010      * @return a set of the extension oid strings in the
1011      * CRL that are NOT marked critical.
1012      */
1013     Set!string getNonCriticalExtensionOIDs() {
1014         if (extensions is null) {
1015             return null;
1016         }
1017         Set!string extSet = new TreeSet!string();
1018         foreach (Extension ex ; extensions.getAllExtensions()) {
1019             if (!ex.isCritical()) {
1020                 extSet.add(ex.getExtensionId().toString());
1021             }
1022         }
1023         return extSet;
1024     }
1025 
1026     /**
1027      * Gets the DER encoded OCTET string for the extension value
1028      * (<code>extnValue</code>) identified by the passed in oid string.
1029      * The <code>oid</code> string is
1030      * represented by a set of positive whole number separated
1031      * by ".", that means,<br>
1032      * &lt;positive whole number&gt;.&lt;positive whole number&gt;.&lt;...&gt;
1033      *
1034      * @param oid the Object Identifier value for the extension.
1035      * @return the der encoded octet string of the extension value.
1036      */
1037     byte[] getExtensionValue(string oid) {
1038         if (extensions is null)
1039             return null;
1040         // try {
1041         //     string extAlias = OIDMap.getName(new ObjectIdentifier(oid));
1042         //     Extension crlExt = null;
1043 
1044         //     if (extAlias is null) { // may be unknown
1045         //         ObjectIdentifier findOID = new ObjectIdentifier(oid);
1046         //         Extension ex = null;
1047         //         ObjectIdentifier inCertOID;
1048         //         for (Enumeration!Extension e = extensions.getElements();
1049         //                                          e.hasMoreElements();) {
1050         //             ex = e.nextElement();
1051         //             inCertOID = ex.getExtensionId();
1052         //             if (inCertOID.equals(cast(Object)findOID)) {
1053         //                 crlExt = ex;
1054         //                 break;
1055         //             }
1056         //         }
1057         //     } else
1058         //         crlExt = extensions.get(extAlias);
1059         //     if (crlExt is null)
1060         //         return null;
1061         //     byte[] extData = crlExt.getExtensionValue();
1062         //     if (extData is null)
1063         //         return null;
1064         //     DerOutputStream ot = new DerOutputStream();
1065         //     ot.putOctetString(extData);
1066         //     return ot.toByteArray();
1067         // } catch (Exception e) {
1068         //     return null;
1069         // }
1070         implementationMissing();
1071         return null;
1072     }
1073 
1074     /**
1075      * get an extension
1076      *
1077      * @param oid ObjectIdentifier of extension desired
1078      * @returns Object of type <extension> or null, if not found
1079      * @throws IOException on error
1080      */
1081     Object getExtension(ObjectIdentifier oid) {
1082         if (extensions is null)
1083             return null;
1084 
1085         // XXX Consider cloning this
1086         // return extensions.get(OIDMap.getName(oid));
1087         implementationMissing();
1088         return null;
1089     }
1090 
1091     /*
1092      * Parses an X.509 CRL, should be used only by constructors.
1093      */
1094     private void parse(DerValue val) {
1095         // check if can over write the certificate
1096         if (readOnly)
1097             throw new CRLException("cannot over-write existing CRL");
1098 
1099         // if ( val.getData() is null || val.tag != DerValue.tag_Sequence)
1100         //     throw new CRLException("Invalid DER-encoded CRL data");
1101 
1102         // signedCRL = val.toByteArray();
1103         // DerValue[] seq = new DerValue[3];
1104 
1105         // seq[0] = val.data.getDerValue();
1106         // seq[1] = val.data.getDerValue();
1107         // seq[2] = val.data.getDerValue();
1108 
1109         // if (val.data.available() != 0)
1110         //     throw new CRLException("signed overrun, bytes = "
1111         //                              + val.data.available());
1112 
1113         // if (seq[0].tag != DerValue.tag_Sequence)
1114         //     throw new CRLException("signed CRL fields invalid");
1115 
1116         // sigAlgId = AlgorithmId.parse(seq[1]);
1117         // signature = seq[2].getBitString();
1118 
1119         // if (seq[1].data.available() != 0)
1120         //     throw new CRLException("AlgorithmId field overrun");
1121 
1122         // if (seq[2].data.available() != 0)
1123         //     throw new CRLException("Signature field overrun");
1124 
1125         // // the tbsCertsList
1126         // tbsCertList = seq[0].toByteArray();
1127 
1128         // // parse the information
1129         // DerInputStream derStrm = seq[0].data;
1130         // DerValue       tmp;
1131         // byte           nextByte;
1132 
1133         // // version (optional if v1)
1134         // _version = 0;   // by default, version = v1 == 0
1135         // nextByte = cast(byte)derStrm.peekByte();
1136         // if (nextByte == DerValue.tag_Integer) {
1137         //     _version = derStrm.getInteger();
1138         //     if (_version != 1)  // i.e. v2
1139         //         throw new CRLException("Invalid version");
1140         // }
1141         // tmp = derStrm.getDerValue();
1142 
1143         // // signature
1144         // AlgorithmId tmpId = AlgorithmId.parse(tmp);
1145 
1146         // // the "inner" and "outer" signature algorithms must match
1147         // if (! tmpId.equals(sigAlgId))
1148         //     throw new CRLException("Signature algorithm mismatch");
1149         // infoSigAlgId = tmpId;
1150 
1151         // // issuer
1152         // issuer = new X500Name(derStrm);
1153         // if (issuer.isEmpty()) {
1154         //     throw new CRLException("Empty issuer DN not allowed in X509CRLs");
1155         // }
1156 
1157         // // thisUpdate
1158         // // check if UTCTime encoded or GeneralizedTime
1159 
1160         // nextByte = cast(byte)derStrm.peekByte();
1161         // if (nextByte == DerValue.tag_UtcTime) {
1162         //     thisUpdate = derStrm.getUTCTime();
1163         // } else if (nextByte == DerValue.tag_GeneralizedTime) {
1164         //     thisUpdate = derStrm.getGeneralizedTime();
1165         // } else {
1166         //     throw new CRLException("Invalid encoding for thisUpdate"
1167         //                            ~ " (tag=" ~ nextByte ~ ")");
1168         // }
1169 
1170         // if (derStrm.available() == 0)
1171         //    return;     // done parsing no more optional fields present
1172 
1173         // // nextUpdate (optional)
1174         // nextByte = cast(byte)derStrm.peekByte();
1175         // if (nextByte == DerValue.tag_UtcTime) {
1176         //     nextUpdate = derStrm.getUTCTime();
1177         // } else if (nextByte == DerValue.tag_GeneralizedTime) {
1178         //     nextUpdate = derStrm.getGeneralizedTime();
1179         // } // else it is not present
1180 
1181         // if (derStrm.available() == 0)
1182         //     return;     // done parsing no more optional fields present
1183 
1184         // // revokedCertificates (optional)
1185         // nextByte = cast(byte)derStrm.peekByte();
1186         // if ((nextByte == DerValue.tag_SequenceOf)
1187         //     && (! ((nextByte & 0x0c0) == 0x080))) {
1188         //     DerValue[] badCerts = derStrm.getSequence(4);
1189 
1190         //     X500Principal crlIssuer = getIssuerX500Principal();
1191         //     X500Principal badCertIssuer = crlIssuer;
1192         //     for (int i = 0; i < badCerts.length; i++) {
1193         //         X509CRLEntryImpl entry = new X509CRLEntryImpl(badCerts[i]);
1194         //         badCertIssuer = getCertIssuer(entry, badCertIssuer);
1195         //         entry.setCertificateIssuer(crlIssuer, badCertIssuer);
1196         //         X509IssuerSerial issuerSerial = new X509IssuerSerial
1197         //             (badCertIssuer, entry.getSerialNumber());
1198         //         revokedMap.put(issuerSerial, entry);
1199         //         revokedList.add(entry);
1200         //     }
1201         // }
1202 
1203         // if (derStrm.available() == 0)
1204         //     return;     // done parsing no extensions
1205 
1206         // // crlExtensions (optional)
1207         // tmp = derStrm.getDerValue();
1208         // if (tmp.isConstructed() && tmp.isContextSpecific(cast(byte)0)) {
1209         //     extensions = new CRLExtensions(tmp.data);
1210         // }
1211         // readOnly = true;
1212         implementationMissing();
1213     }
1214 
1215     /**
1216      * Extract the issuer X500Principal from an X509CRL. Parses the encoded
1217      * form of the CRL to preserve the principal's ASN.1 encoding.
1218      *
1219      * Called by java.security.cert.X509CRL.getIssuerX500Principal().
1220      */
1221     static X500Principal getIssuerX500Principal(X509CRL crl) {
1222         // try {
1223         //     byte[] encoded = crl.getEncoded();
1224         //     DerInputStream derIn = new DerInputStream(encoded);
1225         //     DerValue tbsCert = derIn.getSequence(3)[0];
1226         //     DerInputStream tbsIn = tbsCert.data;
1227 
1228         //     DerValue tmp;
1229         //     // skip version number if present
1230         //     byte nextByte = cast(byte)tbsIn.peekByte();
1231         //     if (nextByte == DerValue.tag_Integer) {
1232         //         tmp = tbsIn.getDerValue();
1233         //     }
1234 
1235         //     tmp = tbsIn.getDerValue();  // skip signature
1236         //     tmp = tbsIn.getDerValue();  // issuer
1237         //     byte[] principalBytes = tmp.toByteArray();
1238         //     return new X500Principal(principalBytes);
1239         // } catch (Exception e) {
1240         //     throw new RuntimeException("Could not parse issuer", e);
1241         // }
1242                 implementationMissing();
1243         return null;
1244 
1245     }
1246 
1247     /**
1248      * Returned the encoding of the given certificate for internal use.
1249      * Callers must guarantee that they neither modify it nor expose it
1250      * to untrusted code. Uses getEncodedInternal() if the certificate
1251      * is instance of X509CertImpl, getEncoded() otherwise.
1252      */
1253     static byte[] getEncodedInternal(X509CRL crl) {
1254         X509CRLImpl crlImpl = cast(X509CRLImpl)crl;
1255         if (crlImpl is null) {
1256             return crl.getEncoded();
1257         } else {
1258             return crlImpl.getEncodedInternal();
1259         }
1260     }
1261 
1262     /**
1263      * Utility method to convert an arbitrary instance of X509CRL
1264      * to a X509CRLImpl. Does a cast if possible, otherwise reparses
1265      * the encoding.
1266      */
1267     static X509CRLImpl toImpl(X509CRL crl) {
1268         X509CRLImpl crlImpl = cast(X509CRLImpl)crl;
1269         if (crlImpl is null) {
1270             return X509Factory.intern(crl);
1271         } else {
1272             return crlImpl;
1273         }
1274     }
1275 
1276     /**
1277      * Returns the X500 certificate issuer DN of a CRL entry.
1278      *
1279      * @param entry the entry to check
1280      * @param prevCertIssuer the previous entry's certificate issuer
1281      * @return the X500Principal in a CertificateIssuerExtension, or
1282      *   prevCertIssuer if it does not exist
1283      */
1284     private X500Principal getCertIssuer(X509CRLEntryImpl entry,
1285         X500Principal prevCertIssuer) {
1286 
1287         // CertificateIssuerExtension ciExt =
1288         //     entry.getCertificateIssuerExtension();
1289         // if (ciExt !is null) {
1290         //     GeneralNames names = ciExt.get(CertificateIssuerExtension.ISSUER);
1291         //     X500Name issuerDN = cast(X500Name) names.get(0).getName();
1292         //     return issuerDN.asX500Principal();
1293         // } else {
1294         //     return prevCertIssuer;
1295         // }
1296                 implementationMissing();
1297         return null;
1298 
1299     }
1300 
1301     override
1302     void derEncode(OutputStream ot) {
1303         if (signedCRL is null)
1304             throw new IOException("Null CRL to encode");
1305         ot.write(signedCRL.dup);
1306     }
1307 
1308     /**
1309      * Immutable X.509 Certificate Issuer DN and serial number pair
1310      */
1311     private final static class X509IssuerSerial : Comparable!X509IssuerSerial {
1312         X500Principal issuer;
1313         BigInteger serial;
1314         size_t hashcode = 0;
1315 
1316         /**
1317          * Create an X509IssuerSerial.
1318          *
1319          * @param issuer the issuer DN
1320          * @param serial the serial number
1321          */
1322         this(X500Principal issuer, BigInteger serial) {
1323             this.issuer = issuer;
1324             this.serial = serial;
1325         }
1326 
1327         /**
1328          * Construct an X509IssuerSerial from an X509Certificate.
1329          */
1330         this(X509Certificate cert) {
1331             this(cert.getIssuerX500Principal(), cert.getSerialNumber());
1332         }
1333 
1334         /**
1335          * Returns the issuer.
1336          *
1337          * @return the issuer
1338          */
1339         X500Principal getIssuer() {
1340             return issuer;
1341         }
1342 
1343         /**
1344          * Returns the serial number.
1345          *
1346          * @return the serial number
1347          */
1348         BigInteger getSerial() {
1349             return serial;
1350         }
1351 
1352         /**
1353          * Compares this X509Serial with another and returns true if they
1354          * are equivalent.
1355          *
1356          * @param o the other object to compare with
1357          * @return true if equal, false otherwise
1358          */
1359         override bool opEquals(Object o) {
1360             if(o is null)   return false;
1361 
1362             if (o is this) {
1363                 return true;
1364             }
1365 
1366             X509IssuerSerial other = cast(X509IssuerSerial) o;
1367             if(other is null)  return false;
1368 
1369             if (serial == other.getSerial() &&
1370                 issuer == other.getIssuer()) {
1371                 return true;
1372             }
1373             return false;
1374         }
1375 
1376         /**
1377          * Returns a hash code value for this X509IssuerSerial.
1378          *
1379          * @return the hash code value
1380          */
1381         override size_t toHash() @trusted nothrow {
1382             if (hashcode == 0) {
1383                 size_t result = 17;
1384                 result = 37*result + issuer.toHash();
1385                 result = 37*result + serial.toHash();
1386                 hashcode = result;
1387             }
1388             return hashcode;
1389         }
1390 
1391         override int opCmp(Object o)
1392         {
1393             return opCmp(cast(X509IssuerSerial) o);
1394         }
1395 
1396         int opCmp(X509IssuerSerial another) {
1397             int cissuer =  std.algorithm.cmp(issuer.toString(), another.issuer.toString());
1398             if (cissuer != 0) return cissuer;
1399             return (this.serial - another.serial).toInt();
1400         }
1401     }
1402 }