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": "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 } 2083 2084 2085class Extension(Sequence): 2086 _fields = [ 2087 ('extn_id', ExtensionId), 2088 ('critical', Boolean, {'default': False}), 2089 ('extn_value', ParsableOctetString), 2090 ] 2091 2092 _oid_pair = ('extn_id', 'extn_value') 2093 _oid_specs = { 2094 'subject_directory_attributes': SubjectDirectoryAttributes, 2095 'key_identifier': OctetString, 2096 'key_usage': KeyUsage, 2097 'private_key_usage_period': PrivateKeyUsagePeriod, 2098 'subject_alt_name': GeneralNames, 2099 'issuer_alt_name': GeneralNames, 2100 'basic_constraints': BasicConstraints, 2101 'name_constraints': NameConstraints, 2102 'crl_distribution_points': CRLDistributionPoints, 2103 'certificate_policies': CertificatePolicies, 2104 'policy_mappings': PolicyMappings, 2105 'authority_key_identifier': AuthorityKeyIdentifier, 2106 'policy_constraints': PolicyConstraints, 2107 'extended_key_usage': ExtKeyUsageSyntax, 2108 'freshest_crl': CRLDistributionPoints, 2109 'inhibit_any_policy': Integer, 2110 'authority_information_access': AuthorityInfoAccessSyntax, 2111 'subject_information_access': SubjectInfoAccessSyntax, 2112 'tls_feature': Features, 2113 'ocsp_no_check': Null, 2114 'entrust_version_extension': EntrustVersionInfo, 2115 'netscape_certificate_type': NetscapeCertificateType, 2116 'signed_certificate_timestamp_list': OctetString, 2117 } 2118 2119 2120class Extensions(SequenceOf): 2121 _child_spec = Extension 2122 2123 2124class TbsCertificate(Sequence): 2125 _fields = [ 2126 ('version', Version, {'explicit': 0, 'default': 'v1'}), 2127 ('serial_number', Integer), 2128 ('signature', SignedDigestAlgorithm), 2129 ('issuer', Name), 2130 ('validity', Validity), 2131 ('subject', Name), 2132 ('subject_public_key_info', PublicKeyInfo), 2133 ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}), 2134 ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}), 2135 ('extensions', Extensions, {'explicit': 3, 'optional': True}), 2136 ] 2137 2138 2139class Certificate(Sequence): 2140 _fields = [ 2141 ('tbs_certificate', TbsCertificate), 2142 ('signature_algorithm', SignedDigestAlgorithm), 2143 ('signature_value', OctetBitString), 2144 ] 2145 2146 _processed_extensions = False 2147 _critical_extensions = None 2148 _subject_directory_attributes_value = None 2149 _key_identifier_value = None 2150 _key_usage_value = None 2151 _subject_alt_name_value = None 2152 _issuer_alt_name_value = None 2153 _basic_constraints_value = None 2154 _name_constraints_value = None 2155 _crl_distribution_points_value = None 2156 _certificate_policies_value = None 2157 _policy_mappings_value = None 2158 _authority_key_identifier_value = None 2159 _policy_constraints_value = None 2160 _freshest_crl_value = None 2161 _inhibit_any_policy_value = None 2162 _extended_key_usage_value = None 2163 _authority_information_access_value = None 2164 _subject_information_access_value = None 2165 _private_key_usage_period_value = None 2166 _tls_feature_value = None 2167 _ocsp_no_check_value = None 2168 _issuer_serial = None 2169 _authority_issuer_serial = False 2170 _crl_distribution_points = None 2171 _delta_crl_distribution_points = None 2172 _valid_domains = None 2173 _valid_ips = None 2174 _self_issued = None 2175 _self_signed = None 2176 _sha1 = None 2177 _sha256 = None 2178 2179 def _set_extensions(self): 2180 """ 2181 Sets common named extensions to private attributes and creates a list 2182 of critical extensions 2183 """ 2184 2185 self._critical_extensions = set() 2186 2187 for extension in self['tbs_certificate']['extensions']: 2188 name = extension['extn_id'].native 2189 attribute_name = '_%s_value' % name 2190 if hasattr(self, attribute_name): 2191 setattr(self, attribute_name, extension['extn_value'].parsed) 2192 if extension['critical'].native: 2193 self._critical_extensions.add(name) 2194 2195 self._processed_extensions = True 2196 2197 @property 2198 def critical_extensions(self): 2199 """ 2200 Returns a set of the names (or OID if not a known extension) of the 2201 extensions marked as critical 2202 2203 :return: 2204 A set of unicode strings 2205 """ 2206 2207 if not self._processed_extensions: 2208 self._set_extensions() 2209 return self._critical_extensions 2210 2211 @property 2212 def private_key_usage_period_value(self): 2213 """ 2214 This extension is used to constrain the period over which the subject 2215 private key may be used 2216 2217 :return: 2218 None or a PrivateKeyUsagePeriod object 2219 """ 2220 2221 if not self._processed_extensions: 2222 self._set_extensions() 2223 return self._private_key_usage_period_value 2224 2225 @property 2226 def subject_directory_attributes_value(self): 2227 """ 2228 This extension is used to contain additional identification attributes 2229 about the subject. 2230 2231 :return: 2232 None or a SubjectDirectoryAttributes object 2233 """ 2234 2235 if not self._processed_extensions: 2236 self._set_extensions() 2237 return self._subject_directory_attributes_value 2238 2239 @property 2240 def key_identifier_value(self): 2241 """ 2242 This extension is used to help in creating certificate validation paths. 2243 It contains an identifier that should generally, but is not guaranteed 2244 to, be unique. 2245 2246 :return: 2247 None or an OctetString object 2248 """ 2249 2250 if not self._processed_extensions: 2251 self._set_extensions() 2252 return self._key_identifier_value 2253 2254 @property 2255 def key_usage_value(self): 2256 """ 2257 This extension is used to define the purpose of the public key 2258 contained within the certificate. 2259 2260 :return: 2261 None or a KeyUsage 2262 """ 2263 2264 if not self._processed_extensions: 2265 self._set_extensions() 2266 return self._key_usage_value 2267 2268 @property 2269 def subject_alt_name_value(self): 2270 """ 2271 This extension allows for additional names to be associate with the 2272 subject of the certificate. While it may contain a whole host of 2273 possible names, it is usually used to allow certificates to be used 2274 with multiple different domain names. 2275 2276 :return: 2277 None or a GeneralNames object 2278 """ 2279 2280 if not self._processed_extensions: 2281 self._set_extensions() 2282 return self._subject_alt_name_value 2283 2284 @property 2285 def issuer_alt_name_value(self): 2286 """ 2287 This extension allows associating one or more alternative names with 2288 the issuer of the certificate. 2289 2290 :return: 2291 None or an x509.GeneralNames object 2292 """ 2293 2294 if not self._processed_extensions: 2295 self._set_extensions() 2296 return self._issuer_alt_name_value 2297 2298 @property 2299 def basic_constraints_value(self): 2300 """ 2301 This extension is used to determine if the subject of the certificate 2302 is a CA, and if so, what the maximum number of intermediate CA certs 2303 after this are, before an end-entity certificate is found. 2304 2305 :return: 2306 None or a BasicConstraints object 2307 """ 2308 2309 if not self._processed_extensions: 2310 self._set_extensions() 2311 return self._basic_constraints_value 2312 2313 @property 2314 def name_constraints_value(self): 2315 """ 2316 This extension is used in CA certificates, and is used to limit the 2317 possible names of certificates issued. 2318 2319 :return: 2320 None or a NameConstraints object 2321 """ 2322 2323 if not self._processed_extensions: 2324 self._set_extensions() 2325 return self._name_constraints_value 2326 2327 @property 2328 def crl_distribution_points_value(self): 2329 """ 2330 This extension is used to help in locating the CRL for this certificate. 2331 2332 :return: 2333 None or a CRLDistributionPoints object 2334 extension 2335 """ 2336 2337 if not self._processed_extensions: 2338 self._set_extensions() 2339 return self._crl_distribution_points_value 2340 2341 @property 2342 def certificate_policies_value(self): 2343 """ 2344 This extension defines policies in CA certificates under which 2345 certificates may be issued. In end-entity certificates, the inclusion 2346 of a policy indicates the issuance of the certificate follows the 2347 policy. 2348 2349 :return: 2350 None or a CertificatePolicies object 2351 """ 2352 2353 if not self._processed_extensions: 2354 self._set_extensions() 2355 return self._certificate_policies_value 2356 2357 @property 2358 def policy_mappings_value(self): 2359 """ 2360 This extension allows mapping policy OIDs to other OIDs. This is used 2361 to allow different policies to be treated as equivalent in the process 2362 of validation. 2363 2364 :return: 2365 None or a PolicyMappings object 2366 """ 2367 2368 if not self._processed_extensions: 2369 self._set_extensions() 2370 return self._policy_mappings_value 2371 2372 @property 2373 def authority_key_identifier_value(self): 2374 """ 2375 This extension helps in identifying the public key with which to 2376 validate the authenticity of the certificate. 2377 2378 :return: 2379 None or an AuthorityKeyIdentifier object 2380 """ 2381 2382 if not self._processed_extensions: 2383 self._set_extensions() 2384 return self._authority_key_identifier_value 2385 2386 @property 2387 def policy_constraints_value(self): 2388 """ 2389 This extension is used to control if policy mapping is allowed and 2390 when policies are required. 2391 2392 :return: 2393 None or a PolicyConstraints object 2394 """ 2395 2396 if not self._processed_extensions: 2397 self._set_extensions() 2398 return self._policy_constraints_value 2399 2400 @property 2401 def freshest_crl_value(self): 2402 """ 2403 This extension is used to help locate any available delta CRLs 2404 2405 :return: 2406 None or an CRLDistributionPoints object 2407 """ 2408 2409 if not self._processed_extensions: 2410 self._set_extensions() 2411 return self._freshest_crl_value 2412 2413 @property 2414 def inhibit_any_policy_value(self): 2415 """ 2416 This extension is used to prevent mapping of the any policy to 2417 specific requirements 2418 2419 :return: 2420 None or a Integer object 2421 """ 2422 2423 if not self._processed_extensions: 2424 self._set_extensions() 2425 return self._inhibit_any_policy_value 2426 2427 @property 2428 def extended_key_usage_value(self): 2429 """ 2430 This extension is used to define additional purposes for the public key 2431 beyond what is contained in the basic constraints. 2432 2433 :return: 2434 None or an ExtKeyUsageSyntax object 2435 """ 2436 2437 if not self._processed_extensions: 2438 self._set_extensions() 2439 return self._extended_key_usage_value 2440 2441 @property 2442 def authority_information_access_value(self): 2443 """ 2444 This extension is used to locate the CA certificate used to sign this 2445 certificate, or the OCSP responder for this certificate. 2446 2447 :return: 2448 None or an AuthorityInfoAccessSyntax object 2449 """ 2450 2451 if not self._processed_extensions: 2452 self._set_extensions() 2453 return self._authority_information_access_value 2454 2455 @property 2456 def subject_information_access_value(self): 2457 """ 2458 This extension is used to access information about the subject of this 2459 certificate. 2460 2461 :return: 2462 None or a SubjectInfoAccessSyntax object 2463 """ 2464 2465 if not self._processed_extensions: 2466 self._set_extensions() 2467 return self._subject_information_access_value 2468 2469 @property 2470 def tls_feature_value(self): 2471 """ 2472 This extension is used to list the TLS features a server must respond 2473 with if a client initiates a request supporting them. 2474 2475 :return: 2476 None or a Features object 2477 """ 2478 2479 if not self._processed_extensions: 2480 self._set_extensions() 2481 return self._tls_feature_value 2482 2483 @property 2484 def ocsp_no_check_value(self): 2485 """ 2486 This extension is used on certificates of OCSP responders, indicating 2487 that revocation information for the certificate should never need to 2488 be verified, thus preventing possible loops in path validation. 2489 2490 :return: 2491 None or a Null object (if present) 2492 """ 2493 2494 if not self._processed_extensions: 2495 self._set_extensions() 2496 return self._ocsp_no_check_value 2497 2498 @property 2499 def signature(self): 2500 """ 2501 :return: 2502 A byte string of the signature 2503 """ 2504 2505 return self['signature_value'].native 2506 2507 @property 2508 def signature_algo(self): 2509 """ 2510 :return: 2511 A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa" 2512 """ 2513 2514 return self['signature_algorithm'].signature_algo 2515 2516 @property 2517 def hash_algo(self): 2518 """ 2519 :return: 2520 A unicode string of "md2", "md5", "sha1", "sha224", "sha256", 2521 "sha384", "sha512", "sha512_224", "sha512_256" 2522 """ 2523 2524 return self['signature_algorithm'].hash_algo 2525 2526 @property 2527 def public_key(self): 2528 """ 2529 :return: 2530 The PublicKeyInfo object for this certificate 2531 """ 2532 2533 return self['tbs_certificate']['subject_public_key_info'] 2534 2535 @property 2536 def subject(self): 2537 """ 2538 :return: 2539 The Name object for the subject of this certificate 2540 """ 2541 2542 return self['tbs_certificate']['subject'] 2543 2544 @property 2545 def issuer(self): 2546 """ 2547 :return: 2548 The Name object for the issuer of this certificate 2549 """ 2550 2551 return self['tbs_certificate']['issuer'] 2552 2553 @property 2554 def serial_number(self): 2555 """ 2556 :return: 2557 An integer of the certificate's serial number 2558 """ 2559 2560 return self['tbs_certificate']['serial_number'].native 2561 2562 @property 2563 def key_identifier(self): 2564 """ 2565 :return: 2566 None or a byte string of the certificate's key identifier from the 2567 key identifier extension 2568 """ 2569 2570 if not self.key_identifier_value: 2571 return None 2572 2573 return self.key_identifier_value.native 2574 2575 @property 2576 def issuer_serial(self): 2577 """ 2578 :return: 2579 A byte string of the SHA-256 hash of the issuer concatenated with 2580 the ascii character ":", concatenated with the serial number as 2581 an ascii string 2582 """ 2583 2584 if self._issuer_serial is None: 2585 self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii') 2586 return self._issuer_serial 2587 2588 @property 2589 def not_valid_after(self): 2590 """ 2591 :return: 2592 A datetime of latest time when the certificate is still valid 2593 """ 2594 return self['tbs_certificate']['validity']['not_after'].native 2595 2596 @property 2597 def not_valid_before(self): 2598 """ 2599 :return: 2600 A datetime of the earliest time when the certificate is valid 2601 """ 2602 return self['tbs_certificate']['validity']['not_before'].native 2603 2604 @property 2605 def authority_key_identifier(self): 2606 """ 2607 :return: 2608 None or a byte string of the key_identifier from the authority key 2609 identifier extension 2610 """ 2611 2612 if not self.authority_key_identifier_value: 2613 return None 2614 2615 return self.authority_key_identifier_value['key_identifier'].native 2616 2617 @property 2618 def authority_issuer_serial(self): 2619 """ 2620 :return: 2621 None or a byte string of the SHA-256 hash of the isser from the 2622 authority key identifier extension concatenated with the ascii 2623 character ":", concatenated with the serial number from the 2624 authority key identifier extension as an ascii string 2625 """ 2626 2627 if self._authority_issuer_serial is False: 2628 akiv = self.authority_key_identifier_value 2629 if akiv and akiv['authority_cert_issuer'].native: 2630 issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen 2631 # We untag the element since it is tagged via being a choice from GeneralName 2632 issuer = issuer.untag() 2633 authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native 2634 self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii') 2635 else: 2636 self._authority_issuer_serial = None 2637 return self._authority_issuer_serial 2638 2639 @property 2640 def crl_distribution_points(self): 2641 """ 2642 Returns complete CRL URLs - does not include delta CRLs 2643 2644 :return: 2645 A list of zero or more DistributionPoint objects 2646 """ 2647 2648 if self._crl_distribution_points is None: 2649 self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value) 2650 return self._crl_distribution_points 2651 2652 @property 2653 def delta_crl_distribution_points(self): 2654 """ 2655 Returns delta CRL URLs - does not include complete CRLs 2656 2657 :return: 2658 A list of zero or more DistributionPoint objects 2659 """ 2660 2661 if self._delta_crl_distribution_points is None: 2662 self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value) 2663 return self._delta_crl_distribution_points 2664 2665 def _get_http_crl_distribution_points(self, crl_distribution_points): 2666 """ 2667 Fetches the DistributionPoint object for non-relative, HTTP CRLs 2668 referenced by the certificate 2669 2670 :param crl_distribution_points: 2671 A CRLDistributionPoints object to grab the DistributionPoints from 2672 2673 :return: 2674 A list of zero or more DistributionPoint objects 2675 """ 2676 2677 output = [] 2678 2679 if crl_distribution_points is None: 2680 return [] 2681 2682 for distribution_point in crl_distribution_points: 2683 distribution_point_name = distribution_point['distribution_point'] 2684 if distribution_point_name is VOID: 2685 continue 2686 # RFC 5280 indicates conforming CA should not use the relative form 2687 if distribution_point_name.name == 'name_relative_to_crl_issuer': 2688 continue 2689 # This library is currently only concerned with HTTP-based CRLs 2690 for general_name in distribution_point_name.chosen: 2691 if general_name.name == 'uniform_resource_identifier': 2692 output.append(distribution_point) 2693 2694 return output 2695 2696 @property 2697 def ocsp_urls(self): 2698 """ 2699 :return: 2700 A list of zero or more unicode strings of the OCSP URLs for this 2701 cert 2702 """ 2703 2704 if not self.authority_information_access_value: 2705 return [] 2706 2707 output = [] 2708 for entry in self.authority_information_access_value: 2709 if entry['access_method'].native == 'ocsp': 2710 location = entry['access_location'] 2711 if location.name != 'uniform_resource_identifier': 2712 continue 2713 url = location.native 2714 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')): 2715 output.append(url) 2716 return output 2717 2718 @property 2719 def valid_domains(self): 2720 """ 2721 :return: 2722 A list of unicode strings of valid domain names for the certificate. 2723 Wildcard certificates will have a domain in the form: *.example.com 2724 """ 2725 2726 if self._valid_domains is None: 2727 self._valid_domains = [] 2728 2729 # For the subject alt name extension, we can look at the name of 2730 # the choice selected since it distinguishes between domain names, 2731 # email addresses, IPs, etc 2732 if self.subject_alt_name_value: 2733 for general_name in self.subject_alt_name_value: 2734 if general_name.name == 'dns_name' and general_name.native not in self._valid_domains: 2735 self._valid_domains.append(general_name.native) 2736 2737 # If there was no subject alt name extension, and the common name 2738 # in the subject looks like a domain, that is considered the valid 2739 # list. This is done because according to 2740 # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common 2741 # name should not be used if the subject alt name is present. 2742 else: 2743 pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$') 2744 for rdn in self.subject.chosen: 2745 for name_type_value in rdn: 2746 if name_type_value['type'].native == 'common_name': 2747 value = name_type_value['value'].native 2748 if pattern.match(value): 2749 self._valid_domains.append(value) 2750 2751 return self._valid_domains 2752 2753 @property 2754 def valid_ips(self): 2755 """ 2756 :return: 2757 A list of unicode strings of valid IP addresses for the certificate 2758 """ 2759 2760 if self._valid_ips is None: 2761 self._valid_ips = [] 2762 2763 if self.subject_alt_name_value: 2764 for general_name in self.subject_alt_name_value: 2765 if general_name.name == 'ip_address': 2766 self._valid_ips.append(general_name.native) 2767 2768 return self._valid_ips 2769 2770 @property 2771 def ca(self): 2772 """ 2773 :return; 2774 A boolean - if the certificate is marked as a CA 2775 """ 2776 2777 return self.basic_constraints_value and self.basic_constraints_value['ca'].native 2778 2779 @property 2780 def max_path_length(self): 2781 """ 2782 :return; 2783 None or an integer of the maximum path length 2784 """ 2785 2786 if not self.ca: 2787 return None 2788 return self.basic_constraints_value['path_len_constraint'].native 2789 2790 @property 2791 def self_issued(self): 2792 """ 2793 :return: 2794 A boolean - if the certificate is self-issued, as defined by RFC 2795 5280 2796 """ 2797 2798 if self._self_issued is None: 2799 self._self_issued = self.subject == self.issuer 2800 return self._self_issued 2801 2802 @property 2803 def self_signed(self): 2804 """ 2805 :return: 2806 A unicode string of "no" or "maybe". The "maybe" result will 2807 be returned if the certificate issuer and subject are the same. 2808 If a key identifier and authority key identifier are present, 2809 they will need to match otherwise "no" will be returned. 2810 2811 To verify is a certificate is truly self-signed, the signature 2812 will need to be verified. See the certvalidator package for 2813 one possible solution. 2814 """ 2815 2816 if self._self_signed is None: 2817 self._self_signed = 'no' 2818 if self.self_issued: 2819 if self.key_identifier: 2820 if not self.authority_key_identifier: 2821 self._self_signed = 'maybe' 2822 elif self.authority_key_identifier == self.key_identifier: 2823 self._self_signed = 'maybe' 2824 else: 2825 self._self_signed = 'maybe' 2826 return self._self_signed 2827 2828 @property 2829 def sha1(self): 2830 """ 2831 :return: 2832 The SHA-1 hash of the DER-encoded bytes of this complete certificate 2833 """ 2834 2835 if self._sha1 is None: 2836 self._sha1 = hashlib.sha1(self.dump()).digest() 2837 return self._sha1 2838 2839 @property 2840 def sha1_fingerprint(self): 2841 """ 2842 :return: 2843 A unicode string of the SHA-1 hash, formatted using hex encoding 2844 with a space between each pair of characters, all uppercase 2845 """ 2846 2847 return ' '.join('%02X' % c for c in bytes_to_list(self.sha1)) 2848 2849 @property 2850 def sha256(self): 2851 """ 2852 :return: 2853 The SHA-256 hash of the DER-encoded bytes of this complete 2854 certificate 2855 """ 2856 2857 if self._sha256 is None: 2858 self._sha256 = hashlib.sha256(self.dump()).digest() 2859 return self._sha256 2860 2861 @property 2862 def sha256_fingerprint(self): 2863 """ 2864 :return: 2865 A unicode string of the SHA-256 hash, formatted using hex encoding 2866 with a space between each pair of characters, all uppercase 2867 """ 2868 2869 return ' '.join('%02X' % c for c in bytes_to_list(self.sha256)) 2870 2871 def is_valid_domain_ip(self, domain_ip): 2872 """ 2873 Check if a domain name or IP address is valid according to the 2874 certificate 2875 2876 :param domain_ip: 2877 A unicode string of a domain name or IP address 2878 2879 :return: 2880 A boolean - if the domain or IP is valid for the certificate 2881 """ 2882 2883 if not isinstance(domain_ip, str_cls): 2884 raise TypeError(unwrap( 2885 ''' 2886 domain_ip must be a unicode string, not %s 2887 ''', 2888 type_name(domain_ip) 2889 )) 2890 2891 encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower() 2892 2893 is_ipv6 = encoded_domain_ip.find(':') != -1 2894 is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip) 2895 is_domain = not is_ipv6 and not is_ipv4 2896 2897 # Handle domain name checks 2898 if is_domain: 2899 if not self.valid_domains: 2900 return False 2901 2902 domain_labels = encoded_domain_ip.split('.') 2903 2904 for valid_domain in self.valid_domains: 2905 encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower() 2906 valid_domain_labels = encoded_valid_domain.split('.') 2907 2908 # The domain must be equal in label length to match 2909 if len(valid_domain_labels) != len(domain_labels): 2910 continue 2911 2912 if valid_domain_labels == domain_labels: 2913 return True 2914 2915 is_wildcard = self._is_wildcard_domain(encoded_valid_domain) 2916 if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels): 2917 return True 2918 2919 return False 2920 2921 # Handle IP address checks 2922 if not self.valid_ips: 2923 return False 2924 2925 family = socket.AF_INET if is_ipv4 else socket.AF_INET6 2926 normalized_ip = inet_pton(family, encoded_domain_ip) 2927 2928 for valid_ip in self.valid_ips: 2929 valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6 2930 normalized_valid_ip = inet_pton(valid_family, valid_ip) 2931 2932 if normalized_valid_ip == normalized_ip: 2933 return True 2934 2935 return False 2936 2937 def _is_wildcard_domain(self, domain): 2938 """ 2939 Checks if a domain is a valid wildcard according to 2940 https://tools.ietf.org/html/rfc6125#section-6.4.3 2941 2942 :param domain: 2943 A unicode string of the domain name, where any U-labels from an IDN 2944 have been converted to A-labels 2945 2946 :return: 2947 A boolean - if the domain is a valid wildcard domain 2948 """ 2949 2950 # The * character must be present for a wildcard match, and if there is 2951 # most than one, it is an invalid wildcard specification 2952 if domain.count('*') != 1: 2953 return False 2954 2955 labels = domain.lower().split('.') 2956 2957 if not labels: 2958 return False 2959 2960 # Wildcards may only appear in the left-most label 2961 if labels[0].find('*') == -1: 2962 return False 2963 2964 # Wildcards may not be embedded in an A-label from an IDN 2965 if labels[0][0:4] == 'xn--': 2966 return False 2967 2968 return True 2969 2970 def _is_wildcard_match(self, domain_labels, valid_domain_labels): 2971 """ 2972 Determines if the labels in a domain are a match for labels from a 2973 wildcard valid domain name 2974 2975 :param domain_labels: 2976 A list of unicode strings, with A-label form for IDNs, of the labels 2977 in the domain name to check 2978 2979 :param valid_domain_labels: 2980 A list of unicode strings, with A-label form for IDNs, of the labels 2981 in a wildcard domain pattern 2982 2983 :return: 2984 A boolean - if the domain matches the valid domain 2985 """ 2986 2987 first_domain_label = domain_labels[0] 2988 other_domain_labels = domain_labels[1:] 2989 2990 wildcard_label = valid_domain_labels[0] 2991 other_valid_domain_labels = valid_domain_labels[1:] 2992 2993 # The wildcard is only allowed in the first label, so if 2994 # The subsequent labels are not equal, there is no match 2995 if other_domain_labels != other_valid_domain_labels: 2996 return False 2997 2998 if wildcard_label == '*': 2999 return True 3000 3001 wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$') 3002 if wildcard_regex.match(first_domain_label): 3003 return True 3004 3005 return False 3006 3007 3008# The structures are taken from the OpenSSL source file x_x509a.c, and specify 3009# extra information that is added to X.509 certificates to store trust 3010# information about the certificate. 3011 3012class KeyPurposeIdentifiers(SequenceOf): 3013 _child_spec = KeyPurposeId 3014 3015 3016class SequenceOfAlgorithmIdentifiers(SequenceOf): 3017 _child_spec = AlgorithmIdentifier 3018 3019 3020class CertificateAux(Sequence): 3021 _fields = [ 3022 ('trust', KeyPurposeIdentifiers, {'optional': True}), 3023 ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}), 3024 ('alias', UTF8String, {'optional': True}), 3025 ('keyid', OctetString, {'optional': True}), 3026 ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}), 3027 ] 3028 3029 3030class TrustedCertificate(Concat): 3031 _child_specs = [Certificate, CertificateAux] 3032