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().dup; 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 }