1# coding: utf-8 2 3""" 4ASN.1 type classes for X.509 certificates. Exports the following items: 5 6 - Attributes() 7 - Certificate() 8 - Extensions() 9 - GeneralName() 10 - GeneralNames() 11 - Name() 12 13Other type classes are defined that help compose the types listed above. 14""" 15 16from __future__ import unicode_literals, division, absolute_import, print_function 17 18from contextlib import contextmanager 19from encodings import idna # noqa 20import hashlib 21import re 22import socket 23import stringprep 24import sys 25import unicodedata 26 27from ._errors import unwrap 28from ._iri import iri_to_uri, uri_to_iri 29from ._ordereddict import OrderedDict 30from ._types import type_name, str_cls, bytes_to_list 31from .algos import AlgorithmIdentifier, AnyAlgorithmIdentifier, DigestAlgorithm, SignedDigestAlgorithm 32from .core import ( 33 Any, 34 BitString, 35 BMPString, 36 Boolean, 37 Choice, 38 Concat, 39 Enumerated, 40 GeneralizedTime, 41 GeneralString, 42 IA5String, 43 Integer, 44 Null, 45 NumericString, 46 ObjectIdentifier, 47 OctetBitString, 48 OctetString, 49 ParsableOctetString, 50 PrintableString, 51 Sequence, 52 SequenceOf, 53 Set, 54 SetOf, 55 TeletexString, 56 UniversalString, 57 UTCTime, 58 UTF8String, 59 VisibleString, 60 VOID, 61) 62from .keys import PublicKeyInfo 63from .util import int_to_bytes, int_from_bytes, inet_ntop, inet_pton 64 65 66# The structures in this file are taken from https://tools.ietf.org/html/rfc5280 67# and a few other supplementary sources, mostly due to extra supported 68# extension and name OIDs 69 70 71class DNSName(IA5String): 72 73 _encoding = 'idna' 74 _bad_tag = (12, 19) 75 76 def __ne__(self, other): 77 return not self == other 78 79 def __eq__(self, other): 80 """ 81 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.2 82 83 :param other: 84 Another DNSName object 85 86 :return: 87 A boolean 88 """ 89 90 if not isinstance(other, DNSName): 91 return False 92 93 return self.__unicode__().lower() == other.__unicode__().lower() 94 95 def set(self, value): 96 """ 97 Sets the value of the DNS name 98 99 :param value: 100 A unicode string 101 """ 102 103 if not isinstance(value, str_cls): 104 raise TypeError(unwrap( 105 ''' 106 %s value must be a unicode string, not %s 107 ''', 108 type_name(self), 109 type_name(value) 110 )) 111 112 if value.startswith('.'): 113 encoded_value = b'.' + value[1:].encode(self._encoding) 114 else: 115 encoded_value = value.encode(self._encoding) 116 117 self._unicode = value 118 self.contents = encoded_value 119 self._header = None 120 if self._trailer != b'': 121 self._trailer = b'' 122 123 124class URI(IA5String): 125 126 def set(self, value): 127 """ 128 Sets the value of the string 129 130 :param value: 131 A unicode string 132 """ 133 134 if not isinstance(value, str_cls): 135 raise TypeError(unwrap( 136 ''' 137 %s value must be a unicode string, not %s 138 ''', 139 type_name(self), 140 type_name(value) 141 )) 142 143 self._unicode = value 144 self.contents = iri_to_uri(value) 145 self._header = None 146 if self._trailer != b'': 147 self._trailer = b'' 148 149 def __ne__(self, other): 150 return not self == other 151 152 def __eq__(self, other): 153 """ 154 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.4 155 156 :param other: 157 Another URI object 158 159 :return: 160 A boolean 161 """ 162 163 if not isinstance(other, URI): 164 return False 165 166 return iri_to_uri(self.native, True) == iri_to_uri(other.native, True) 167 168 def __unicode__(self): 169 """ 170 :return: 171 A unicode string 172 """ 173 174 if self.contents is None: 175 return '' 176 if self._unicode is None: 177 self._unicode = uri_to_iri(self._merge_chunks()) 178 return self._unicode 179 180 181class EmailAddress(IA5String): 182 183 _contents = None 184 185 # If the value has gone through the .set() method, thus normalizing it 186 _normalized = False 187 188 # In the wild we've seen this encoded as a UTF8String and PrintableString 189 _bad_tag = (12, 19) 190 191 @property 192 def contents(self): 193 """ 194 :return: 195 A byte string of the DER-encoded contents of the sequence 196 """ 197 198 return self._contents 199 200 @contents.setter 201 def contents(self, value): 202 """ 203 :param value: 204 A byte string of the DER-encoded contents of the sequence 205 """ 206 207 self._normalized = False 208 self._contents = value 209 210 def set(self, value): 211 """ 212 Sets the value of the string 213 214 :param value: 215 A unicode string 216 """ 217 218 if not isinstance(value, str_cls): 219 raise TypeError(unwrap( 220 ''' 221 %s value must be a unicode string, not %s 222 ''', 223 type_name(self), 224 type_name(value) 225 )) 226 227 if value.find('@') != -1: 228 mailbox, hostname = value.rsplit('@', 1) 229 encoded_value = mailbox.encode('ascii') + b'@' + hostname.encode('idna') 230 else: 231 encoded_value = value.encode('ascii') 232 233 self._normalized = True 234 self._unicode = value 235 self.contents = encoded_value 236 self._header = None 237 if self._trailer != b'': 238 self._trailer = b'' 239 240 def __unicode__(self): 241 """ 242 :return: 243 A unicode string 244 """ 245 246 # We've seen this in the wild as a PrintableString, and since ascii is a 247 # subset of cp1252, we use the later for decoding to be more user friendly 248 if self._unicode is None: 249 contents = self._merge_chunks() 250 if contents.find(b'@') == -1: 251 self._unicode = contents.decode('cp1252') 252 else: 253 mailbox, hostname = contents.rsplit(b'@', 1) 254 self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna') 255 return self._unicode 256 257 def __ne__(self, other): 258 return not self == other 259 260 def __eq__(self, other): 261 """ 262 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.5 263 264 :param other: 265 Another EmailAddress object 266 267 :return: 268 A boolean 269 """ 270 271 if not isinstance(other, EmailAddress): 272 return False 273 274 if not self._normalized: 275 self.set(self.native) 276 if not other._normalized: 277 other.set(other.native) 278 279 if self._contents.find(b'@') == -1 or other._contents.find(b'@') == -1: 280 return self._contents == other._contents 281 282 other_mailbox, other_hostname = other._contents.rsplit(b'@', 1) 283 mailbox, hostname = self._contents.rsplit(b'@', 1) 284 285 if mailbox != other_mailbox: 286 return False 287 288 if hostname.lower() != other_hostname.lower(): 289 return False 290 291 return True 292 293 294class IPAddress(OctetString): 295 def parse(self, spec=None, spec_params=None): 296 """ 297 This method is not applicable to IP addresses 298 """ 299 300 raise ValueError(unwrap( 301 ''' 302 IP address values can not be parsed 303 ''' 304 )) 305 306 def set(self, value): 307 """ 308 Sets the value of the object 309 310 :param value: 311 A unicode string containing an IPv4 address, IPv4 address with CIDR, 312 an IPv6 address or IPv6 address with CIDR 313 """ 314 315 if not isinstance(value, str_cls): 316 raise TypeError(unwrap( 317 ''' 318 %s value must be a unicode string, not %s 319 ''', 320 type_name(self), 321 type_name(value) 322 )) 323 324 original_value = value 325 326 has_cidr = value.find('/') != -1 327 cidr = 0 328 if has_cidr: 329 parts = value.split('/', 1) 330 value = parts[0] 331 cidr = int(parts[1]) 332 if cidr < 0: 333 raise ValueError(unwrap( 334 ''' 335 %s value contains a CIDR range less than 0 336 ''', 337 type_name(self) 338 )) 339 340 if value.find(':') != -1: 341 family = socket.AF_INET6 342 if cidr > 128: 343 raise ValueError(unwrap( 344 ''' 345 %s value contains a CIDR range bigger than 128, the maximum 346 value for an IPv6 address 347 ''', 348 type_name(self) 349 )) 350 cidr_size = 128 351 else: 352 family = socket.AF_INET 353 if cidr > 32: 354 raise ValueError(unwrap( 355 ''' 356 %s value contains a CIDR range bigger than 32, the maximum 357 value for an IPv4 address 358 ''', 359 type_name(self) 360 )) 361 cidr_size = 32 362 363 cidr_bytes = b'' 364 if has_cidr: 365 cidr_mask = '1' * cidr 366 cidr_mask += '0' * (cidr_size - len(cidr_mask)) 367 cidr_bytes = int_to_bytes(int(cidr_mask, 2)) 368 cidr_bytes = (b'\x00' * ((cidr_size // 8) - len(cidr_bytes))) + cidr_bytes 369 370 self._native = original_value 371 self.contents = inet_pton(family, value) + cidr_bytes 372 self._bytes = self.contents 373 self._header = None 374 if self._trailer != b'': 375 self._trailer = b'' 376 377 @property 378 def native(self): 379 """ 380 The native Python datatype representation of this value 381 382 :return: 383 A unicode string or None 384 """ 385 386 if self.contents is None: 387 return None 388 389 if self._native is None: 390 byte_string = self.__bytes__() 391 byte_len = len(byte_string) 392 value = None 393 cidr_int = None 394 if byte_len in set([32, 16]): 395 value = inet_ntop(socket.AF_INET6, byte_string[0:16]) 396 if byte_len > 16: 397 cidr_int = int_from_bytes(byte_string[16:]) 398 elif byte_len in set([8, 4]): 399 value = inet_ntop(socket.AF_INET, byte_string[0:4]) 400 if byte_len > 4: 401 cidr_int = int_from_bytes(byte_string[4:]) 402 if cidr_int is not None: 403 cidr_bits = '{0:b}'.format(cidr_int) 404 cidr = len(cidr_bits.rstrip('0')) 405 value = value + '/' + str_cls(cidr) 406 self._native = value 407 return self._native 408 409 def __ne__(self, other): 410 return not self == other 411 412 def __eq__(self, other): 413 """ 414 :param other: 415 Another IPAddress object 416 417 :return: 418 A boolean 419 """ 420 421 if not isinstance(other, IPAddress): 422 return False 423 424 return self.__bytes__() == other.__bytes__() 425 426 427class Attribute(Sequence): 428 _fields = [ 429 ('type', ObjectIdentifier), 430 ('values', SetOf, {'spec': Any}), 431 ] 432 433 434class Attributes(SequenceOf): 435 _child_spec = Attribute 436 437 438class KeyUsage(BitString): 439 _map = { 440 0: 'digital_signature', 441 1: 'non_repudiation', 442 2: 'key_encipherment', 443 3: 'data_encipherment', 444 4: 'key_agreement', 445 5: 'key_cert_sign', 446 6: 'crl_sign', 447 7: 'encipher_only', 448 8: 'decipher_only', 449 } 450 451 452class PrivateKeyUsagePeriod(Sequence): 453 _fields = [ 454 ('not_before', GeneralizedTime, {'implicit': 0, 'optional': True}), 455 ('not_after', GeneralizedTime, {'implicit': 1, 'optional': True}), 456 ] 457 458 459class NotReallyTeletexString(TeletexString): 460 """ 461 OpenSSL (and probably some other libraries) puts ISO-8859-1 462 into TeletexString instead of ITU T.61. We use Windows-1252 when 463 decoding since it is a superset of ISO-8859-1, and less likely to 464 cause encoding issues, but we stay strict with encoding to prevent 465 us from creating bad data. 466 """ 467 468 _decoding_encoding = 'cp1252' 469 470 def __unicode__(self): 471 """ 472 :return: 473 A unicode string 474 """ 475 476 if self.contents is None: 477 return '' 478 if self._unicode is None: 479 self._unicode = self._merge_chunks().decode(self._decoding_encoding) 480 return self._unicode 481 482 483@contextmanager 484def strict_teletex(): 485 try: 486 NotReallyTeletexString._decoding_encoding = 'teletex' 487 yield 488 finally: 489 NotReallyTeletexString._decoding_encoding = 'cp1252' 490 491 492class DirectoryString(Choice): 493 _alternatives = [ 494 ('teletex_string', NotReallyTeletexString), 495 ('printable_string', PrintableString), 496 ('universal_string', UniversalString), 497 ('utf8_string', UTF8String), 498 ('bmp_string', BMPString), 499 # This is an invalid/bad alternative, but some broken certs use it 500 ('ia5_string', IA5String), 501 ] 502 503 504class NameType(ObjectIdentifier): 505 _map = { 506 '2.5.4.3': 'common_name', 507 '2.5.4.4': 'surname', 508 '2.5.4.5': 'serial_number', 509 '2.5.4.6': 'country_name', 510 '2.5.4.7': 'locality_name', 511 '2.5.4.8': 'state_or_province_name', 512 '2.5.4.9': 'street_address', 513 '2.5.4.10': 'organization_name', 514 '2.5.4.11': 'organizational_unit_name', 515 '2.5.4.12': 'title', 516 '2.5.4.15': 'business_category', 517 '2.5.4.17': 'postal_code', 518 '2.5.4.20': 'telephone_number', 519 '2.5.4.41': 'name', 520 '2.5.4.42': 'given_name', 521 '2.5.4.43': 'initials', 522 '2.5.4.44': 'generation_qualifier', 523 '2.5.4.45': 'unique_identifier', 524 '2.5.4.46': 'dn_qualifier', 525 '2.5.4.65': 'pseudonym', 526 '2.5.4.97': 'organization_identifier', 527 # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf 528 '2.23.133.2.1': 'tpm_manufacturer', 529 '2.23.133.2.2': 'tpm_model', 530 '2.23.133.2.3': 'tpm_version', 531 '2.23.133.2.4': 'platform_manufacturer', 532 '2.23.133.2.5': 'platform_model', 533 '2.23.133.2.6': 'platform_version', 534 # https://tools.ietf.org/html/rfc2985#page-26 535 '1.2.840.113549.1.9.1': 'email_address', 536 # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf 537 '1.3.6.1.4.1.311.60.2.1.1': 'incorporation_locality', 538 '1.3.6.1.4.1.311.60.2.1.2': 'incorporation_state_or_province', 539 '1.3.6.1.4.1.311.60.2.1.3': 'incorporation_country', 540 # https://tools.ietf.org/html/rfc4519#section-2.39 541 '0.9.2342.19200300.100.1.1': 'user_id', 542 # https://tools.ietf.org/html/rfc2247#section-4 543 '0.9.2342.19200300.100.1.25': 'domain_component', 544 # http://www.alvestrand.no/objectid/0.2.262.1.10.7.20.html 545 '0.2.262.1.10.7.20': 'name_distinguisher', 546 } 547 548 # This order is largely based on observed order seen in EV certs from 549 # Symantec and DigiCert. Some of the uncommon name-related fields are 550 # just placed in what seems like a reasonable order. 551 preferred_order = [ 552 'incorporation_country', 553 'incorporation_state_or_province', 554 'incorporation_locality', 555 'business_category', 556 'serial_number', 557 'country_name', 558 'postal_code', 559 'state_or_province_name', 560 'locality_name', 561 'street_address', 562 'organization_name', 563 'organizational_unit_name', 564 'title', 565 'common_name', 566 'user_id', 567 'initials', 568 'generation_qualifier', 569 'surname', 570 'given_name', 571 'name', 572 'pseudonym', 573 'dn_qualifier', 574 'telephone_number', 575 'email_address', 576 'domain_component', 577 'name_distinguisher', 578 'organization_identifier', 579 'tpm_manufacturer', 580 'tpm_model', 581 'tpm_version', 582 'platform_manufacturer', 583 'platform_model', 584 'platform_version', 585 ] 586 587 @classmethod 588 def preferred_ordinal(cls, attr_name): 589 """ 590 Returns an ordering value for a particular attribute key. 591 592 Unrecognized attributes and OIDs will be sorted lexically at the end. 593 594 :return: 595 An orderable value. 596 597 """ 598 599 attr_name = cls.map(attr_name) 600 if attr_name in cls.preferred_order: 601 ordinal = cls.preferred_order.index(attr_name) 602 else: 603 ordinal = len(cls.preferred_order) 604 605 return (ordinal, attr_name) 606 607 @property 608 def human_friendly(self): 609 """ 610 :return: 611 A human-friendly unicode string to display to users 612 """ 613 614 return { 615 'common_name': 'Common Name', 616 'surname': 'Surname', 617 'serial_number': 'Serial Number', 618 'country_name': 'Country', 619 'locality_name': 'Locality', 620 'state_or_province_name': 'State/Province', 621 'street_address': 'Street Address', 622 'organization_name': 'Organization', 623 'organizational_unit_name': 'Organizational Unit', 624 'title': 'Title', 625 'business_category': 'Business Category', 626 'postal_code': 'Postal Code', 627 'telephone_number': 'Telephone Number', 628 'name': 'Name', 629 'given_name': 'Given Name', 630 'initials': 'Initials', 631 'generation_qualifier': 'Generation Qualifier', 632 'unique_identifier': 'Unique Identifier', 633 'dn_qualifier': 'DN Qualifier', 634 'pseudonym': 'Pseudonym', 635 'email_address': 'Email Address', 636 'incorporation_locality': 'Incorporation Locality', 637 'incorporation_state_or_province': 'Incorporation State/Province', 638 'incorporation_country': 'Incorporation Country', 639 'domain_component': 'Domain Component', 640 'name_distinguisher': 'Name Distinguisher', 641 'organization_identifier': 'Organization Identifier', 642 'tpm_manufacturer': 'TPM Manufacturer', 643 'tpm_model': 'TPM Model', 644 'tpm_version': 'TPM Version', 645 'platform_manufacturer': 'Platform Manufacturer', 646 'platform_model': 'Platform Model', 647 'platform_version': 'Platform Version', 648 'user_id': 'User ID', 649 }.get(self.native, self.native) 650 651 652class NameTypeAndValue(Sequence): 653 _fields = [ 654 ('type', NameType), 655 ('value', Any), 656 ] 657 658 _oid_pair = ('type', 'value') 659 _oid_specs = { 660 'common_name': DirectoryString, 661 'surname': DirectoryString, 662 'serial_number': DirectoryString, 663 'country_name': DirectoryString, 664 'locality_name': DirectoryString, 665 'state_or_province_name': DirectoryString, 666 'street_address': DirectoryString, 667 'organization_name': DirectoryString, 668 'organizational_unit_name': DirectoryString, 669 'title': DirectoryString, 670 'business_category': DirectoryString, 671 'postal_code': DirectoryString, 672 'telephone_number': PrintableString, 673 'name': DirectoryString, 674 'given_name': DirectoryString, 675 'initials': DirectoryString, 676 'generation_qualifier': DirectoryString, 677 'unique_identifier': OctetBitString, 678 'dn_qualifier': DirectoryString, 679 'pseudonym': DirectoryString, 680 # https://tools.ietf.org/html/rfc2985#page-26 681 'email_address': EmailAddress, 682 # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf 683 'incorporation_locality': DirectoryString, 684 'incorporation_state_or_province': DirectoryString, 685 'incorporation_country': DirectoryString, 686 'domain_component': DNSName, 687 'name_distinguisher': DirectoryString, 688 'organization_identifier': DirectoryString, 689 'tpm_manufacturer': UTF8String, 690 'tpm_model': UTF8String, 691 'tpm_version': UTF8String, 692 'platform_manufacturer': UTF8String, 693 'platform_model': UTF8String, 694 'platform_version': UTF8String, 695 'user_id': DirectoryString, 696 } 697 698 _prepped = None 699 700 @property 701 def prepped_value(self): 702 """ 703 Returns the value after being processed by the internationalized string 704 preparation as specified by RFC 5280 705 706 :return: 707 A unicode string 708 """ 709 710 if self._prepped is None: 711 self._prepped = self._ldap_string_prep(self['value'].native) 712 return self._prepped 713 714 def __ne__(self, other): 715 return not self == other 716 717 def __eq__(self, other): 718 """ 719 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1 720 721 :param other: 722 Another NameTypeAndValue object 723 724 :return: 725 A boolean 726 """ 727 728 if not isinstance(other, NameTypeAndValue): 729 return False 730 731 if other['type'].native != self['type'].native: 732 return False 733 734 return other.prepped_value == self.prepped_value 735 736 def _ldap_string_prep(self, string): 737 """ 738 Implements the internationalized string preparation algorithm from 739 RFC 4518. https://tools.ietf.org/html/rfc4518#section-2 740 741 :param string: 742 A unicode string to prepare 743 744 :return: 745 A prepared unicode string, ready for comparison 746 """ 747 748 # Map step 749 string = re.sub('[\u00ad\u1806\u034f\u180b-\u180d\ufe0f-\uff00\ufffc]+', '', string) 750 string = re.sub('[\u0009\u000a\u000b\u000c\u000d\u0085]', ' ', string) 751 if sys.maxunicode == 0xffff: 752 # Some installs of Python 2.7 don't support 8-digit unicode escape 753 # ranges, so we have to break them into pieces 754 # Original was: \U0001D173-\U0001D17A and \U000E0020-\U000E007F 755 string = re.sub('\ud834[\udd73-\udd7a]|\udb40[\udc20-\udc7f]|\U000e0001', '', string) 756 else: 757 string = re.sub('[\U0001D173-\U0001D17A\U000E0020-\U000E007F\U000e0001]', '', string) 758 string = re.sub( 759 '[\u0000-\u0008\u000e-\u001f\u007f-\u0084\u0086-\u009f\u06dd\u070f\u180e\u200c-\u200f' 760 '\u202a-\u202e\u2060-\u2063\u206a-\u206f\ufeff\ufff9-\ufffb]+', 761 '', 762 string 763 ) 764 string = string.replace('\u200b', '') 765 string = re.sub('[\u00a0\u1680\u2000-\u200a\u2028-\u2029\u202f\u205f\u3000]', ' ', string) 766 767 string = ''.join(map(stringprep.map_table_b2, string)) 768 769 # Normalize step 770 string = unicodedata.normalize('NFKC', string) 771 772 # Prohibit step 773 for char in string: 774 if stringprep.in_table_a1(char): 775 raise ValueError(unwrap( 776 ''' 777 X.509 Name objects may not contain unassigned code points 778 ''' 779 )) 780 781 if stringprep.in_table_c8(char): 782 raise ValueError(unwrap( 783 ''' 784 X.509 Name objects may not contain change display or 785 zzzzdeprecated characters 786 ''' 787 )) 788 789 if stringprep.in_table_c3(char): 790 raise ValueError(unwrap( 791 ''' 792 X.509 Name objects may not contain private use characters 793 ''' 794 )) 795 796 if stringprep.in_table_c4(char): 797 raise ValueError(unwrap( 798 ''' 799 X.509 Name objects may not contain non-character code points 800 ''' 801 )) 802 803 if stringprep.in_table_c5(char): 804 raise ValueError(unwrap( 805 ''' 806 X.509 Name objects may not contain surrogate code points 807 ''' 808 )) 809 810 if char == '\ufffd': 811 raise ValueError(unwrap( 812 ''' 813 X.509 Name objects may not contain the replacement character 814 ''' 815 )) 816 817 # Check bidirectional step - here we ensure that we are not mixing 818 # left-to-right and right-to-left text in the string 819 has_r_and_al_cat = False 820 has_l_cat = False 821 for char in string: 822 if stringprep.in_table_d1(char): 823 has_r_and_al_cat = True 824 elif stringprep.in_table_d2(char): 825 has_l_cat = True 826 827 if has_r_and_al_cat: 828 first_is_r_and_al = stringprep.in_table_d1(string[0]) 829 last_is_r_and_al = stringprep.in_table_d1(string[-1]) 830 831 if has_l_cat or not first_is_r_and_al or not last_is_r_and_al: 832 raise ValueError(unwrap( 833 ''' 834 X.509 Name object contains a malformed bidirectional 835 sequence 836 ''' 837 )) 838 839 # Insignificant space handling step 840 string = ' ' + re.sub(' +', ' ', string).strip() + ' ' 841 842 return string 843 844 845class RelativeDistinguishedName(SetOf): 846 _child_spec = NameTypeAndValue 847 848 @property 849 def hashable(self): 850 """ 851 :return: 852 A unicode string that can be used as a dict key or in a set 853 """ 854 855 output = [] 856 values = self._get_values(self) 857 for key in sorted(values.keys()): 858 output.append('%s: %s' % (key, values[key])) 859 # Unit separator is used here since the normalization process for 860 # values moves any such character, and the keys are all dotted integers 861 # or under_score_words 862 return '\x1F'.join(output) 863 864 def __ne__(self, other): 865 return not self == other 866 867 def __eq__(self, other): 868 """ 869 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1 870 871 :param other: 872 Another RelativeDistinguishedName object 873 874 :return: 875 A boolean 876 """ 877 878 if not isinstance(other, RelativeDistinguishedName): 879 return False 880 881 if len(self) != len(other): 882 return False 883 884 self_types = self._get_types(self) 885 other_types = self._get_types(other) 886 887 if self_types != other_types: 888 return False 889 890 self_values = self._get_values(self) 891 other_values = self._get_values(other) 892 893 for type_name_ in self_types: 894 if self_values[type_name_] != other_values[type_name_]: 895 return False 896 897 return True 898 899 def _get_types(self, rdn): 900 """ 901 Returns a set of types contained in an RDN 902 903 :param rdn: 904 A RelativeDistinguishedName object 905 906 :return: 907 A set object with unicode strings of NameTypeAndValue type field 908 values 909 """ 910 911 return set([ntv['type'].native for ntv in rdn]) 912 913 def _get_values(self, rdn): 914 """ 915 Returns a dict of prepped values contained in an RDN 916 917 :param rdn: 918 A RelativeDistinguishedName object 919 920 :return: 921 A dict object with unicode strings of NameTypeAndValue value field 922 values that have been prepped for comparison 923 """ 924 925 output = {} 926 [output.update([(ntv['type'].native, ntv.prepped_value)]) for ntv in rdn] 927 return output 928 929 930class RDNSequence(SequenceOf): 931 _child_spec = RelativeDistinguishedName 932 933 @property 934 def hashable(self): 935 """ 936 :return: 937 A unicode string that can be used as a dict key or in a set 938 """ 939 940 # Record separator is used here since the normalization process for 941 # values moves any such character, and the keys are all dotted integers 942 # or under_score_words 943 return '\x1E'.join(rdn.hashable for rdn in self) 944 945 def __ne__(self, other): 946 return not self == other 947 948 def __eq__(self, other): 949 """ 950 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1 951 952 :param other: 953 Another RDNSequence object 954 955 :return: 956 A boolean 957 """ 958 959 if not isinstance(other, RDNSequence): 960 return False 961 962 if len(self) != len(other): 963 return False 964 965 for index, self_rdn in enumerate(self): 966 if other[index] != self_rdn: 967 return False 968 969 return True 970 971 972class Name(Choice): 973 _alternatives = [ 974 ('', RDNSequence), 975 ] 976 977 _human_friendly = None 978 _sha1 = None 979 _sha256 = None 980 981 @classmethod 982 def build(cls, name_dict, use_printable=False): 983 """ 984 Creates a Name object from a dict of unicode string keys and values. 985 The keys should be from NameType._map, or a dotted-integer OID unicode 986 string. 987 988 :param name_dict: 989 A dict of name information, e.g. {"common_name": "Will Bond", 990 "country_name": "US", "organization_name": "Codex Non Sufficit LC"} 991 992 :param use_printable: 993 A bool - if PrintableString should be used for encoding instead of 994 UTF8String. This is for backwards compatibility with old software. 995 996 :return: 997 An x509.Name object 998 """ 999 1000 rdns = [] 1001 if not use_printable: 1002 encoding_name = 'utf8_string' 1003 encoding_class = UTF8String 1004 else: 1005 encoding_name = 'printable_string' 1006 encoding_class = PrintableString 1007 1008 # Sort the attributes according to NameType.preferred_order 1009 name_dict = OrderedDict( 1010 sorted( 1011 name_dict.items(), 1012 key=lambda item: NameType.preferred_ordinal(item[0]) 1013 ) 1014 ) 1015 1016 for attribute_name, attribute_value in name_dict.items(): 1017 attribute_name = NameType.map(attribute_name) 1018 if attribute_name == 'email_address': 1019 value = EmailAddress(attribute_value) 1020 elif attribute_name == 'domain_component': 1021 value = DNSName(attribute_value) 1022 elif attribute_name in set(['dn_qualifier', 'country_name', 'serial_number']): 1023 value = DirectoryString( 1024 name='printable_string', 1025 value=PrintableString(attribute_value) 1026 ) 1027 else: 1028 value = DirectoryString( 1029 name=encoding_name, 1030 value=encoding_class(attribute_value) 1031 ) 1032 1033 rdns.append(RelativeDistinguishedName([ 1034 NameTypeAndValue({ 1035 'type': attribute_name, 1036 'value': value 1037 }) 1038 ])) 1039 1040 return cls(name='', value=RDNSequence(rdns)) 1041 1042 @property 1043 def hashable(self): 1044 """ 1045 :return: 1046 A unicode string that can be used as a dict key or in a set 1047 """ 1048 1049 return self.chosen.hashable 1050 1051 def __len__(self): 1052 return len(self.chosen) 1053 1054 def __ne__(self, other): 1055 return not self == other 1056 1057 def __eq__(self, other): 1058 """ 1059 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1 1060 1061 :param other: 1062 Another Name object 1063 1064 :return: 1065 A boolean 1066 """ 1067 1068 if not isinstance(other, Name): 1069 return False 1070 return self.chosen == other.chosen 1071 1072 @property 1073 def native(self): 1074 if self._native is None: 1075 self._native = OrderedDict() 1076 for rdn in self.chosen.native: 1077 for type_val in rdn: 1078 field_name = type_val['type'] 1079 if field_name in self._native: 1080 existing = self._native[field_name] 1081 if not isinstance(existing, list): 1082 existing = self._native[field_name] = [existing] 1083 existing.append(type_val['value']) 1084 else: 1085 self._native[field_name] = type_val['value'] 1086 return self._native 1087 1088 @property 1089 def human_friendly(self): 1090 """ 1091 :return: 1092 A human-friendly unicode string containing the parts of the name 1093 """ 1094 1095 if self._human_friendly is None: 1096 data = OrderedDict() 1097 last_field = None 1098 for rdn in self.chosen: 1099 for type_val in rdn: 1100 field_name = type_val['type'].human_friendly 1101 last_field = field_name 1102 if field_name in data: 1103 data[field_name] = [data[field_name]] 1104 data[field_name].append(type_val['value']) 1105 else: 1106 data[field_name] = type_val['value'] 1107 to_join = [] 1108 keys = data.keys() 1109 if last_field == 'Country': 1110 keys = reversed(list(keys)) 1111 for key in keys: 1112 value = data[key] 1113 native_value = self._recursive_humanize(value) 1114 to_join.append('%s: %s' % (key, native_value)) 1115 1116 has_comma = False 1117 for element in to_join: 1118 if element.find(',') != -1: 1119 has_comma = True 1120 break 1121 1122 separator = ', ' if not has_comma else '; ' 1123 self._human_friendly = separator.join(to_join[::-1]) 1124 1125 return self._human_friendly 1126 1127 def _recursive_humanize(self, value): 1128 """ 1129 Recursively serializes data compiled from the RDNSequence 1130 1131 :param value: 1132 An Asn1Value object, or a list of Asn1Value objects 1133 1134 :return: 1135 A unicode string 1136 """ 1137 1138 if isinstance(value, list): 1139 return ', '.join( 1140 reversed([self._recursive_humanize(sub_value) for sub_value in value]) 1141 ) 1142 return value.native 1143 1144 @property 1145 def sha1(self): 1146 """ 1147 :return: 1148 The SHA1 hash of the DER-encoded bytes of this name 1149 """ 1150 1151 if self._sha1 is None: 1152 self._sha1 = hashlib.sha1(self.dump()).digest() 1153 return self._sha1 1154 1155 @property 1156 def sha256(self): 1157 """ 1158 :return: 1159 The SHA-256 hash of the DER-encoded bytes of this name 1160 """ 1161 1162 if self._sha256 is None: 1163 self._sha256 = hashlib.sha256(self.dump()).digest() 1164 return self._sha256 1165 1166 1167class AnotherName(Sequence): 1168 _fields = [ 1169 ('type_id', ObjectIdentifier), 1170 ('value', Any, {'explicit': 0}), 1171 ] 1172 1173 1174class CountryName(Choice): 1175 class_ = 1 1176 tag = 1 1177 1178 _alternatives = [ 1179 ('x121_dcc_code', NumericString), 1180 ('iso_3166_alpha2_code', PrintableString), 1181 ] 1182 1183 1184class AdministrationDomainName(Choice): 1185 class_ = 1 1186 tag = 2 1187 1188 _alternatives = [ 1189 ('numeric', NumericString), 1190 ('printable', PrintableString), 1191 ] 1192 1193 1194class PrivateDomainName(Choice): 1195 _alternatives = [ 1196 ('numeric', NumericString), 1197 ('printable', PrintableString), 1198 ] 1199 1200 1201class PersonalName(Set): 1202 _fields = [ 1203 ('surname', PrintableString, {'implicit': 0}), 1204 ('given_name', PrintableString, {'implicit': 1, 'optional': True}), 1205 ('initials', PrintableString, {'implicit': 2, 'optional': True}), 1206 ('generation_qualifier', PrintableString, {'implicit': 3, 'optional': True}), 1207 ] 1208 1209 1210class TeletexPersonalName(Set): 1211 _fields = [ 1212 ('surname', TeletexString, {'implicit': 0}), 1213 ('given_name', TeletexString, {'implicit': 1, 'optional': True}), 1214 ('initials', TeletexString, {'implicit': 2, 'optional': True}), 1215 ('generation_qualifier', TeletexString, {'implicit': 3, 'optional': True}), 1216 ] 1217 1218 1219class OrganizationalUnitNames(SequenceOf): 1220 _child_spec = PrintableString 1221 1222 1223class TeletexOrganizationalUnitNames(SequenceOf): 1224 _child_spec = TeletexString 1225 1226 1227class BuiltInStandardAttributes(Sequence): 1228 _fields = [ 1229 ('country_name', CountryName, {'optional': True}), 1230 ('administration_domain_name', AdministrationDomainName, {'optional': True}), 1231 ('network_address', NumericString, {'implicit': 0, 'optional': True}), 1232 ('terminal_identifier', PrintableString, {'implicit': 1, 'optional': True}), 1233 ('private_domain_name', PrivateDomainName, {'explicit': 2, 'optional': True}), 1234 ('organization_name', PrintableString, {'implicit': 3, 'optional': True}), 1235 ('numeric_user_identifier', NumericString, {'implicit': 4, 'optional': True}), 1236 ('personal_name', PersonalName, {'implicit': 5, 'optional': True}), 1237 ('organizational_unit_names', OrganizationalUnitNames, {'implicit': 6, 'optional': True}), 1238 ] 1239 1240 1241class BuiltInDomainDefinedAttribute(Sequence): 1242 _fields = [ 1243 ('type', PrintableString), 1244 ('value', PrintableString), 1245 ] 1246 1247 1248class BuiltInDomainDefinedAttributes(SequenceOf): 1249 _child_spec = BuiltInDomainDefinedAttribute 1250 1251 1252class TeletexDomainDefinedAttribute(Sequence): 1253 _fields = [ 1254 ('type', TeletexString), 1255 ('value', TeletexString), 1256 ] 1257 1258 1259class TeletexDomainDefinedAttributes(SequenceOf): 1260 _child_spec = TeletexDomainDefinedAttribute 1261 1262 1263class PhysicalDeliveryCountryName(Choice): 1264 _alternatives = [ 1265 ('x121_dcc_code', NumericString), 1266 ('iso_3166_alpha2_code', PrintableString), 1267 ] 1268 1269 1270class PostalCode(Choice): 1271 _alternatives = [ 1272 ('numeric_code', NumericString), 1273 ('printable_code', PrintableString), 1274 ] 1275 1276 1277class PDSParameter(Set): 1278 _fields = [ 1279 ('printable_string', PrintableString, {'optional': True}), 1280 ('teletex_string', TeletexString, {'optional': True}), 1281 ] 1282 1283 1284class PrintableAddress(SequenceOf): 1285 _child_spec = PrintableString 1286 1287 1288class UnformattedPostalAddress(Set): 1289 _fields = [ 1290 ('printable_address', PrintableAddress, {'optional': True}), 1291 ('teletex_string', TeletexString, {'optional': True}), 1292 ] 1293 1294 1295class E1634Address(Sequence): 1296 _fields = [ 1297 ('number', NumericString, {'implicit': 0}), 1298 ('sub_address', NumericString, {'implicit': 1, 'optional': True}), 1299 ] 1300 1301 1302class NAddresses(SetOf): 1303 _child_spec = OctetString 1304 1305 1306class PresentationAddress(Sequence): 1307 _fields = [ 1308 ('p_selector', OctetString, {'explicit': 0, 'optional': True}), 1309 ('s_selector', OctetString, {'explicit': 1, 'optional': True}), 1310 ('t_selector', OctetString, {'explicit': 2, 'optional': True}), 1311 ('n_addresses', NAddresses, {'explicit': 3}), 1312 ] 1313 1314 1315class ExtendedNetworkAddress(Choice): 1316 _alternatives = [ 1317 ('e163_4_address', E1634Address), 1318 ('psap_address', PresentationAddress, {'implicit': 0}) 1319 ] 1320 1321 1322class TerminalType(Integer): 1323 _map = { 1324 3: 'telex', 1325 4: 'teletex', 1326 5: 'g3_facsimile', 1327 6: 'g4_facsimile', 1328 7: 'ia5_terminal', 1329 8: 'videotex', 1330 } 1331 1332 1333class ExtensionAttributeType(Integer): 1334 _map = { 1335 1: 'common_name', 1336 2: 'teletex_common_name', 1337 3: 'teletex_organization_name', 1338 4: 'teletex_personal_name', 1339 5: 'teletex_organization_unit_names', 1340 6: 'teletex_domain_defined_attributes', 1341 7: 'pds_name', 1342 8: 'physical_delivery_country_name', 1343 9: 'postal_code', 1344 10: 'physical_delivery_office_name', 1345 11: 'physical_delivery_office_number', 1346 12: 'extension_of_address_components', 1347 13: 'physical_delivery_personal_name', 1348 14: 'physical_delivery_organization_name', 1349 15: 'extension_physical_delivery_address_components', 1350 16: 'unformatted_postal_address', 1351 17: 'street_address', 1352 18: 'post_office_box_address', 1353 19: 'poste_restante_address', 1354 20: 'unique_postal_name', 1355 21: 'local_postal_attributes', 1356 22: 'extended_network_address', 1357 23: 'terminal_type', 1358 } 1359 1360 1361class ExtensionAttribute(Sequence): 1362 _fields = [ 1363 ('extension_attribute_type', ExtensionAttributeType, {'implicit': 0}), 1364 ('extension_attribute_value', Any, {'explicit': 1}), 1365 ] 1366 1367 _oid_pair = ('extension_attribute_type', 'extension_attribute_value') 1368 _oid_specs = { 1369 'common_name': PrintableString, 1370 'teletex_common_name': TeletexString, 1371 'teletex_organization_name': TeletexString, 1372 'teletex_personal_name': TeletexPersonalName, 1373 'teletex_organization_unit_names': TeletexOrganizationalUnitNames, 1374 'teletex_domain_defined_attributes': TeletexDomainDefinedAttributes, 1375 'pds_name': PrintableString, 1376 'physical_delivery_country_name': PhysicalDeliveryCountryName, 1377 'postal_code': PostalCode, 1378 'physical_delivery_office_name': PDSParameter, 1379 'physical_delivery_office_number': PDSParameter, 1380 'extension_of_address_components': PDSParameter, 1381 'physical_delivery_personal_name': PDSParameter, 1382 'physical_delivery_organization_name': PDSParameter, 1383 'extension_physical_delivery_address_components': PDSParameter, 1384 'unformatted_postal_address': UnformattedPostalAddress, 1385 'street_address': PDSParameter, 1386 'post_office_box_address': PDSParameter, 1387 'poste_restante_address': PDSParameter, 1388 'unique_postal_name': PDSParameter, 1389 'local_postal_attributes': PDSParameter, 1390 'extended_network_address': ExtendedNetworkAddress, 1391 'terminal_type': TerminalType, 1392 } 1393 1394 1395class ExtensionAttributes(SequenceOf): 1396 _child_spec = ExtensionAttribute 1397 1398 1399class ORAddress(Sequence): 1400 _fields = [ 1401 ('built_in_standard_attributes', BuiltInStandardAttributes), 1402 ('built_in_domain_defined_attributes', BuiltInDomainDefinedAttributes, {'optional': True}), 1403 ('extension_attributes', ExtensionAttributes, {'optional': True}), 1404 ] 1405 1406 1407class EDIPartyName(Sequence): 1408 _fields = [ 1409 ('name_assigner', DirectoryString, {'implicit': 0, 'optional': True}), 1410 ('party_name', DirectoryString, {'implicit': 1}), 1411 ] 1412 1413 1414class GeneralName(Choice): 1415 _alternatives = [ 1416 ('other_name', AnotherName, {'implicit': 0}), 1417 ('rfc822_name', EmailAddress, {'implicit': 1}), 1418 ('dns_name', DNSName, {'implicit': 2}), 1419 ('x400_address', ORAddress, {'implicit': 3}), 1420 ('directory_name', Name, {'explicit': 4}), 1421 ('edi_party_name', EDIPartyName, {'implicit': 5}), 1422 ('uniform_resource_identifier', URI, {'implicit': 6}), 1423 ('ip_address', IPAddress, {'implicit': 7}), 1424 ('registered_id', ObjectIdentifier, {'implicit': 8}), 1425 ] 1426 1427 def __ne__(self, other): 1428 return not self == other 1429 1430 def __eq__(self, other): 1431 """ 1432 Does not support other_name, x400_address or edi_party_name 1433 1434 :param other: 1435 The other GeneralName to compare to 1436 1437 :return: 1438 A boolean 1439 """ 1440 1441 if self.name in ('other_name', 'x400_address', 'edi_party_name'): 1442 raise ValueError(unwrap( 1443 ''' 1444 Comparison is not supported for GeneralName objects of 1445 choice %s 1446 ''', 1447 self.name 1448 )) 1449 1450 if other.name in ('other_name', 'x400_address', 'edi_party_name'): 1451 raise ValueError(unwrap( 1452 ''' 1453 Comparison is not supported for GeneralName objects of choice 1454 %s''', 1455 other.name 1456 )) 1457 1458 if self.name != other.name: 1459 return False 1460 1461 return self.chosen == other.chosen 1462 1463 1464class GeneralNames(SequenceOf): 1465 _child_spec = GeneralName 1466 1467 1468class Time(Choice): 1469 _alternatives = [ 1470 ('utc_time', UTCTime), 1471 ('general_time', GeneralizedTime), 1472 ] 1473 1474 1475class Validity(Sequence): 1476 _fields = [ 1477 ('not_before', Time), 1478 ('not_after', Time), 1479 ] 1480 1481 1482class BasicConstraints(Sequence): 1483 _fields = [ 1484 ('ca', Boolean, {'default': False}), 1485 ('path_len_constraint', Integer, {'optional': True}), 1486 ] 1487 1488 1489class AuthorityKeyIdentifier(Sequence): 1490 _fields = [ 1491 ('key_identifier', OctetString, {'implicit': 0, 'optional': True}), 1492 ('authority_cert_issuer', GeneralNames, {'implicit': 1, 'optional': True}), 1493 ('authority_cert_serial_number', Integer, {'implicit': 2, 'optional': True}), 1494 ] 1495 1496 1497class DistributionPointName(Choice): 1498 _alternatives = [ 1499 ('full_name', GeneralNames, {'implicit': 0}), 1500 ('name_relative_to_crl_issuer', RelativeDistinguishedName, {'implicit': 1}), 1501 ] 1502 1503 1504class ReasonFlags(BitString): 1505 _map = { 1506 0: 'unused', 1507 1: 'key_compromise', 1508 2: 'ca_compromise', 1509 3: 'affiliation_changed', 1510 4: 'superseded', 1511 5: 'cessation_of_operation', 1512 6: 'certificate_hold', 1513 7: 'privilege_withdrawn', 1514 8: 'aa_compromise', 1515 } 1516 1517 1518class GeneralSubtree(Sequence): 1519 _fields = [ 1520 ('base', GeneralName), 1521 ('minimum', Integer, {'implicit': 0, 'default': 0}), 1522 ('maximum', Integer, {'implicit': 1, 'optional': True}), 1523 ] 1524 1525 1526class GeneralSubtrees(SequenceOf): 1527 _child_spec = GeneralSubtree 1528 1529 1530class NameConstraints(Sequence): 1531 _fields = [ 1532 ('permitted_subtrees', GeneralSubtrees, {'implicit': 0, 'optional': True}), 1533 ('excluded_subtrees', GeneralSubtrees, {'implicit': 1, 'optional': True}), 1534 ] 1535 1536 1537class DistributionPoint(Sequence): 1538 _fields = [ 1539 ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}), 1540 ('reasons', ReasonFlags, {'implicit': 1, 'optional': True}), 1541 ('crl_issuer', GeneralNames, {'implicit': 2, 'optional': True}), 1542 ] 1543 1544 _url = False 1545 1546 @property 1547 def url(self): 1548 """ 1549 :return: 1550 None or a unicode string of the distribution point's URL 1551 """ 1552 1553 if self._url is False: 1554 self._url = None 1555 name = self['distribution_point'] 1556 if name.name != 'full_name': 1557 raise ValueError(unwrap( 1558 ''' 1559 CRL distribution points that are relative to the issuer are 1560 not supported 1561 ''' 1562 )) 1563 1564 for general_name in name.chosen: 1565 if general_name.name == 'uniform_resource_identifier': 1566 url = general_name.native 1567 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')): 1568 self._url = url 1569 break 1570 1571 return self._url 1572 1573 1574class CRLDistributionPoints(SequenceOf): 1575 _child_spec = DistributionPoint 1576 1577 1578class DisplayText(Choice): 1579 _alternatives = [ 1580 ('ia5_string', IA5String), 1581 ('visible_string', VisibleString), 1582 ('bmp_string', BMPString), 1583 ('utf8_string', UTF8String), 1584 ] 1585 1586 1587class NoticeNumbers(SequenceOf): 1588 _child_spec = Integer 1589 1590 1591class NoticeReference(Sequence): 1592 _fields = [ 1593 ('organization', DisplayText), 1594 ('notice_numbers', NoticeNumbers), 1595 ] 1596 1597 1598class UserNotice(Sequence): 1599 _fields = [ 1600 ('notice_ref', NoticeReference, {'optional': True}), 1601 ('explicit_text', DisplayText, {'optional': True}), 1602 ] 1603 1604 1605class PolicyQualifierId(ObjectIdentifier): 1606 _map = { 1607 '1.3.6.1.5.5.7.2.1': 'certification_practice_statement', 1608 '1.3.6.1.5.5.7.2.2': 'user_notice', 1609 } 1610 1611 1612class PolicyQualifierInfo(Sequence): 1613 _fields = [ 1614 ('policy_qualifier_id', PolicyQualifierId), 1615 ('qualifier', Any), 1616 ] 1617 1618 _oid_pair = ('policy_qualifier_id', 'qualifier') 1619 _oid_specs = { 1620 'certification_practice_statement': IA5String, 1621 'user_notice': UserNotice, 1622 } 1623 1624 1625class PolicyQualifierInfos(SequenceOf): 1626 _child_spec = PolicyQualifierInfo 1627 1628 1629class PolicyIdentifier(ObjectIdentifier): 1630 _map = { 1631 '2.5.29.32.0': 'any_policy', 1632 } 1633 1634 1635class PolicyInformation(Sequence): 1636 _fields = [ 1637 ('policy_identifier', PolicyIdentifier), 1638 ('policy_qualifiers', PolicyQualifierInfos, {'optional': True}) 1639 ] 1640 1641 1642class CertificatePolicies(SequenceOf): 1643 _child_spec = PolicyInformation 1644 1645 1646class PolicyMapping(Sequence): 1647 _fields = [ 1648 ('issuer_domain_policy', PolicyIdentifier), 1649 ('subject_domain_policy', PolicyIdentifier), 1650 ] 1651 1652 1653class PolicyMappings(SequenceOf): 1654 _child_spec = PolicyMapping 1655 1656 1657class PolicyConstraints(Sequence): 1658 _fields = [ 1659 ('require_explicit_policy', Integer, {'implicit': 0, 'optional': True}), 1660 ('inhibit_policy_mapping', Integer, {'implicit': 1, 'optional': True}), 1661 ] 1662 1663 1664class KeyPurposeId(ObjectIdentifier): 1665 _map = { 1666 # https://tools.ietf.org/html/rfc5280#page-45 1667 '2.5.29.37.0': 'any_extended_key_usage', 1668 '1.3.6.1.5.5.7.3.1': 'server_auth', 1669 '1.3.6.1.5.5.7.3.2': 'client_auth', 1670 '1.3.6.1.5.5.7.3.3': 'code_signing', 1671 '1.3.6.1.5.5.7.3.4': 'email_protection', 1672 '1.3.6.1.5.5.7.3.5': 'ipsec_end_system', 1673 '1.3.6.1.5.5.7.3.6': 'ipsec_tunnel', 1674 '1.3.6.1.5.5.7.3.7': 'ipsec_user', 1675 '1.3.6.1.5.5.7.3.8': 'time_stamping', 1676 '1.3.6.1.5.5.7.3.9': 'ocsp_signing', 1677 # http://tools.ietf.org/html/rfc3029.html#page-9 1678 '1.3.6.1.5.5.7.3.10': 'dvcs', 1679 # http://tools.ietf.org/html/rfc6268.html#page-16 1680 '1.3.6.1.5.5.7.3.13': 'eap_over_ppp', 1681 '1.3.6.1.5.5.7.3.14': 'eap_over_lan', 1682 # https://tools.ietf.org/html/rfc5055#page-76 1683 '1.3.6.1.5.5.7.3.15': 'scvp_server', 1684 '1.3.6.1.5.5.7.3.16': 'scvp_client', 1685 # https://tools.ietf.org/html/rfc4945#page-31 1686 '1.3.6.1.5.5.7.3.17': 'ipsec_ike', 1687 # https://tools.ietf.org/html/rfc5415#page-38 1688 '1.3.6.1.5.5.7.3.18': 'capwap_ac', 1689 '1.3.6.1.5.5.7.3.19': 'capwap_wtp', 1690 # https://tools.ietf.org/html/rfc5924#page-8 1691 '1.3.6.1.5.5.7.3.20': 'sip_domain', 1692 # https://tools.ietf.org/html/rfc6187#page-7 1693 '1.3.6.1.5.5.7.3.21': 'secure_shell_client', 1694 '1.3.6.1.5.5.7.3.22': 'secure_shell_server', 1695 # https://tools.ietf.org/html/rfc6494#page-7 1696 '1.3.6.1.5.5.7.3.23': 'send_router', 1697 '1.3.6.1.5.5.7.3.24': 'send_proxied_router', 1698 '1.3.6.1.5.5.7.3.25': 'send_owner', 1699 '1.3.6.1.5.5.7.3.26': 'send_proxied_owner', 1700 # https://tools.ietf.org/html/rfc6402#page-10 1701 '1.3.6.1.5.5.7.3.27': 'cmc_ca', 1702 '1.3.6.1.5.5.7.3.28': 'cmc_ra', 1703 '1.3.6.1.5.5.7.3.29': 'cmc_archive', 1704 # https://tools.ietf.org/html/draft-ietf-sidr-bgpsec-pki-profiles-15#page-6 1705 '1.3.6.1.5.5.7.3.30': 'bgpspec_router', 1706 # https://www.ietf.org/proceedings/44/I-D/draft-ietf-ipsec-pki-req-01.txt 1707 '1.3.6.1.5.5.8.2.2': 'ike_intermediate', 1708 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378132(v=vs.85).aspx 1709 # and https://support.microsoft.com/en-us/kb/287547 1710 '1.3.6.1.4.1.311.10.3.1': 'microsoft_trust_list_signing', 1711 '1.3.6.1.4.1.311.10.3.2': 'microsoft_time_stamp_signing', 1712 '1.3.6.1.4.1.311.10.3.3': 'microsoft_server_gated', 1713 '1.3.6.1.4.1.311.10.3.3.1': 'microsoft_serialized', 1714 '1.3.6.1.4.1.311.10.3.4': 'microsoft_efs', 1715 '1.3.6.1.4.1.311.10.3.4.1': 'microsoft_efs_recovery', 1716 '1.3.6.1.4.1.311.10.3.5': 'microsoft_whql', 1717 '1.3.6.1.4.1.311.10.3.6': 'microsoft_nt5', 1718 '1.3.6.1.4.1.311.10.3.7': 'microsoft_oem_whql', 1719 '1.3.6.1.4.1.311.10.3.8': 'microsoft_embedded_nt', 1720 '1.3.6.1.4.1.311.10.3.9': 'microsoft_root_list_signer', 1721 '1.3.6.1.4.1.311.10.3.10': 'microsoft_qualified_subordination', 1722 '1.3.6.1.4.1.311.10.3.11': 'microsoft_key_recovery', 1723 '1.3.6.1.4.1.311.10.3.12': 'microsoft_document_signing', 1724 '1.3.6.1.4.1.311.10.3.13': 'microsoft_lifetime_signing', 1725 '1.3.6.1.4.1.311.10.3.14': 'microsoft_mobile_device_software', 1726 # https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography 1727 '1.3.6.1.4.1.311.20.2.2': 'microsoft_smart_card_logon', 1728 # https://opensource.apple.com/source 1729 # - /Security/Security-57031.40.6/Security/libsecurity_keychain/lib/SecPolicy.cpp 1730 # - /libsecurity_cssm/libsecurity_cssm-36064/lib/oidsalg.c 1731 '1.2.840.113635.100.1.2': 'apple_x509_basic', 1732 '1.2.840.113635.100.1.3': 'apple_ssl', 1733 '1.2.840.113635.100.1.4': 'apple_local_cert_gen', 1734 '1.2.840.113635.100.1.5': 'apple_csr_gen', 1735 '1.2.840.113635.100.1.6': 'apple_revocation_crl', 1736 '1.2.840.113635.100.1.7': 'apple_revocation_ocsp', 1737 '1.2.840.113635.100.1.8': 'apple_smime', 1738 '1.2.840.113635.100.1.9': 'apple_eap', 1739 '1.2.840.113635.100.1.10': 'apple_software_update_signing', 1740 '1.2.840.113635.100.1.11': 'apple_ipsec', 1741 '1.2.840.113635.100.1.12': 'apple_ichat', 1742 '1.2.840.113635.100.1.13': 'apple_resource_signing', 1743 '1.2.840.113635.100.1.14': 'apple_pkinit_client', 1744 '1.2.840.113635.100.1.15': 'apple_pkinit_server', 1745 '1.2.840.113635.100.1.16': 'apple_code_signing', 1746 '1.2.840.113635.100.1.17': 'apple_package_signing', 1747 '1.2.840.113635.100.1.18': 'apple_id_validation', 1748 '1.2.840.113635.100.1.20': 'apple_time_stamping', 1749 '1.2.840.113635.100.1.21': 'apple_revocation', 1750 '1.2.840.113635.100.1.22': 'apple_passbook_signing', 1751 '1.2.840.113635.100.1.23': 'apple_mobile_store', 1752 '1.2.840.113635.100.1.24': 'apple_escrow_service', 1753 '1.2.840.113635.100.1.25': 'apple_profile_signer', 1754 '1.2.840.113635.100.1.26': 'apple_qa_profile_signer', 1755 '1.2.840.113635.100.1.27': 'apple_test_mobile_store', 1756 '1.2.840.113635.100.1.28': 'apple_otapki_signer', 1757 '1.2.840.113635.100.1.29': 'apple_test_otapki_signer', 1758 '1.2.840.113625.100.1.30': 'apple_id_validation_record_signing_policy', 1759 '1.2.840.113625.100.1.31': 'apple_smp_encryption', 1760 '1.2.840.113625.100.1.32': 'apple_test_smp_encryption', 1761 '1.2.840.113635.100.1.33': 'apple_server_authentication', 1762 '1.2.840.113635.100.1.34': 'apple_pcs_escrow_service', 1763 # http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.201-2.pdf 1764 '2.16.840.1.101.3.6.8': 'piv_card_authentication', 1765 '2.16.840.1.101.3.6.7': 'piv_content_signing', 1766 # https://tools.ietf.org/html/rfc4556.html 1767 '1.3.6.1.5.2.3.4': 'pkinit_kpclientauth', 1768 '1.3.6.1.5.2.3.5': 'pkinit_kpkdc', 1769 # https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/changes.html 1770 '1.2.840.113583.1.1.5': 'adobe_authentic_documents_trust', 1771 # https://www.idmanagement.gov/wp-content/uploads/sites/1171/uploads/fpki-pivi-cert-profiles.pdf 1772 '2.16.840.1.101.3.8.7': 'fpki_pivi_content_signing' 1773 } 1774 1775 1776class ExtKeyUsageSyntax(SequenceOf): 1777 _child_spec = KeyPurposeId 1778 1779 1780class AccessMethod(ObjectIdentifier): 1781 _map = { 1782 '1.3.6.1.5.5.7.48.1': 'ocsp', 1783 '1.3.6.1.5.5.7.48.2': 'ca_issuers', 1784 '1.3.6.1.5.5.7.48.3': 'time_stamping', 1785 '1.3.6.1.5.5.7.48.5': 'ca_repository', 1786 } 1787 1788 1789class AccessDescription(Sequence): 1790 _fields = [ 1791 ('access_method', AccessMethod), 1792 ('access_location', GeneralName), 1793 ] 1794 1795 1796class AuthorityInfoAccessSyntax(SequenceOf): 1797 _child_spec = AccessDescription 1798 1799 1800class SubjectInfoAccessSyntax(SequenceOf): 1801 _child_spec = AccessDescription 1802 1803 1804# https://tools.ietf.org/html/rfc7633 1805class Features(SequenceOf): 1806 _child_spec = Integer 1807 1808 1809class EntrustVersionInfo(Sequence): 1810 _fields = [ 1811 ('entrust_vers', GeneralString), 1812 ('entrust_info_flags', BitString) 1813 ] 1814 1815 1816class NetscapeCertificateType(BitString): 1817 _map = { 1818 0: 'ssl_client', 1819 1: 'ssl_server', 1820 2: 'email', 1821 3: 'object_signing', 1822 4: 'reserved', 1823 5: 'ssl_ca', 1824 6: 'email_ca', 1825 7: 'object_signing_ca', 1826 } 1827 1828 1829class Version(Integer): 1830 _map = { 1831 0: 'v1', 1832 1: 'v2', 1833 2: 'v3', 1834 } 1835 1836 1837class TPMSpecification(Sequence): 1838 _fields = [ 1839 ('family', UTF8String), 1840 ('level', Integer), 1841 ('revision', Integer), 1842 ] 1843 1844 1845class SetOfTPMSpecification(SetOf): 1846 _child_spec = TPMSpecification 1847 1848 1849class TCGSpecificationVersion(Sequence): 1850 _fields = [ 1851 ('major_version', Integer), 1852 ('minor_version', Integer), 1853 ('revision', Integer), 1854 ] 1855 1856 1857class TCGPlatformSpecification(Sequence): 1858 _fields = [ 1859 ('version', TCGSpecificationVersion), 1860 ('platform_class', OctetString), 1861 ] 1862 1863 1864class SetOfTCGPlatformSpecification(SetOf): 1865 _child_spec = TCGPlatformSpecification 1866 1867 1868class EKGenerationType(Enumerated): 1869 _map = { 1870 0: 'internal', 1871 1: 'injected', 1872 2: 'internal_revocable', 1873 3: 'injected_revocable', 1874 } 1875 1876 1877class EKGenerationLocation(Enumerated): 1878 _map = { 1879 0: 'tpm_manufacturer', 1880 1: 'platform_manufacturer', 1881 2: 'ek_cert_signer', 1882 } 1883 1884 1885class EKCertificateGenerationLocation(Enumerated): 1886 _map = { 1887 0: 'tpm_manufacturer', 1888 1: 'platform_manufacturer', 1889 2: 'ek_cert_signer', 1890 } 1891 1892 1893class EvaluationAssuranceLevel(Enumerated): 1894 _map = { 1895 1: 'level1', 1896 2: 'level2', 1897 3: 'level3', 1898 4: 'level4', 1899 5: 'level5', 1900 6: 'level6', 1901 7: 'level7', 1902 } 1903 1904 1905class EvaluationStatus(Enumerated): 1906 _map = { 1907 0: 'designed_to_meet', 1908 1: 'evaluation_in_progress', 1909 2: 'evaluation_completed', 1910 } 1911 1912 1913class StrengthOfFunction(Enumerated): 1914 _map = { 1915 0: 'basic', 1916 1: 'medium', 1917 2: 'high', 1918 } 1919 1920 1921class URIReference(Sequence): 1922 _fields = [ 1923 ('uniform_resource_identifier', IA5String), 1924 ('hash_algorithm', DigestAlgorithm, {'optional': True}), 1925 ('hash_value', BitString, {'optional': True}), 1926 ] 1927 1928 1929class CommonCriteriaMeasures(Sequence): 1930 _fields = [ 1931 ('version', IA5String), 1932 ('assurance_level', EvaluationAssuranceLevel), 1933 ('evaluation_status', EvaluationStatus), 1934 ('plus', Boolean, {'default': False}), 1935 ('strengh_of_function', StrengthOfFunction, {'implicit': 0, 'optional': True}), 1936 ('profile_oid', ObjectIdentifier, {'implicit': 1, 'optional': True}), 1937 ('profile_url', URIReference, {'implicit': 2, 'optional': True}), 1938 ('target_oid', ObjectIdentifier, {'implicit': 3, 'optional': True}), 1939 ('target_uri', URIReference, {'implicit': 4, 'optional': True}), 1940 ] 1941 1942 1943class SecurityLevel(Enumerated): 1944 _map = { 1945 1: 'level1', 1946 2: 'level2', 1947 3: 'level3', 1948 4: 'level4', 1949 } 1950 1951 1952class FIPSLevel(Sequence): 1953 _fields = [ 1954 ('version', IA5String), 1955 ('level', SecurityLevel), 1956 ('plus', Boolean, {'default': False}), 1957 ] 1958 1959 1960class TPMSecurityAssertions(Sequence): 1961 _fields = [ 1962 ('version', Version, {'default': 'v1'}), 1963 ('field_upgradable', Boolean, {'default': False}), 1964 ('ek_generation_type', EKGenerationType, {'implicit': 0, 'optional': True}), 1965 ('ek_generation_location', EKGenerationLocation, {'implicit': 1, 'optional': True}), 1966 ('ek_certificate_generation_location', EKCertificateGenerationLocation, {'implicit': 2, 'optional': True}), 1967 ('cc_info', CommonCriteriaMeasures, {'implicit': 3, 'optional': True}), 1968 ('fips_level', FIPSLevel, {'implicit': 4, 'optional': True}), 1969 ('iso_9000_certified', Boolean, {'implicit': 5, 'default': False}), 1970 ('iso_9000_uri', IA5String, {'optional': True}), 1971 ] 1972 1973 1974class SetOfTPMSecurityAssertions(SetOf): 1975 _child_spec = TPMSecurityAssertions 1976 1977 1978class SubjectDirectoryAttributeId(ObjectIdentifier): 1979 _map = { 1980 # https://tools.ietf.org/html/rfc2256#page-11 1981 '2.5.4.52': 'supported_algorithms', 1982 # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf 1983 '2.23.133.2.16': 'tpm_specification', 1984 '2.23.133.2.17': 'tcg_platform_specification', 1985 '2.23.133.2.18': 'tpm_security_assertions', 1986 # https://tools.ietf.org/html/rfc3739#page-18 1987 '1.3.6.1.5.5.7.9.1': 'pda_date_of_birth', 1988 '1.3.6.1.5.5.7.9.2': 'pda_place_of_birth', 1989 '1.3.6.1.5.5.7.9.3': 'pda_gender', 1990 '1.3.6.1.5.5.7.9.4': 'pda_country_of_citizenship', 1991 '1.3.6.1.5.5.7.9.5': 'pda_country_of_residence', 1992 # https://holtstrom.com/michael/tools/asn1decoder.php 1993 '1.2.840.113533.7.68.29': 'entrust_user_role', 1994 } 1995 1996 1997class SetOfGeneralizedTime(SetOf): 1998 _child_spec = GeneralizedTime 1999 2000 2001class SetOfDirectoryString(SetOf): 2002 _child_spec = DirectoryString 2003 2004 2005class SetOfPrintableString(SetOf): 2006 _child_spec = PrintableString 2007 2008 2009class SupportedAlgorithm(Sequence): 2010 _fields = [ 2011 ('algorithm_identifier', AnyAlgorithmIdentifier), 2012 ('intended_usage', KeyUsage, {'explicit': 0, 'optional': True}), 2013 ('intended_certificate_policies', CertificatePolicies, {'explicit': 1, 'optional': True}), 2014 ] 2015 2016 2017class SetOfSupportedAlgorithm(SetOf): 2018 _child_spec = SupportedAlgorithm 2019 2020 2021class SubjectDirectoryAttribute(Sequence): 2022 _fields = [ 2023 ('type', SubjectDirectoryAttributeId), 2024 ('values', Any), 2025 ] 2026 2027 _oid_pair = ('type', 'values') 2028 _oid_specs = { 2029 'supported_algorithms': SetOfSupportedAlgorithm, 2030 'tpm_specification': SetOfTPMSpecification, 2031 'tcg_platform_specification': SetOfTCGPlatformSpecification, 2032 'tpm_security_assertions': SetOfTPMSecurityAssertions, 2033 'pda_date_of_birth': SetOfGeneralizedTime, 2034 'pda_place_of_birth': SetOfDirectoryString, 2035 'pda_gender': SetOfPrintableString, 2036 'pda_country_of_citizenship': SetOfPrintableString, 2037 'pda_country_of_residence': SetOfPrintableString, 2038 } 2039 2040 def _values_spec(self): 2041 type_ = self['type'].native 2042 if type_ in self._oid_specs: 2043 return self._oid_specs[type_] 2044 return SetOf 2045 2046 _spec_callbacks = { 2047 'values': _values_spec 2048 } 2049 2050 2051class SubjectDirectoryAttributes(SequenceOf): 2052 _child_spec = SubjectDirectoryAttribute 2053 2054 2055class ExtensionId(ObjectIdentifier): 2056 _map = { 2057 '2.5.29.9': 'subject_directory_attributes', 2058 '2.5.29.14': 'key_identifier', 2059 '2.5.29.15': 'key_usage', 2060 '2.5.29.16': 'private_key_usage_period', 2061 '2.5.29.17': 'subject_alt_name', 2062 '2.5.29.18': 'issuer_alt_name', 2063 '2.5.29.19': 'basic_constraints', 2064 '2.5.29.30': 'name_constraints', 2065 '2.5.29.31': 'crl_distribution_points', 2066 '2.5.29.32': 'certificate_policies', 2067 '2.5.29.33': 'policy_mappings', 2068 '2.5.29.35': 'authority_key_identifier', 2069 '2.5.29.36': 'policy_constraints', 2070 '2.5.29.37': 'extended_key_usage', 2071 '2.5.29.46': 'freshest_crl', 2072 '2.5.29.54': 'inhibit_any_policy', 2073 '1.3.6.1.5.5.7.1.1': 'authority_information_access', 2074 '1.3.6.1.5.5.7.1.11': 'subject_information_access', 2075 # https://tools.ietf.org/html/rfc7633 2076 '1.3.6.1.5.5.7.1.24': 'tls_feature', 2077 '1.3.6.1.5.5.7.48.1.5': 'ocsp_no_check', 2078 '1.2.840.113533.7.65.0': 'entrust_version_extension', 2079 '2.16.840.1.113730.1.1': 'netscape_certificate_type', 2080 # https://tools.ietf.org/html/rfc6962.html#page-14 2081 '1.3.6.1.4.1.11129.2.4.2': 'signed_certificate_timestamp_list', 2082 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/3aec3e50-511a-42f9-a5d5-240af503e470 2083 '1.3.6.1.4.1.311.20.2': 'microsoft_enroll_certtype', 2084 } 2085 2086 2087class Extension(Sequence): 2088 _fields = [ 2089 ('extn_id', ExtensionId), 2090 ('critical', Boolean, {'default': False}), 2091 ('extn_value', ParsableOctetString), 2092 ] 2093 2094 _oid_pair = ('extn_id', 'extn_value') 2095 _oid_specs = { 2096 'subject_directory_attributes': SubjectDirectoryAttributes, 2097 'key_identifier': OctetString, 2098 'key_usage': KeyUsage, 2099 'private_key_usage_period': PrivateKeyUsagePeriod, 2100 'subject_alt_name': GeneralNames, 2101 'issuer_alt_name': GeneralNames, 2102 'basic_constraints': BasicConstraints, 2103 'name_constraints': NameConstraints, 2104 'crl_distribution_points': CRLDistributionPoints, 2105 'certificate_policies': CertificatePolicies, 2106 'policy_mappings': PolicyMappings, 2107 'authority_key_identifier': AuthorityKeyIdentifier, 2108 'policy_constraints': PolicyConstraints, 2109 'extended_key_usage': ExtKeyUsageSyntax, 2110 'freshest_crl': CRLDistributionPoints, 2111 'inhibit_any_policy': Integer, 2112 'authority_information_access': AuthorityInfoAccessSyntax, 2113 'subject_information_access': SubjectInfoAccessSyntax, 2114 'tls_feature': Features, 2115 'ocsp_no_check': Null, 2116 'entrust_version_extension': EntrustVersionInfo, 2117 'netscape_certificate_type': NetscapeCertificateType, 2118 'signed_certificate_timestamp_list': OctetString, 2119 # Not UTF8String as Microsofts docs claim, see: 2120 # https://www.alvestrand.no/objectid/1.3.6.1.4.1.311.20.2.html 2121 'microsoft_enroll_certtype': BMPString, 2122 } 2123 2124 2125class Extensions(SequenceOf): 2126 _child_spec = Extension 2127 2128 2129class TbsCertificate(Sequence): 2130 _fields = [ 2131 ('version', Version, {'explicit': 0, 'default': 'v1'}), 2132 ('serial_number', Integer), 2133 ('signature', SignedDigestAlgorithm), 2134 ('issuer', Name), 2135 ('validity', Validity), 2136 ('subject', Name), 2137 ('subject_public_key_info', PublicKeyInfo), 2138 ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}), 2139 ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}), 2140 ('extensions', Extensions, {'explicit': 3, 'optional': True}), 2141 ] 2142 2143 2144class Certificate(Sequence): 2145 _fields = [ 2146 ('tbs_certificate', TbsCertificate), 2147 ('signature_algorithm', SignedDigestAlgorithm), 2148 ('signature_value', OctetBitString), 2149 ] 2150 2151 _processed_extensions = False 2152 _critical_extensions = None 2153 _subject_directory_attributes_value = None 2154 _key_identifier_value = None 2155 _key_usage_value = None 2156 _subject_alt_name_value = None 2157 _issuer_alt_name_value = None 2158 _basic_constraints_value = None 2159 _name_constraints_value = None 2160 _crl_distribution_points_value = None 2161 _certificate_policies_value = None 2162 _policy_mappings_value = None 2163 _authority_key_identifier_value = None 2164 _policy_constraints_value = None 2165 _freshest_crl_value = None 2166 _inhibit_any_policy_value = None 2167 _extended_key_usage_value = None 2168 _authority_information_access_value = None 2169 _subject_information_access_value = None 2170 _private_key_usage_period_value = None 2171 _tls_feature_value = None 2172 _ocsp_no_check_value = None 2173 _issuer_serial = None 2174 _authority_issuer_serial = False 2175 _crl_distribution_points = None 2176 _delta_crl_distribution_points = None 2177 _valid_domains = None 2178 _valid_ips = None 2179 _self_issued = None 2180 _self_signed = None 2181 _sha1 = None 2182 _sha256 = None 2183 2184 def _set_extensions(self): 2185 """ 2186 Sets common named extensions to private attributes and creates a list 2187 of critical extensions 2188 """ 2189 2190 self._critical_extensions = set() 2191 2192 for extension in self['tbs_certificate']['extensions']: 2193 name = extension['extn_id'].native 2194 attribute_name = '_%s_value' % name 2195 if hasattr(self, attribute_name): 2196 setattr(self, attribute_name, extension['extn_value'].parsed) 2197 if extension['critical'].native: 2198 self._critical_extensions.add(name) 2199 2200 self._processed_extensions = True 2201 2202 @property 2203 def critical_extensions(self): 2204 """ 2205 Returns a set of the names (or OID if not a known extension) of the 2206 extensions marked as critical 2207 2208 :return: 2209 A set of unicode strings 2210 """ 2211 2212 if not self._processed_extensions: 2213 self._set_extensions() 2214 return self._critical_extensions 2215 2216 @property 2217 def private_key_usage_period_value(self): 2218 """ 2219 This extension is used to constrain the period over which the subject 2220 private key may be used 2221 2222 :return: 2223 None or a PrivateKeyUsagePeriod object 2224 """ 2225 2226 if not self._processed_extensions: 2227 self._set_extensions() 2228 return self._private_key_usage_period_value 2229 2230 @property 2231 def subject_directory_attributes_value(self): 2232 """ 2233 This extension is used to contain additional identification attributes 2234 about the subject. 2235 2236 :return: 2237 None or a SubjectDirectoryAttributes object 2238 """ 2239 2240 if not self._processed_extensions: 2241 self._set_extensions() 2242 return self._subject_directory_attributes_value 2243 2244 @property 2245 def key_identifier_value(self): 2246 """ 2247 This extension is used to help in creating certificate validation paths. 2248 It contains an identifier that should generally, but is not guaranteed 2249 to, be unique. 2250 2251 :return: 2252 None or an OctetString object 2253 """ 2254 2255 if not self._processed_extensions: 2256 self._set_extensions() 2257 return self._key_identifier_value 2258 2259 @property 2260 def key_usage_value(self): 2261 """ 2262 This extension is used to define the purpose of the public key 2263 contained within the certificate. 2264 2265 :return: 2266 None or a KeyUsage 2267 """ 2268 2269 if not self._processed_extensions: 2270 self._set_extensions() 2271 return self._key_usage_value 2272 2273 @property 2274 def subject_alt_name_value(self): 2275 """ 2276 This extension allows for additional names to be associate with the 2277 subject of the certificate. While it may contain a whole host of 2278 possible names, it is usually used to allow certificates to be used 2279 with multiple different domain names. 2280 2281 :return: 2282 None or a GeneralNames object 2283 """ 2284 2285 if not self._processed_extensions: 2286 self._set_extensions() 2287 return self._subject_alt_name_value 2288 2289 @property 2290 def issuer_alt_name_value(self): 2291 """ 2292 This extension allows associating one or more alternative names with 2293 the issuer of the certificate. 2294 2295 :return: 2296 None or an x509.GeneralNames object 2297 """ 2298 2299 if not self._processed_extensions: 2300 self._set_extensions() 2301 return self._issuer_alt_name_value 2302 2303 @property 2304 def basic_constraints_value(self): 2305 """ 2306 This extension is used to determine if the subject of the certificate 2307 is a CA, and if so, what the maximum number of intermediate CA certs 2308 after this are, before an end-entity certificate is found. 2309 2310 :return: 2311 None or a BasicConstraints object 2312 """ 2313 2314 if not self._processed_extensions: 2315 self._set_extensions() 2316 return self._basic_constraints_value 2317 2318 @property 2319 def name_constraints_value(self): 2320 """ 2321 This extension is used in CA certificates, and is used to limit the 2322 possible names of certificates issued. 2323 2324 :return: 2325 None or a NameConstraints object 2326 """ 2327 2328 if not self._processed_extensions: 2329 self._set_extensions() 2330 return self._name_constraints_value 2331 2332 @property 2333 def crl_distribution_points_value(self): 2334 """ 2335 This extension is used to help in locating the CRL for this certificate. 2336 2337 :return: 2338 None or a CRLDistributionPoints object 2339 extension 2340 """ 2341 2342 if not self._processed_extensions: 2343 self._set_extensions() 2344 return self._crl_distribution_points_value 2345 2346 @property 2347 def certificate_policies_value(self): 2348 """ 2349 This extension defines policies in CA certificates under which 2350 certificates may be issued. In end-entity certificates, the inclusion 2351 of a policy indicates the issuance of the certificate follows the 2352 policy. 2353 2354 :return: 2355 None or a CertificatePolicies object 2356 """ 2357 2358 if not self._processed_extensions: 2359 self._set_extensions() 2360 return self._certificate_policies_value 2361 2362 @property 2363 def policy_mappings_value(self): 2364 """ 2365 This extension allows mapping policy OIDs to other OIDs. This is used 2366 to allow different policies to be treated as equivalent in the process 2367 of validation. 2368 2369 :return: 2370 None or a PolicyMappings object 2371 """ 2372 2373 if not self._processed_extensions: 2374 self._set_extensions() 2375 return self._policy_mappings_value 2376 2377 @property 2378 def authority_key_identifier_value(self): 2379 """ 2380 This extension helps in identifying the public key with which to 2381 validate the authenticity of the certificate. 2382 2383 :return: 2384 None or an AuthorityKeyIdentifier object 2385 """ 2386 2387 if not self._processed_extensions: 2388 self._set_extensions() 2389 return self._authority_key_identifier_value 2390 2391 @property 2392 def policy_constraints_value(self): 2393 """ 2394 This extension is used to control if policy mapping is allowed and 2395 when policies are required. 2396 2397 :return: 2398 None or a PolicyConstraints object 2399 """ 2400 2401 if not self._processed_extensions: 2402 self._set_extensions() 2403 return self._policy_constraints_value 2404 2405 @property 2406 def freshest_crl_value(self): 2407 """ 2408 This extension is used to help locate any available delta CRLs 2409 2410 :return: 2411 None or an CRLDistributionPoints object 2412 """ 2413 2414 if not self._processed_extensions: 2415 self._set_extensions() 2416 return self._freshest_crl_value 2417 2418 @property 2419 def inhibit_any_policy_value(self): 2420 """ 2421 This extension is used to prevent mapping of the any policy to 2422 specific requirements 2423 2424 :return: 2425 None or a Integer object 2426 """ 2427 2428 if not self._processed_extensions: 2429 self._set_extensions() 2430 return self._inhibit_any_policy_value 2431 2432 @property 2433 def extended_key_usage_value(self): 2434 """ 2435 This extension is used to define additional purposes for the public key 2436 beyond what is contained in the basic constraints. 2437 2438 :return: 2439 None or an ExtKeyUsageSyntax object 2440 """ 2441 2442 if not self._processed_extensions: 2443 self._set_extensions() 2444 return self._extended_key_usage_value 2445 2446 @property 2447 def authority_information_access_value(self): 2448 """ 2449 This extension is used to locate the CA certificate used to sign this 2450 certificate, or the OCSP responder for this certificate. 2451 2452 :return: 2453 None or an AuthorityInfoAccessSyntax object 2454 """ 2455 2456 if not self._processed_extensions: 2457 self._set_extensions() 2458 return self._authority_information_access_value 2459 2460 @property 2461 def subject_information_access_value(self): 2462 """ 2463 This extension is used to access information about the subject of this 2464 certificate. 2465 2466 :return: 2467 None or a SubjectInfoAccessSyntax object 2468 """ 2469 2470 if not self._processed_extensions: 2471 self._set_extensions() 2472 return self._subject_information_access_value 2473 2474 @property 2475 def tls_feature_value(self): 2476 """ 2477 This extension is used to list the TLS features a server must respond 2478 with if a client initiates a request supporting them. 2479 2480 :return: 2481 None or a Features object 2482 """ 2483 2484 if not self._processed_extensions: 2485 self._set_extensions() 2486 return self._tls_feature_value 2487 2488 @property 2489 def ocsp_no_check_value(self): 2490 """ 2491 This extension is used on certificates of OCSP responders, indicating 2492 that revocation information for the certificate should never need to 2493 be verified, thus preventing possible loops in path validation. 2494 2495 :return: 2496 None or a Null object (if present) 2497 """ 2498 2499 if not self._processed_extensions: 2500 self._set_extensions() 2501 return self._ocsp_no_check_value 2502 2503 @property 2504 def signature(self): 2505 """ 2506 :return: 2507 A byte string of the signature 2508 """ 2509 2510 return self['signature_value'].native 2511 2512 @property 2513 def signature_algo(self): 2514 """ 2515 :return: 2516 A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa" 2517 """ 2518 2519 return self['signature_algorithm'].signature_algo 2520 2521 @property 2522 def hash_algo(self): 2523 """ 2524 :return: 2525 A unicode string of "md2", "md5", "sha1", "sha224", "sha256", 2526 "sha384", "sha512", "sha512_224", "sha512_256" 2527 """ 2528 2529 return self['signature_algorithm'].hash_algo 2530 2531 @property 2532 def public_key(self): 2533 """ 2534 :return: 2535 The PublicKeyInfo object for this certificate 2536 """ 2537 2538 return self['tbs_certificate']['subject_public_key_info'] 2539 2540 @property 2541 def subject(self): 2542 """ 2543 :return: 2544 The Name object for the subject of this certificate 2545 """ 2546 2547 return self['tbs_certificate']['subject'] 2548 2549 @property 2550 def issuer(self): 2551 """ 2552 :return: 2553 The Name object for the issuer of this certificate 2554 """ 2555 2556 return self['tbs_certificate']['issuer'] 2557 2558 @property 2559 def serial_number(self): 2560 """ 2561 :return: 2562 An integer of the certificate's serial number 2563 """ 2564 2565 return self['tbs_certificate']['serial_number'].native 2566 2567 @property 2568 def key_identifier(self): 2569 """ 2570 :return: 2571 None or a byte string of the certificate's key identifier from the 2572 key identifier extension 2573 """ 2574 2575 if not self.key_identifier_value: 2576 return None 2577 2578 return self.key_identifier_value.native 2579 2580 @property 2581 def issuer_serial(self): 2582 """ 2583 :return: 2584 A byte string of the SHA-256 hash of the issuer concatenated with 2585 the ascii character ":", concatenated with the serial number as 2586 an ascii string 2587 """ 2588 2589 if self._issuer_serial is None: 2590 self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii') 2591 return self._issuer_serial 2592 2593 @property 2594 def not_valid_after(self): 2595 """ 2596 :return: 2597 A datetime of latest time when the certificate is still valid 2598 """ 2599 return self['tbs_certificate']['validity']['not_after'].native 2600 2601 @property 2602 def not_valid_before(self): 2603 """ 2604 :return: 2605 A datetime of the earliest time when the certificate is valid 2606 """ 2607 return self['tbs_certificate']['validity']['not_before'].native 2608 2609 @property 2610 def authority_key_identifier(self): 2611 """ 2612 :return: 2613 None or a byte string of the key_identifier from the authority key 2614 identifier extension 2615 """ 2616 2617 if not self.authority_key_identifier_value: 2618 return None 2619 2620 return self.authority_key_identifier_value['key_identifier'].native 2621 2622 @property 2623 def authority_issuer_serial(self): 2624 """ 2625 :return: 2626 None or a byte string of the SHA-256 hash of the isser from the 2627 authority key identifier extension concatenated with the ascii 2628 character ":", concatenated with the serial number from the 2629 authority key identifier extension as an ascii string 2630 """ 2631 2632 if self._authority_issuer_serial is False: 2633 akiv = self.authority_key_identifier_value 2634 if akiv and akiv['authority_cert_issuer'].native: 2635 issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen 2636 # We untag the element since it is tagged via being a choice from GeneralName 2637 issuer = issuer.untag() 2638 authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native 2639 self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii') 2640 else: 2641 self._authority_issuer_serial = None 2642 return self._authority_issuer_serial 2643 2644 @property 2645 def crl_distribution_points(self): 2646 """ 2647 Returns complete CRL URLs - does not include delta CRLs 2648 2649 :return: 2650 A list of zero or more DistributionPoint objects 2651 """ 2652 2653 if self._crl_distribution_points is None: 2654 self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value) 2655 return self._crl_distribution_points 2656 2657 @property 2658 def delta_crl_distribution_points(self): 2659 """ 2660 Returns delta CRL URLs - does not include complete CRLs 2661 2662 :return: 2663 A list of zero or more DistributionPoint objects 2664 """ 2665 2666 if self._delta_crl_distribution_points is None: 2667 self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value) 2668 return self._delta_crl_distribution_points 2669 2670 def _get_http_crl_distribution_points(self, crl_distribution_points): 2671 """ 2672 Fetches the DistributionPoint object for non-relative, HTTP CRLs 2673 referenced by the certificate 2674 2675 :param crl_distribution_points: 2676 A CRLDistributionPoints object to grab the DistributionPoints from 2677 2678 :return: 2679 A list of zero or more DistributionPoint objects 2680 """ 2681 2682 output = [] 2683 2684 if crl_distribution_points is None: 2685 return [] 2686 2687 for distribution_point in crl_distribution_points: 2688 distribution_point_name = distribution_point['distribution_point'] 2689 if distribution_point_name is VOID: 2690 continue 2691 # RFC 5280 indicates conforming CA should not use the relative form 2692 if distribution_point_name.name == 'name_relative_to_crl_issuer': 2693 continue 2694 # This library is currently only concerned with HTTP-based CRLs 2695 for general_name in distribution_point_name.chosen: 2696 if general_name.name == 'uniform_resource_identifier': 2697 output.append(distribution_point) 2698 2699 return output 2700 2701 @property 2702 def ocsp_urls(self): 2703 """ 2704 :return: 2705 A list of zero or more unicode strings of the OCSP URLs for this 2706 cert 2707 """ 2708 2709 if not self.authority_information_access_value: 2710 return [] 2711 2712 output = [] 2713 for entry in self.authority_information_access_value: 2714 if entry['access_method'].native == 'ocsp': 2715 location = entry['access_location'] 2716 if location.name != 'uniform_resource_identifier': 2717 continue 2718 url = location.native 2719 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')): 2720 output.append(url) 2721 return output 2722 2723 @property 2724 def valid_domains(self): 2725 """ 2726 :return: 2727 A list of unicode strings of valid domain names for the certificate. 2728 Wildcard certificates will have a domain in the form: *.example.com 2729 """ 2730 2731 if self._valid_domains is None: 2732 self._valid_domains = [] 2733 2734 # For the subject alt name extension, we can look at the name of 2735 # the choice selected since it distinguishes between domain names, 2736 # email addresses, IPs, etc 2737 if self.subject_alt_name_value: 2738 for general_name in self.subject_alt_name_value: 2739 if general_name.name == 'dns_name' and general_name.native not in self._valid_domains: 2740 self._valid_domains.append(general_name.native) 2741 2742 # If there was no subject alt name extension, and the common name 2743 # in the subject looks like a domain, that is considered the valid 2744 # list. This is done because according to 2745 # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common 2746 # name should not be used if the subject alt name is present. 2747 else: 2748 pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$') 2749 for rdn in self.subject.chosen: 2750 for name_type_value in rdn: 2751 if name_type_value['type'].native == 'common_name': 2752 value = name_type_value['value'].native 2753 if pattern.match(value): 2754 self._valid_domains.append(value) 2755 2756 return self._valid_domains 2757 2758 @property 2759 def valid_ips(self): 2760 """ 2761 :return: 2762 A list of unicode strings of valid IP addresses for the certificate 2763 """ 2764 2765 if self._valid_ips is None: 2766 self._valid_ips = [] 2767 2768 if self.subject_alt_name_value: 2769 for general_name in self.subject_alt_name_value: 2770 if general_name.name == 'ip_address': 2771 self._valid_ips.append(general_name.native) 2772 2773 return self._valid_ips 2774 2775 @property 2776 def ca(self): 2777 """ 2778 :return; 2779 A boolean - if the certificate is marked as a CA 2780 """ 2781 2782 return self.basic_constraints_value and self.basic_constraints_value['ca'].native 2783 2784 @property 2785 def max_path_length(self): 2786 """ 2787 :return; 2788 None or an integer of the maximum path length 2789 """ 2790 2791 if not self.ca: 2792 return None 2793 return self.basic_constraints_value['path_len_constraint'].native 2794 2795 @property 2796 def self_issued(self): 2797 """ 2798 :return: 2799 A boolean - if the certificate is self-issued, as defined by RFC 2800 5280 2801 """ 2802 2803 if self._self_issued is None: 2804 self._self_issued = self.subject == self.issuer 2805 return self._self_issued 2806 2807 @property 2808 def self_signed(self): 2809 """ 2810 :return: 2811 A unicode string of "no" or "maybe". The "maybe" result will 2812 be returned if the certificate issuer and subject are the same. 2813 If a key identifier and authority key identifier are present, 2814 they will need to match otherwise "no" will be returned. 2815 2816 To verify is a certificate is truly self-signed, the signature 2817 will need to be verified. See the certvalidator package for 2818 one possible solution. 2819 """ 2820 2821 if self._self_signed is None: 2822 self._self_signed = 'no' 2823 if self.self_issued: 2824 if self.key_identifier: 2825 if not self.authority_key_identifier: 2826 self._self_signed = 'maybe' 2827 elif self.authority_key_identifier == self.key_identifier: 2828 self._self_signed = 'maybe' 2829 else: 2830 self._self_signed = 'maybe' 2831 return self._self_signed 2832 2833 @property 2834 def sha1(self): 2835 """ 2836 :return: 2837 The SHA-1 hash of the DER-encoded bytes of this complete certificate 2838 """ 2839 2840 if self._sha1 is None: 2841 self._sha1 = hashlib.sha1(self.dump()).digest() 2842 return self._sha1 2843 2844 @property 2845 def sha1_fingerprint(self): 2846 """ 2847 :return: 2848 A unicode string of the SHA-1 hash, formatted using hex encoding 2849 with a space between each pair of characters, all uppercase 2850 """ 2851 2852 return ' '.join('%02X' % c for c in bytes_to_list(self.sha1)) 2853 2854 @property 2855 def sha256(self): 2856 """ 2857 :return: 2858 The SHA-256 hash of the DER-encoded bytes of this complete 2859 certificate 2860 """ 2861 2862 if self._sha256 is None: 2863 self._sha256 = hashlib.sha256(self.dump()).digest() 2864 return self._sha256 2865 2866 @property 2867 def sha256_fingerprint(self): 2868 """ 2869 :return: 2870 A unicode string of the SHA-256 hash, formatted using hex encoding 2871 with a space between each pair of characters, all uppercase 2872 """ 2873 2874 return ' '.join('%02X' % c for c in bytes_to_list(self.sha256)) 2875 2876 def is_valid_domain_ip(self, domain_ip): 2877 """ 2878 Check if a domain name or IP address is valid according to the 2879 certificate 2880 2881 :param domain_ip: 2882 A unicode string of a domain name or IP address 2883 2884 :return: 2885 A boolean - if the domain or IP is valid for the certificate 2886 """ 2887 2888 if not isinstance(domain_ip, str_cls): 2889 raise TypeError(unwrap( 2890 ''' 2891 domain_ip must be a unicode string, not %s 2892 ''', 2893 type_name(domain_ip) 2894 )) 2895 2896 encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower() 2897 2898 is_ipv6 = encoded_domain_ip.find(':') != -1 2899 is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip) 2900 is_domain = not is_ipv6 and not is_ipv4 2901 2902 # Handle domain name checks 2903 if is_domain: 2904 if not self.valid_domains: 2905 return False 2906 2907 domain_labels = encoded_domain_ip.split('.') 2908 2909 for valid_domain in self.valid_domains: 2910 encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower() 2911 valid_domain_labels = encoded_valid_domain.split('.') 2912 2913 # The domain must be equal in label length to match 2914 if len(valid_domain_labels) != len(domain_labels): 2915 continue 2916 2917 if valid_domain_labels == domain_labels: 2918 return True 2919 2920 is_wildcard = self._is_wildcard_domain(encoded_valid_domain) 2921 if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels): 2922 return True 2923 2924 return False 2925 2926 # Handle IP address checks 2927 if not self.valid_ips: 2928 return False 2929 2930 family = socket.AF_INET if is_ipv4 else socket.AF_INET6 2931 normalized_ip = inet_pton(family, encoded_domain_ip) 2932 2933 for valid_ip in self.valid_ips: 2934 valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6 2935 normalized_valid_ip = inet_pton(valid_family, valid_ip) 2936 2937 if normalized_valid_ip == normalized_ip: 2938 return True 2939 2940 return False 2941 2942 def _is_wildcard_domain(self, domain): 2943 """ 2944 Checks if a domain is a valid wildcard according to 2945 https://tools.ietf.org/html/rfc6125#section-6.4.3 2946 2947 :param domain: 2948 A unicode string of the domain name, where any U-labels from an IDN 2949 have been converted to A-labels 2950 2951 :return: 2952 A boolean - if the domain is a valid wildcard domain 2953 """ 2954 2955 # The * character must be present for a wildcard match, and if there is 2956 # most than one, it is an invalid wildcard specification 2957 if domain.count('*') != 1: 2958 return False 2959 2960 labels = domain.lower().split('.') 2961 2962 if not labels: 2963 return False 2964 2965 # Wildcards may only appear in the left-most label 2966 if labels[0].find('*') == -1: 2967 return False 2968 2969 # Wildcards may not be embedded in an A-label from an IDN 2970 if labels[0][0:4] == 'xn--': 2971 return False 2972 2973 return True 2974 2975 def _is_wildcard_match(self, domain_labels, valid_domain_labels): 2976 """ 2977 Determines if the labels in a domain are a match for labels from a 2978 wildcard valid domain name 2979 2980 :param domain_labels: 2981 A list of unicode strings, with A-label form for IDNs, of the labels 2982 in the domain name to check 2983 2984 :param valid_domain_labels: 2985 A list of unicode strings, with A-label form for IDNs, of the labels 2986 in a wildcard domain pattern 2987 2988 :return: 2989 A boolean - if the domain matches the valid domain 2990 """ 2991 2992 first_domain_label = domain_labels[0] 2993 other_domain_labels = domain_labels[1:] 2994 2995 wildcard_label = valid_domain_labels[0] 2996 other_valid_domain_labels = valid_domain_labels[1:] 2997 2998 # The wildcard is only allowed in the first label, so if 2999 # The subsequent labels are not equal, there is no match 3000 if other_domain_labels != other_valid_domain_labels: 3001 return False 3002 3003 if wildcard_label == '*': 3004 return True 3005 3006 wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$') 3007 if wildcard_regex.match(first_domain_label): 3008 return True 3009 3010 return False 3011 3012 3013# The structures are taken from the OpenSSL source file x_x509a.c, and specify 3014# extra information that is added to X.509 certificates to store trust 3015# information about the certificate. 3016 3017class KeyPurposeIdentifiers(SequenceOf): 3018 _child_spec = KeyPurposeId 3019 3020 3021class SequenceOfAlgorithmIdentifiers(SequenceOf): 3022 _child_spec = AlgorithmIdentifier 3023 3024 3025class CertificateAux(Sequence): 3026 _fields = [ 3027 ('trust', KeyPurposeIdentifiers, {'optional': True}), 3028 ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}), 3029 ('alias', UTF8String, {'optional': True}), 3030 ('keyid', OctetString, {'optional': True}), 3031 ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}), 3032 ] 3033 3034 3035class TrustedCertificate(Concat): 3036 _child_specs = [Certificate, CertificateAux] 3037