1 /**
2 * X.509 Certificates
3 *
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.cert.x509.x509cert;
12
13 import botan.constants;
14 version(X509):
15 public import botan.utils.datastor.datastor;
16 public import botan.pubkey.x509_key;
17 public import botan.cert.x509.x509_obj;
18 public import botan.asn1.x509_dn;
19 public import botan.cert.x509.certstor;
20 import botan.cert.x509.key_constraint : KeyConstraints;
21 import botan.cert.x509.x509_ext;
22 import botan.codec.pem;
23 import botan.codec.hex;
24 import botan.asn1.asn1_alt_name;
25 import botan.asn1.der_enc;
26 import botan.asn1.ber_dec;
27 import botan.asn1.oids;
28 import botan.asn1.asn1_time;
29 import botan.libstate.lookup;
30 import botan.math.bigint.bigint;
31 import botan.utils.types;
32 import memutils.refcounted;
33 import memutils.dictionarylist;
34 import memutils.hashmap;
35 import botan.utils.parsing;
36 import botan.utils.types : Vector, RefCounted;
37 import std.algorithm;
38 import std.array : Appender;
39
40
41 alias X509Certificate = RefCounted!X509CertificateImpl;
42
43 /**
44 * This class represents X.509 Certificate
45 */
46 final class X509CertificateImpl : X509Object
47 {
48 public:
49 /**
50 * Get the public key associated with this certificate.
51 * Returns: subject public key of this certificate
52 */
53 PublicKey subjectPublicKey() const
54 {
55 Vector!ubyte keybits = subjectPublicKeyBits().clone;
56 return x509_key.loadKey(putInSequence(keybits));
57 }
58
59 /**
60 * Get the public key associated with this certificate.
61 * Returns: subject public key of this certificate
62 */
63 const(Vector!ubyte) subjectPublicKeyBits() const
64 {
65 return hexDecode(m_subject.get1("X509.Certificate.public_key"));
66 }
67
68 /**
69 * Get the issuer certificate DN.
70 * Returns: issuer DN of this certificate
71 */
72 const(X509DN) issuerDn() const
73 {
74 return createDn(m_issuer);
75 }
76
77 /**
78 * Get the subject certificate DN.
79 * Returns: subject DN of this certificate
80 */
81 const(X509DN) subjectDn() const
82 {
83 return createDn(m_subject);
84 }
85
86 /**
87 * Get a value for a specific subject_info parameter name.
88 *
89 * Params:
90 * what = the name of the paramter to look up. Possible names are
91 * "X509.Certificate.version", "X509.Certificate.serial",
92 * "X509.Certificate.start", "X509.Certificate.end",
93 * "X509.Certificate.v2.key_id", "X509.Certificate.public_key",
94 * "X509v3.BasicConstraints.path_constraint",
95 * "X509v3.BasicConstraints.is_ca", "X509v3.ExtendedKeyUsage",
96 * "X509v3.CertificatePolicies", "X509v3.SubjectKeyIdentifier" or
97 * "X509.Certificate.serial".
98 * Returns: value(s) of the specified parameter
99 */
100 const(Vector!string) subjectInfo(in string what) const
101 {
102 return m_subject.get(X509DNImpl.derefInfoField(what));
103 }
104
105 /**
106 * Get a value for a specific subject_info parameter name.
107 *
108 * Params:
109 * what = the name of the paramter to look up. Possible names are
110 * "X509.Certificate.v2.key_id" or "X509v3.AuthorityKeyIdentifier".
111 * Returns: value(s) of the specified parameter
112 */
113 const(Vector!string) issuerInfo(in string what) const
114 {
115 return m_issuer.get(X509DNImpl.derefInfoField(what));
116 }
117
118 /**
119 * Raw subject DN
120 */
121 const(Vector!ubyte) rawIssuerDn() const
122 {
123 return m_issuer.get1Memvec("X509.Certificate.dn_bits");
124 }
125
126
127 /**
128 * Raw issuer DN
129 */
130 const(Vector!ubyte) rawSubjectDn() const
131 {
132 return m_subject.get1Memvec("X509.Certificate.dn_bits");
133 }
134
135 /**
136 * Get the notBefore of the certificate.
137 * Returns: notBefore of the certificate
138 */
139 string startTime() const
140 {
141 return m_subject.get1("X509.Certificate.start");
142 }
143
144 /**
145 * Get the notAfter of the certificate.
146 * Returns: notAfter of the certificate
147 */
148 string endTime() const
149 {
150 return m_subject.get1("X509.Certificate.end");
151 }
152
153 /**
154 * Get the X509 version of this certificate object.
155 * Returns: X509 version
156 */
157 uint x509Version() const
158 {
159 return (m_subject.get1Uint("X509.Certificate.version") + 1);
160 }
161
162 /**
163 * Get the serial number of this certificate.
164 * Returns: certificates serial number
165 */
166 const(Vector!ubyte) serialNumber() const
167 {
168 return m_subject.get1Memvec("X509.Certificate.serial");
169 }
170
171 /**
172 * Get the DER encoded AuthorityKeyIdentifier of this certificate.
173 * Returns: DER encoded AuthorityKeyIdentifier
174 */
175 const(Vector!ubyte) authorityKeyId() const
176 {
177 return m_issuer.get1Memvec("X509v3.AuthorityKeyIdentifier");
178 }
179
180 /**
181 * Get the DER encoded SubjectKeyIdentifier of this certificate.
182 * Returns: DER encoded SubjectKeyIdentifier
183 */
184 const(Vector!ubyte) subjectKeyId() const
185 {
186 return m_subject.get1Memvec("X509v3.SubjectKeyIdentifier");
187 }
188
189 /**
190 * Check whether this certificate is self signed.
191 * Returns: true if this certificate is self signed
192 */
193 bool isSelfSigned() const { return m_self_signed; }
194
195 /**
196 * Check whether this certificate is a CA certificate.
197 * Returns: true if this certificate is a CA certificate
198 */
199 bool isCACert() const
200 {
201 if (!m_subject.get1Uint("X509v3.BasicConstraints.is_ca"))
202 return false;
203
204 return allowedUsage(KeyConstraints.KEY_CERT_SIGN);
205 }
206
207
208 bool allowedUsage(KeyConstraints usage) const
209 {
210 if (constraints() == KeyConstraints.NO_CONSTRAINTS)
211 return true;
212 return cast(bool) (constraints() & usage);
213 }
214
215 /**
216 * Returns true if and only if name (referring to an extended key
217 * constraint, eg "PKIX.ServerAuth") is included in the extended
218 * key extension.
219 */
220 bool allowedUsage(in string usage) const
221 {
222 auto constraints = exConstraints();
223 foreach (constraint; constraints[])
224 if (constraint == usage)
225 return true;
226
227 return false;
228 }
229
230 /**
231 * Get the path limit as defined in the BasicConstraints extension of
232 * this certificate.
233 * Returns: path limit
234 */
235 uint pathLimit() const
236 {
237 return m_subject.get1Uint("X509v3.BasicConstraints.path_constraint", 0);
238 }
239
240 /**
241 * Get the key constraints as defined in the KeyUsage extension of this
242 * certificate.
243 * Returns: key constraints
244 */
245 const(KeyConstraints) constraints() const
246 {
247 return cast(KeyConstraints) m_subject.get1Uint("X509v3.KeyUsage", KeyConstraints.NO_CONSTRAINTS);
248 }
249
250 /**
251 * Get the key constraints as defined in the ExtendedKeyUsage
252 * extension of this
253 * certificate.
254 * Returns: key constraints
255 */
256 const(Vector!string) exConstraints() const
257 {
258 return lookupOids(m_subject.get("X509v3.ExtendedKeyUsage"));
259 }
260
261 /**
262 * Get the policies as defined in the CertificatePolicies extension
263 * of this certificate.
264 * Returns: certificate policies
265 */
266 const(Vector!string) policies() const
267 {
268 return lookupOids(m_subject.get("X509v3.CertificatePolicies"));
269 }
270
271 /**
272 * Return the listed address of an OCSP responder, or empty if not set
273 */
274 string ocspResponder() const
275 {
276 //logTrace("Find OCSP responder in DataStore: ", m_subject.toString());
277 return m_subject.get1("OCSP.responder", "");
278 }
279
280 /**
281 * Return the CRL distribution point, or empty if not set
282 */
283 string crlDistributionPoint() const
284 {
285 import std.range : front;
286 auto crl_dist = m_subject.get("CRL.DistributionPoint");
287 if (crl_dist.length)
288 return crl_dist.front;
289 return "";
290 }
291
292 /**
293 * Returns: a string describing the certificate
294 */
295
296 override string toString() const
297 {
298 import std.array : Appender;
299 __gshared immutable string[] dn_fields = [ "Name",
300 "Email",
301 "Organization",
302 "Organizational Unit",
303 "Locality",
304 "State",
305 "Country",
306 "IP",
307 "DNS",
308 "URI",
309 "PKIX.XMPPAddr" ];
310
311 Appender!string output;
312
313 foreach (const dn_field; dn_fields)
314 {
315 const Vector!string vals = subjectInfo(dn_field);
316
317 if (vals.empty)
318 continue;
319
320 output ~= "Subject " ~ dn_field ~ ":";
321 for (size_t j = 0; j != vals.length; ++j)
322 output ~= " " ~ vals[j];
323 output ~= "\n";
324 }
325
326 foreach (const dn_field; dn_fields)
327 {
328 const Vector!string vals = issuerInfo(dn_field);
329
330 if (vals.empty)
331 continue;
332
333 output ~= "Issuer " ~ dn_field ~ ":";
334 for (size_t j = 0; j != vals.length; ++j)
335 output ~= " " ~ vals[j];
336 output ~= "\n";
337 }
338
339 output ~= "\nVersion: " ~ x509Version().to!string;
340
341 output ~= "\nNot valid before: " ~ startTime();
342 output ~= "\nNot valid after: " ~ endTime();
343
344 output ~= "\nConstraints:";
345 KeyConstraints constraints = constraints();
346 if (constraints == KeyConstraints.NO_CONSTRAINTS)
347 output ~= " None";
348 else
349 {
350 if (constraints & KeyConstraints.DIGITAL_SIGNATURE)
351 output ~= "\n Digital Signature";
352 if (constraints & KeyConstraints.NON_REPUDIATION)
353 output ~= "\n Non-Repuidation";
354 if (constraints & KeyConstraints.KEY_ENCIPHERMENT)
355 output ~= "\n Key Encipherment";
356 if (constraints & KeyConstraints.DATA_ENCIPHERMENT)
357 output ~= "\n Data Encipherment";
358 if (constraints & KeyConstraints.KEY_AGREEMENT)
359 output ~= "\n Key Agreement";
360 if (constraints & KeyConstraints.KEY_CERT_SIGN)
361 output ~= "\n Cert Sign";
362 if (constraints & KeyConstraints.CRL_SIGN)
363 output ~= "\n CRL Sign";
364 }
365
366 const Vector!string policies = policies();
367 if (!policies.empty)
368 {
369 output ~= "\nPolicies: ";
370 foreach (const policy; policies[])
371 output ~= " " ~ policy;
372 }
373
374 const Vector!string ex_constraints = exConstraints();
375 if (!ex_constraints.empty)
376 {
377 output ~= "\nExtended Constraints:";
378 foreach (const ex_constraint; ex_constraints[])
379 output ~= " " ~ ex_constraint;
380 }
381
382 if (ocspResponder() != "")
383 output ~= "\nOCSP responder " ~ ocspResponder();
384 if (crlDistributionPoint() != "")
385 output ~= "\nCRL " ~ crlDistributionPoint();
386
387 output ~= "\nSignature algorithm: " ~ OIDS.lookup(signatureAlgorithm().oid);
388
389 output ~= "\nSerial number: " ~ hexEncode(serialNumber());
390
391 if (authorityKeyId().length)
392 output ~= "\nAuthority keyid: " ~ hexEncode(authorityKeyId());
393
394 if (subjectKeyId().length)
395 output ~= "\nSubject keyid: " ~ hexEncode(subjectKeyId());
396
397 Unique!X509PublicKey pubkey = subjectPublicKey();
398 output ~= "\nPublic Key:\n\n" ~ x509_key.PEM_encode(*pubkey) ~ "\n";
399
400 return output.data;
401 }
402
403
404 /**
405 * Return a fingerprint of the certificate
406 */
407 string fingerprint(in string hash_name) const
408 {
409 Unique!HashFunction hash = retrieveHash(hash_name).clone();
410 hash.update(BER_encode());
411 const auto hex_print = hexEncode(hash.finished());
412
413 Vector!char formatted_print;
414
415 for (size_t i = 0; i != hex_print.length; i += 2)
416 {
417 formatted_print.pushBack(hex_print[i]);
418 formatted_print.pushBack(hex_print[i+1]);
419
420 if (i != hex_print.length - 2)
421 formatted_print.pushBack(':');
422 }
423
424 return formatted_print[].idup;
425 }
426
427 /**
428 * Check if a certain DNS name matches up with the information in
429 * the cert
430 */
431 bool matchesDnsName(in string name) const
432 {
433 if (name == "")
434 return false;
435
436 if (certSubjectDnsMatch(name, subjectInfo("DNS")))
437 return true;
438
439 if (certSubjectDnsMatch(name, subjectInfo("Name")))
440 return true;
441
442 return false;
443 }
444
445 /**
446 * Check to certificates for equality.
447 * Returns: true both certificates are (binary) equal
448 */
449 bool opEquals(in X509Certificate other) const
450 {
451 if (*other is null)
452 return false;
453 return (m_sig == other.m_sig &&
454 m_sig_algo == other.m_sig_algo &&
455 m_self_signed == other.m_self_signed &&
456 m_issuer == other.m_issuer &&
457 m_subject == other.m_subject);
458 }
459
460 /**
461 * Impose an arbitrary (but consistent) ordering
462 * Returns: true if this is less than other by some unspecified criteria
463 */
464 bool opBinary(string op)(in X509Certificate other) const
465 if (op == "<")
466 {
467 /* If signature values are not equal, sort by lexicographic ordering of that */
468 if (sig != other.sig)
469 {
470 if (sig < other.sig)
471 return true;
472 return false;
473 }
474
475 // Then compare the signed contents
476 return tbs_bits < other.tbs_bits;
477 }
478
479 /**
480 * Check two certificates for quality
481 * Returns: true if the arguments represent different certificates,
482 * false if they are binary identical
483 */
484 int opCmp(in X509Certificate cert2)
485 {
486 if (this == cert2) return 0;
487 else return -1;
488 }
489
490 bool isValid() {
491 return !m_subject.get("X509.Certificate.start").empty;
492 }
493
494 /**
495 * Create a certificate from a data source providing the DER or
496 * PEM encoded certificate.
497 *
498 * Params:
499 * input = the data source
500 */
501 this(DataSource input)
502 {
503 super(input, "CERTIFICATE/X509 CERTIFICATE");
504 m_self_signed = false;
505 doDecode();
506 }
507
508 /**
509 * Create a certificate from a file containing the DER or PEM
510 * encoded certificate.
511 *
512 * Params:
513 * filename = the name of the certificate file
514 */
515 this(in string filename)
516 {
517 super(filename, "CERTIFICATE/X509 CERTIFICATE");
518 m_self_signed = false;
519 doDecode();
520 }
521
522 this(ALLOC)(auto const ref Vector!(ubyte, ALLOC) input)
523 {
524 super(input, "CERTIFICATE/X509 CERTIFICATE");
525 m_self_signed = false;
526 doDecode();
527 }
528
529 this(ALLOC)(auto const ref RefCounted!(Vector!(ubyte, ALLOC), ALLOC) input)
530 {
531 super(input, "CERTIFICATE/X509 CERTIFICATE");
532 m_self_signed = false;
533 doDecode();
534 }
535
536 protected:
537 /*
538 * Decode the TBSCertificate data
539 */
540 override void forceDecode()
541 {
542 size_t _version;
543 BigInt serial_bn;
544 auto sig_algo_inner = AlgorithmIdentifier();
545 X509DN dn_issuer, dn_subject;
546 import std.datetime : Clock, UTC;
547 X509Time start = X509Time(Clock.currTime(UTC()));
548 X509Time end = X509Time(Clock.currTime(UTC()));
549
550 BERDecoder tbsCert = BERDecoder(m_tbs_bits);
551 tbsCert.decodeOptional(_version, (cast(ASN1Tag) 0),
552 (ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC))
553 .decode(serial_bn)
554 .decode(sig_algo_inner)
555 .decode(dn_issuer)
556 .startCons(ASN1Tag.SEQUENCE)
557 .decode(start)
558 .decode(end)
559 .verifyEnd()
560 .endCons()
561 .decode(dn_subject);
562
563 if (_version > 2)
564 throw new DecodingError("Unknown X.509 cert version " ~ to!string(_version));
565 if (m_sig_algo != sig_algo_inner)
566 throw new DecodingError("Algorithm identifier mismatch");
567
568 m_self_signed = (dn_subject == dn_issuer);
569 //logTrace("Is self signed: ", m_self_signed);
570 m_subject.add(dn_subject.contents());
571 m_issuer.add(dn_issuer.contents());
572
573 m_subject.add("X509.Certificate.dn_bits", putInSequence(dn_subject.getBits()));
574 m_issuer.add("X509.Certificate.dn_bits", putInSequence(dn_issuer.getBits()));
575
576 BERObject public_key = tbsCert.getNextObject();
577
578 if (public_key.type_tag != ASN1Tag.SEQUENCE || public_key.class_tag != ASN1Tag.CONSTRUCTED)
579 throw new BERBadTag("X509Certificate: Unexpected tag for public key",
580 public_key.type_tag, public_key.class_tag);
581
582 Vector!ubyte v2_issuer_key_id, v2_subject_key_id;
583
584 tbsCert.decodeOptionalString(v2_issuer_key_id, ASN1Tag.BIT_STRING, 1);
585 tbsCert.decodeOptionalString(v2_subject_key_id, ASN1Tag.BIT_STRING, 2);
586
587 BERObject v3_exts_data = tbsCert.getNextObject();
588 if (v3_exts_data.type_tag == 3 &&
589 v3_exts_data.class_tag == (ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC))
590 {
591 X509Extensions extensions = X509Extensions(true);
592
593 BERDecoder(v3_exts_data.value).decode(extensions).verifyEnd();
594
595 extensions.contentsTo(m_subject, m_issuer);
596 }
597 else if (v3_exts_data.type_tag != ASN1Tag.NO_OBJECT)
598 throw new BERBadTag("Unknown tag in X.509 cert", v3_exts_data.type_tag, v3_exts_data.class_tag);
599
600 if (tbsCert.moreItems())
601 throw new DecodingError("TBSCertificate has more items that expected");
602
603 m_subject.add("X509.Certificate.version", _version);
604 m_subject.add("X509.Certificate.serial", BigInt.encode(serial_bn));
605 m_subject.add("X509.Certificate.start", start.readableString());
606 m_subject.add("X509.Certificate.end", end.readableString());
607
608 m_issuer.add("X509.Certificate.v2.key_id", v2_issuer_key_id);
609 m_subject.add("X509.Certificate.v2.key_id", v2_subject_key_id);
610
611 m_subject.add("X509.Certificate.public_key",
612 hexEncode(public_key.value));
613
614 if (m_self_signed && _version == 0)
615 {
616 m_subject.add("X509v3.BasicConstraints.is_ca", 1);
617 m_subject.add("X509v3.BasicConstraints.path_constraint", NO_CERT_PATH_LIMIT);
618 }
619
620 if (isCACert() &&
621 !m_subject.hasValue("X509v3.BasicConstraints.path_constraint"))
622 {
623 const size_t limit = (x509Version() < 3) ? NO_CERT_PATH_LIMIT : 0;
624
625 m_subject.add("X509v3.BasicConstraints.path_constraint", limit);
626 }
627
628 }
629
630
631 this() {}
632
633 DataStore m_subject, m_issuer;
634 bool m_self_signed;
635 }
636
637
638 /*
639 * Data Store Extraction Operations
640 */
641 /*
642 * Create and populate a X509DN
643 */
644 X509DN createDn(in DataStore info)
645 {
646 bool search_for(string key, string val)
647 {
648 return (key.canFind("X520."));
649 }
650 auto names = info.searchFor(&search_for);
651
652 X509DN dn = X509DN();
653
654 foreach (const ref string key, const ref string value; names)
655 dn.addAttribute(key, value);
656
657 return dn;
658 }
659
660
661 /*
662 * Create and populate an AlternativeName
663 */
664 AlternativeName createAltName(in DataStore info)
665 {
666 auto names = info.searchFor((string key, string)
667 { return (key == "RFC822" || key == "DNS" || key == "URI" || key == "IP"); });
668
669 AlternativeName alt_name = AlternativeName();
670
671 foreach (const ref string key, const ref string value; names)
672 alt_name.addAttribute(key, value);
673
674 return alt_name;
675 }
676
677
678
679 /*
680 * Lookup each OID in the vector
681 */
682 Vector!string lookupOids(ALLOC)(auto const ref Vector!(string, ALLOC) input)
683 {
684 Vector!string output = Vector!string();
685
686 foreach (oid_name; input[])
687 output.pushBack(OIDS.lookup(OID(oid_name)));
688 return output;
689 }
690
691
692 bool certSubjectDnsMatch(ALLOC)(in string name,
693 auto const ref Vector!(string, ALLOC) cert_names)
694 {
695 foreach (const cn; cert_names[])
696 {
697 if (cn == name)
698 return true;
699
700 /*
701 * Possible wildcard match. We only support the most basic form of
702 * cert wildcarding ala RFC 2595
703 */
704 if (cn.length > 2 && cn[0] == '*' && cn[1] == '.' && name.length > cn.length)
705 {
706 const string base = cn[1 .. $];
707 size_t start = name.length - base.length;
708 if (name[start .. start + base.length] == base)
709 return true;
710 }
711 }
712
713 return false;
714 }