Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/asn1crypto/x509.py: 46%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# coding: utf-8
3"""
4ASN.1 type classes for X.509 certificates. Exports the following items:
6 - Attributes()
7 - Certificate()
8 - Extensions()
9 - GeneralName()
10 - GeneralNames()
11 - Name()
13Other type classes are defined that help compose the types listed above.
14"""
16from __future__ import unicode_literals, division, absolute_import, print_function
18from contextlib import contextmanager
19from encodings import idna # noqa
20import hashlib
21import re
22import socket
23import stringprep
24import sys
25import unicodedata
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
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
71class DNSName(IA5String):
73 _encoding = 'idna'
74 _bad_tag = (12, 19)
76 def __ne__(self, other):
77 return not self == other
79 def __eq__(self, other):
80 """
81 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.2
83 :param other:
84 Another DNSName object
86 :return:
87 A boolean
88 """
90 if not isinstance(other, DNSName):
91 return False
93 return self.__unicode__().lower() == other.__unicode__().lower()
95 def set(self, value):
96 """
97 Sets the value of the DNS name
99 :param value:
100 A unicode string
101 """
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 ))
112 if value.startswith('.'):
113 encoded_value = b'.' + value[1:].encode(self._encoding)
114 else:
115 encoded_value = value.encode(self._encoding)
117 self._unicode = value
118 self.contents = encoded_value
119 self._header = None
120 if self._trailer != b'':
121 self._trailer = b''
124class URI(IA5String):
126 def set(self, value):
127 """
128 Sets the value of the string
130 :param value:
131 A unicode string
132 """
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 ))
143 self._unicode = value
144 self.contents = iri_to_uri(value)
145 self._header = None
146 if self._trailer != b'':
147 self._trailer = b''
149 def __ne__(self, other):
150 return not self == other
152 def __eq__(self, other):
153 """
154 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.4
156 :param other:
157 Another URI object
159 :return:
160 A boolean
161 """
163 if not isinstance(other, URI):
164 return False
166 return iri_to_uri(self.native, True) == iri_to_uri(other.native, True)
168 def __unicode__(self):
169 """
170 :return:
171 A unicode string
172 """
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
181class EmailAddress(IA5String):
183 _contents = None
185 # If the value has gone through the .set() method, thus normalizing it
186 _normalized = False
188 # In the wild we've seen this encoded as a UTF8String and PrintableString
189 _bad_tag = (12, 19)
191 @property
192 def contents(self):
193 """
194 :return:
195 A byte string of the DER-encoded contents of the sequence
196 """
198 return self._contents
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 """
207 self._normalized = False
208 self._contents = value
210 def set(self, value):
211 """
212 Sets the value of the string
214 :param value:
215 A unicode string
216 """
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 ))
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')
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''
240 def __unicode__(self):
241 """
242 :return:
243 A unicode string
244 """
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
257 def __ne__(self, other):
258 return not self == other
260 def __eq__(self, other):
261 """
262 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.5
264 :param other:
265 Another EmailAddress object
267 :return:
268 A boolean
269 """
271 if not isinstance(other, EmailAddress):
272 return False
274 if not self._normalized:
275 self.set(self.native)
276 if not other._normalized:
277 other.set(other.native)
279 if self._contents.find(b'@') == -1 or other._contents.find(b'@') == -1:
280 return self._contents == other._contents
282 other_mailbox, other_hostname = other._contents.rsplit(b'@', 1)
283 mailbox, hostname = self._contents.rsplit(b'@', 1)
285 if mailbox != other_mailbox:
286 return False
288 if hostname.lower() != other_hostname.lower():
289 return False
291 return True
294class IPAddress(OctetString):
295 def parse(self, spec=None, spec_params=None):
296 """
297 This method is not applicable to IP addresses
298 """
300 raise ValueError(unwrap(
301 '''
302 IP address values can not be parsed
303 '''
304 ))
306 def set(self, value):
307 """
308 Sets the value of the object
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 """
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 ))
324 original_value = value
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 ))
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
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
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''
377 @property
378 def native(self):
379 """
380 The native Python datatype representation of this value
382 :return:
383 A unicode string or None
384 """
386 if self.contents is None:
387 return None
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
409 def __ne__(self, other):
410 return not self == other
412 def __eq__(self, other):
413 """
414 :param other:
415 Another IPAddress object
417 :return:
418 A boolean
419 """
421 if not isinstance(other, IPAddress):
422 return False
424 return self.__bytes__() == other.__bytes__()
427class Attribute(Sequence):
428 _fields = [
429 ('type', ObjectIdentifier),
430 ('values', SetOf, {'spec': Any}),
431 ]
434class Attributes(SequenceOf):
435 _child_spec = Attribute
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 }
452class PrivateKeyUsagePeriod(Sequence):
453 _fields = [
454 ('not_before', GeneralizedTime, {'implicit': 0, 'optional': True}),
455 ('not_after', GeneralizedTime, {'implicit': 1, 'optional': True}),
456 ]
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 """
468 _decoding_encoding = 'cp1252'
470 def __unicode__(self):
471 """
472 :return:
473 A unicode string
474 """
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
483@contextmanager
484def strict_teletex():
485 try:
486 NotReallyTeletexString._decoding_encoding = 'teletex'
487 yield
488 finally:
489 NotReallyTeletexString._decoding_encoding = 'cp1252'
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 ]
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 }
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 ]
587 @classmethod
588 def preferred_ordinal(cls, attr_name):
589 """
590 Returns an ordering value for a particular attribute key.
592 Unrecognized attributes and OIDs will be sorted lexically at the end.
594 :return:
595 An orderable value.
597 """
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)
605 return (ordinal, attr_name)
607 @property
608 def human_friendly(self):
609 """
610 :return:
611 A human-friendly unicode string to display to users
612 """
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)
652class NameTypeAndValue(Sequence):
653 _fields = [
654 ('type', NameType),
655 ('value', Any),
656 ]
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 }
698 _prepped = None
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
706 :return:
707 A unicode string
708 """
710 if self._prepped is None:
711 self._prepped = self._ldap_string_prep(self['value'].native)
712 return self._prepped
714 def __ne__(self, other):
715 return not self == other
717 def __eq__(self, other):
718 """
719 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
721 :param other:
722 Another NameTypeAndValue object
724 :return:
725 A boolean
726 """
728 if not isinstance(other, NameTypeAndValue):
729 return False
731 if other['type'].native != self['type'].native:
732 return False
734 return other.prepped_value == self.prepped_value
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
741 :param string:
742 A unicode string to prepare
744 :return:
745 A prepared unicode string, ready for comparison
746 """
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)
767 string = ''.join(map(stringprep.map_table_b2, string))
769 # Normalize step
770 string = unicodedata.normalize('NFKC', string)
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 ))
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 ))
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 ))
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 ))
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 ))
810 if char == '\ufffd':
811 raise ValueError(unwrap(
812 '''
813 X.509 Name objects may not contain the replacement character
814 '''
815 ))
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
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])
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 ))
839 # Insignificant space handling step
840 string = ' ' + re.sub(' +', ' ', string).strip() + ' '
842 return string
845class RelativeDistinguishedName(SetOf):
846 _child_spec = NameTypeAndValue
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 """
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)
864 def __ne__(self, other):
865 return not self == other
867 def __eq__(self, other):
868 """
869 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
871 :param other:
872 Another RelativeDistinguishedName object
874 :return:
875 A boolean
876 """
878 if not isinstance(other, RelativeDistinguishedName):
879 return False
881 if len(self) != len(other):
882 return False
884 self_types = self._get_types(self)
885 other_types = self._get_types(other)
887 if self_types != other_types:
888 return False
890 self_values = self._get_values(self)
891 other_values = self._get_values(other)
893 for type_name_ in self_types:
894 if self_values[type_name_] != other_values[type_name_]:
895 return False
897 return True
899 def _get_types(self, rdn):
900 """
901 Returns a set of types contained in an RDN
903 :param rdn:
904 A RelativeDistinguishedName object
906 :return:
907 A set object with unicode strings of NameTypeAndValue type field
908 values
909 """
911 return set([ntv['type'].native for ntv in rdn])
913 def _get_values(self, rdn):
914 """
915 Returns a dict of prepped values contained in an RDN
917 :param rdn:
918 A RelativeDistinguishedName object
920 :return:
921 A dict object with unicode strings of NameTypeAndValue value field
922 values that have been prepped for comparison
923 """
925 output = {}
926 [output.update([(ntv['type'].native, ntv.prepped_value)]) for ntv in rdn]
927 return output
930class RDNSequence(SequenceOf):
931 _child_spec = RelativeDistinguishedName
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 """
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)
945 def __ne__(self, other):
946 return not self == other
948 def __eq__(self, other):
949 """
950 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
952 :param other:
953 Another RDNSequence object
955 :return:
956 A boolean
957 """
959 if not isinstance(other, RDNSequence):
960 return False
962 if len(self) != len(other):
963 return False
965 for index, self_rdn in enumerate(self):
966 if other[index] != self_rdn:
967 return False
969 return True
972class Name(Choice):
973 _alternatives = [
974 ('', RDNSequence),
975 ]
977 _human_friendly = None
978 _sha1 = None
979 _sha256 = None
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.
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"}
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.
996 :return:
997 An x509.Name object
998 """
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
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 )
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 )
1033 rdns.append(RelativeDistinguishedName([
1034 NameTypeAndValue({
1035 'type': attribute_name,
1036 'value': value
1037 })
1038 ]))
1040 return cls(name='', value=RDNSequence(rdns))
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 """
1049 return self.chosen.hashable
1051 def __len__(self):
1052 return len(self.chosen)
1054 def __ne__(self, other):
1055 return not self == other
1057 def __eq__(self, other):
1058 """
1059 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
1061 :param other:
1062 Another Name object
1064 :return:
1065 A boolean
1066 """
1068 if not isinstance(other, Name):
1069 return False
1070 return self.chosen == other.chosen
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
1088 @property
1089 def human_friendly(self):
1090 """
1091 :return:
1092 A human-friendly unicode string containing the parts of the name
1093 """
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))
1116 has_comma = False
1117 for element in to_join:
1118 if element.find(',') != -1:
1119 has_comma = True
1120 break
1122 separator = ', ' if not has_comma else '; '
1123 self._human_friendly = separator.join(to_join[::-1])
1125 return self._human_friendly
1127 def _recursive_humanize(self, value):
1128 """
1129 Recursively serializes data compiled from the RDNSequence
1131 :param value:
1132 An Asn1Value object, or a list of Asn1Value objects
1134 :return:
1135 A unicode string
1136 """
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
1144 @property
1145 def sha1(self):
1146 """
1147 :return:
1148 The SHA1 hash of the DER-encoded bytes of this name
1149 """
1151 if self._sha1 is None:
1152 self._sha1 = hashlib.sha1(self.dump()).digest()
1153 return self._sha1
1155 @property
1156 def sha256(self):
1157 """
1158 :return:
1159 The SHA-256 hash of the DER-encoded bytes of this name
1160 """
1162 if self._sha256 is None:
1163 self._sha256 = hashlib.sha256(self.dump()).digest()
1164 return self._sha256
1167class AnotherName(Sequence):
1168 _fields = [
1169 ('type_id', ObjectIdentifier),
1170 ('value', Any, {'explicit': 0}),
1171 ]
1174class CountryName(Choice):
1175 class_ = 1
1176 tag = 1
1178 _alternatives = [
1179 ('x121_dcc_code', NumericString),
1180 ('iso_3166_alpha2_code', PrintableString),
1181 ]
1184class AdministrationDomainName(Choice):
1185 class_ = 1
1186 tag = 2
1188 _alternatives = [
1189 ('numeric', NumericString),
1190 ('printable', PrintableString),
1191 ]
1194class PrivateDomainName(Choice):
1195 _alternatives = [
1196 ('numeric', NumericString),
1197 ('printable', PrintableString),
1198 ]
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 ]
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 ]
1219class OrganizationalUnitNames(SequenceOf):
1220 _child_spec = PrintableString
1223class TeletexOrganizationalUnitNames(SequenceOf):
1224 _child_spec = TeletexString
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 ]
1241class BuiltInDomainDefinedAttribute(Sequence):
1242 _fields = [
1243 ('type', PrintableString),
1244 ('value', PrintableString),
1245 ]
1248class BuiltInDomainDefinedAttributes(SequenceOf):
1249 _child_spec = BuiltInDomainDefinedAttribute
1252class TeletexDomainDefinedAttribute(Sequence):
1253 _fields = [
1254 ('type', TeletexString),
1255 ('value', TeletexString),
1256 ]
1259class TeletexDomainDefinedAttributes(SequenceOf):
1260 _child_spec = TeletexDomainDefinedAttribute
1263class PhysicalDeliveryCountryName(Choice):
1264 _alternatives = [
1265 ('x121_dcc_code', NumericString),
1266 ('iso_3166_alpha2_code', PrintableString),
1267 ]
1270class PostalCode(Choice):
1271 _alternatives = [
1272 ('numeric_code', NumericString),
1273 ('printable_code', PrintableString),
1274 ]
1277class PDSParameter(Set):
1278 _fields = [
1279 ('printable_string', PrintableString, {'optional': True}),
1280 ('teletex_string', TeletexString, {'optional': True}),
1281 ]
1284class PrintableAddress(SequenceOf):
1285 _child_spec = PrintableString
1288class UnformattedPostalAddress(Set):
1289 _fields = [
1290 ('printable_address', PrintableAddress, {'optional': True}),
1291 ('teletex_string', TeletexString, {'optional': True}),
1292 ]
1295class E1634Address(Sequence):
1296 _fields = [
1297 ('number', NumericString, {'implicit': 0}),
1298 ('sub_address', NumericString, {'implicit': 1, 'optional': True}),
1299 ]
1302class NAddresses(SetOf):
1303 _child_spec = OctetString
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 ]
1315class ExtendedNetworkAddress(Choice):
1316 _alternatives = [
1317 ('e163_4_address', E1634Address),
1318 ('psap_address', PresentationAddress, {'implicit': 0})
1319 ]
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 }
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 }
1361class ExtensionAttribute(Sequence):
1362 _fields = [
1363 ('extension_attribute_type', ExtensionAttributeType, {'implicit': 0}),
1364 ('extension_attribute_value', Any, {'explicit': 1}),
1365 ]
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 }
1395class ExtensionAttributes(SequenceOf):
1396 _child_spec = ExtensionAttribute
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 ]
1407class EDIPartyName(Sequence):
1408 _fields = [
1409 ('name_assigner', DirectoryString, {'implicit': 0, 'optional': True}),
1410 ('party_name', DirectoryString, {'implicit': 1}),
1411 ]
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 ]
1427 def __ne__(self, other):
1428 return not self == other
1430 def __eq__(self, other):
1431 """
1432 Does not support other_name, x400_address or edi_party_name
1434 :param other:
1435 The other GeneralName to compare to
1437 :return:
1438 A boolean
1439 """
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 ))
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 ))
1458 if self.name != other.name:
1459 return False
1461 return self.chosen == other.chosen
1464class GeneralNames(SequenceOf):
1465 _child_spec = GeneralName
1468class Time(Choice):
1469 _alternatives = [
1470 ('utc_time', UTCTime),
1471 ('general_time', GeneralizedTime),
1472 ]
1475class Validity(Sequence):
1476 _fields = [
1477 ('not_before', Time),
1478 ('not_after', Time),
1479 ]
1482class BasicConstraints(Sequence):
1483 _fields = [
1484 ('ca', Boolean, {'default': False}),
1485 ('path_len_constraint', Integer, {'optional': True}),
1486 ]
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 ]
1497class DistributionPointName(Choice):
1498 _alternatives = [
1499 ('full_name', GeneralNames, {'implicit': 0}),
1500 ('name_relative_to_crl_issuer', RelativeDistinguishedName, {'implicit': 1}),
1501 ]
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 }
1518class GeneralSubtree(Sequence):
1519 _fields = [
1520 ('base', GeneralName),
1521 ('minimum', Integer, {'implicit': 0, 'default': 0}),
1522 ('maximum', Integer, {'implicit': 1, 'optional': True}),
1523 ]
1526class GeneralSubtrees(SequenceOf):
1527 _child_spec = GeneralSubtree
1530class NameConstraints(Sequence):
1531 _fields = [
1532 ('permitted_subtrees', GeneralSubtrees, {'implicit': 0, 'optional': True}),
1533 ('excluded_subtrees', GeneralSubtrees, {'implicit': 1, 'optional': True}),
1534 ]
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 ]
1544 _url = False
1546 @property
1547 def url(self):
1548 """
1549 :return:
1550 None or a unicode string of the distribution point's URL
1551 """
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 ))
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
1571 return self._url
1574class CRLDistributionPoints(SequenceOf):
1575 _child_spec = DistributionPoint
1578class DisplayText(Choice):
1579 _alternatives = [
1580 ('ia5_string', IA5String),
1581 ('visible_string', VisibleString),
1582 ('bmp_string', BMPString),
1583 ('utf8_string', UTF8String),
1584 ]
1587class NoticeNumbers(SequenceOf):
1588 _child_spec = Integer
1591class NoticeReference(Sequence):
1592 _fields = [
1593 ('organization', DisplayText),
1594 ('notice_numbers', NoticeNumbers),
1595 ]
1598class UserNotice(Sequence):
1599 _fields = [
1600 ('notice_ref', NoticeReference, {'optional': True}),
1601 ('explicit_text', DisplayText, {'optional': True}),
1602 ]
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 }
1612class PolicyQualifierInfo(Sequence):
1613 _fields = [
1614 ('policy_qualifier_id', PolicyQualifierId),
1615 ('qualifier', Any),
1616 ]
1618 _oid_pair = ('policy_qualifier_id', 'qualifier')
1619 _oid_specs = {
1620 'certification_practice_statement': IA5String,
1621 'user_notice': UserNotice,
1622 }
1625class PolicyQualifierInfos(SequenceOf):
1626 _child_spec = PolicyQualifierInfo
1629class PolicyIdentifier(ObjectIdentifier):
1630 _map = {
1631 '2.5.29.32.0': 'any_policy',
1632 }
1635class PolicyInformation(Sequence):
1636 _fields = [
1637 ('policy_identifier', PolicyIdentifier),
1638 ('policy_qualifiers', PolicyQualifierInfos, {'optional': True})
1639 ]
1642class CertificatePolicies(SequenceOf):
1643 _child_spec = PolicyInformation
1646class PolicyMapping(Sequence):
1647 _fields = [
1648 ('issuer_domain_policy', PolicyIdentifier),
1649 ('subject_domain_policy', PolicyIdentifier),
1650 ]
1653class PolicyMappings(SequenceOf):
1654 _child_spec = PolicyMapping
1657class PolicyConstraints(Sequence):
1658 _fields = [
1659 ('require_explicit_policy', Integer, {'implicit': 0, 'optional': True}),
1660 ('inhibit_policy_mapping', Integer, {'implicit': 1, 'optional': True}),
1661 ]
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 }
1776class ExtKeyUsageSyntax(SequenceOf):
1777 _child_spec = KeyPurposeId
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 }
1789class AccessDescription(Sequence):
1790 _fields = [
1791 ('access_method', AccessMethod),
1792 ('access_location', GeneralName),
1793 ]
1796class AuthorityInfoAccessSyntax(SequenceOf):
1797 _child_spec = AccessDescription
1800class SubjectInfoAccessSyntax(SequenceOf):
1801 _child_spec = AccessDescription
1804# https://tools.ietf.org/html/rfc7633
1805class Features(SequenceOf):
1806 _child_spec = Integer
1809class EntrustVersionInfo(Sequence):
1810 _fields = [
1811 ('entrust_vers', GeneralString),
1812 ('entrust_info_flags', BitString)
1813 ]
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 }
1829class Version(Integer):
1830 _map = {
1831 0: 'v1',
1832 1: 'v2',
1833 2: 'v3',
1834 }
1837class TPMSpecification(Sequence):
1838 _fields = [
1839 ('family', UTF8String),
1840 ('level', Integer),
1841 ('revision', Integer),
1842 ]
1845class SetOfTPMSpecification(SetOf):
1846 _child_spec = TPMSpecification
1849class TCGSpecificationVersion(Sequence):
1850 _fields = [
1851 ('major_version', Integer),
1852 ('minor_version', Integer),
1853 ('revision', Integer),
1854 ]
1857class TCGPlatformSpecification(Sequence):
1858 _fields = [
1859 ('version', TCGSpecificationVersion),
1860 ('platform_class', OctetString),
1861 ]
1864class SetOfTCGPlatformSpecification(SetOf):
1865 _child_spec = TCGPlatformSpecification
1868class EKGenerationType(Enumerated):
1869 _map = {
1870 0: 'internal',
1871 1: 'injected',
1872 2: 'internal_revocable',
1873 3: 'injected_revocable',
1874 }
1877class EKGenerationLocation(Enumerated):
1878 _map = {
1879 0: 'tpm_manufacturer',
1880 1: 'platform_manufacturer',
1881 2: 'ek_cert_signer',
1882 }
1885class EKCertificateGenerationLocation(Enumerated):
1886 _map = {
1887 0: 'tpm_manufacturer',
1888 1: 'platform_manufacturer',
1889 2: 'ek_cert_signer',
1890 }
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 }
1905class EvaluationStatus(Enumerated):
1906 _map = {
1907 0: 'designed_to_meet',
1908 1: 'evaluation_in_progress',
1909 2: 'evaluation_completed',
1910 }
1913class StrengthOfFunction(Enumerated):
1914 _map = {
1915 0: 'basic',
1916 1: 'medium',
1917 2: 'high',
1918 }
1921class URIReference(Sequence):
1922 _fields = [
1923 ('uniform_resource_identifier', IA5String),
1924 ('hash_algorithm', DigestAlgorithm, {'optional': True}),
1925 ('hash_value', BitString, {'optional': True}),
1926 ]
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 ]
1943class SecurityLevel(Enumerated):
1944 _map = {
1945 1: 'level1',
1946 2: 'level2',
1947 3: 'level3',
1948 4: 'level4',
1949 }
1952class FIPSLevel(Sequence):
1953 _fields = [
1954 ('version', IA5String),
1955 ('level', SecurityLevel),
1956 ('plus', Boolean, {'default': False}),
1957 ]
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 ]
1974class SetOfTPMSecurityAssertions(SetOf):
1975 _child_spec = TPMSecurityAssertions
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 }
1997class SetOfGeneralizedTime(SetOf):
1998 _child_spec = GeneralizedTime
2001class SetOfDirectoryString(SetOf):
2002 _child_spec = DirectoryString
2005class SetOfPrintableString(SetOf):
2006 _child_spec = PrintableString
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 ]
2017class SetOfSupportedAlgorithm(SetOf):
2018 _child_spec = SupportedAlgorithm
2021class SubjectDirectoryAttribute(Sequence):
2022 _fields = [
2023 ('type', SubjectDirectoryAttributeId),
2024 ('values', Any),
2025 ]
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 }
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
2046 _spec_callbacks = {
2047 'values': _values_spec
2048 }
2051class SubjectDirectoryAttributes(SequenceOf):
2052 _child_spec = SubjectDirectoryAttribute
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 }
2087class Extension(Sequence):
2088 _fields = [
2089 ('extn_id', ExtensionId),
2090 ('critical', Boolean, {'default': False}),
2091 ('extn_value', ParsableOctetString),
2092 ]
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 }
2125class Extensions(SequenceOf):
2126 _child_spec = Extension
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 ]
2144class Certificate(Sequence):
2145 _fields = [
2146 ('tbs_certificate', TbsCertificate),
2147 ('signature_algorithm', SignedDigestAlgorithm),
2148 ('signature_value', OctetBitString),
2149 ]
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
2184 def _set_extensions(self):
2185 """
2186 Sets common named extensions to private attributes and creates a list
2187 of critical extensions
2188 """
2190 self._critical_extensions = set()
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)
2200 self._processed_extensions = True
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
2208 :return:
2209 A set of unicode strings
2210 """
2212 if not self._processed_extensions:
2213 self._set_extensions()
2214 return self._critical_extensions
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
2222 :return:
2223 None or a PrivateKeyUsagePeriod object
2224 """
2226 if not self._processed_extensions:
2227 self._set_extensions()
2228 return self._private_key_usage_period_value
2230 @property
2231 def subject_directory_attributes_value(self):
2232 """
2233 This extension is used to contain additional identification attributes
2234 about the subject.
2236 :return:
2237 None or a SubjectDirectoryAttributes object
2238 """
2240 if not self._processed_extensions:
2241 self._set_extensions()
2242 return self._subject_directory_attributes_value
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.
2251 :return:
2252 None or an OctetString object
2253 """
2255 if not self._processed_extensions:
2256 self._set_extensions()
2257 return self._key_identifier_value
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.
2265 :return:
2266 None or a KeyUsage
2267 """
2269 if not self._processed_extensions:
2270 self._set_extensions()
2271 return self._key_usage_value
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.
2281 :return:
2282 None or a GeneralNames object
2283 """
2285 if not self._processed_extensions:
2286 self._set_extensions()
2287 return self._subject_alt_name_value
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.
2295 :return:
2296 None or an x509.GeneralNames object
2297 """
2299 if not self._processed_extensions:
2300 self._set_extensions()
2301 return self._issuer_alt_name_value
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.
2310 :return:
2311 None or a BasicConstraints object
2312 """
2314 if not self._processed_extensions:
2315 self._set_extensions()
2316 return self._basic_constraints_value
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.
2324 :return:
2325 None or a NameConstraints object
2326 """
2328 if not self._processed_extensions:
2329 self._set_extensions()
2330 return self._name_constraints_value
2332 @property
2333 def crl_distribution_points_value(self):
2334 """
2335 This extension is used to help in locating the CRL for this certificate.
2337 :return:
2338 None or a CRLDistributionPoints object
2339 extension
2340 """
2342 if not self._processed_extensions:
2343 self._set_extensions()
2344 return self._crl_distribution_points_value
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.
2354 :return:
2355 None or a CertificatePolicies object
2356 """
2358 if not self._processed_extensions:
2359 self._set_extensions()
2360 return self._certificate_policies_value
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.
2369 :return:
2370 None or a PolicyMappings object
2371 """
2373 if not self._processed_extensions:
2374 self._set_extensions()
2375 return self._policy_mappings_value
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.
2383 :return:
2384 None or an AuthorityKeyIdentifier object
2385 """
2387 if not self._processed_extensions:
2388 self._set_extensions()
2389 return self._authority_key_identifier_value
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.
2397 :return:
2398 None or a PolicyConstraints object
2399 """
2401 if not self._processed_extensions:
2402 self._set_extensions()
2403 return self._policy_constraints_value
2405 @property
2406 def freshest_crl_value(self):
2407 """
2408 This extension is used to help locate any available delta CRLs
2410 :return:
2411 None or an CRLDistributionPoints object
2412 """
2414 if not self._processed_extensions:
2415 self._set_extensions()
2416 return self._freshest_crl_value
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
2424 :return:
2425 None or a Integer object
2426 """
2428 if not self._processed_extensions:
2429 self._set_extensions()
2430 return self._inhibit_any_policy_value
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.
2438 :return:
2439 None or an ExtKeyUsageSyntax object
2440 """
2442 if not self._processed_extensions:
2443 self._set_extensions()
2444 return self._extended_key_usage_value
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.
2452 :return:
2453 None or an AuthorityInfoAccessSyntax object
2454 """
2456 if not self._processed_extensions:
2457 self._set_extensions()
2458 return self._authority_information_access_value
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.
2466 :return:
2467 None or a SubjectInfoAccessSyntax object
2468 """
2470 if not self._processed_extensions:
2471 self._set_extensions()
2472 return self._subject_information_access_value
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.
2480 :return:
2481 None or a Features object
2482 """
2484 if not self._processed_extensions:
2485 self._set_extensions()
2486 return self._tls_feature_value
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.
2495 :return:
2496 None or a Null object (if present)
2497 """
2499 if not self._processed_extensions:
2500 self._set_extensions()
2501 return self._ocsp_no_check_value
2503 @property
2504 def signature(self):
2505 """
2506 :return:
2507 A byte string of the signature
2508 """
2510 return self['signature_value'].native
2512 @property
2513 def signature_algo(self):
2514 """
2515 :return:
2516 A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa"
2517 """
2519 return self['signature_algorithm'].signature_algo
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 """
2529 return self['signature_algorithm'].hash_algo
2531 @property
2532 def public_key(self):
2533 """
2534 :return:
2535 The PublicKeyInfo object for this certificate
2536 """
2538 return self['tbs_certificate']['subject_public_key_info']
2540 @property
2541 def subject(self):
2542 """
2543 :return:
2544 The Name object for the subject of this certificate
2545 """
2547 return self['tbs_certificate']['subject']
2549 @property
2550 def issuer(self):
2551 """
2552 :return:
2553 The Name object for the issuer of this certificate
2554 """
2556 return self['tbs_certificate']['issuer']
2558 @property
2559 def serial_number(self):
2560 """
2561 :return:
2562 An integer of the certificate's serial number
2563 """
2565 return self['tbs_certificate']['serial_number'].native
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 """
2575 if not self.key_identifier_value:
2576 return None
2578 return self.key_identifier_value.native
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 """
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
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
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
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 """
2617 if not self.authority_key_identifier_value:
2618 return None
2620 return self.authority_key_identifier_value['key_identifier'].native
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 """
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
2644 @property
2645 def crl_distribution_points(self):
2646 """
2647 Returns complete CRL URLs - does not include delta CRLs
2649 :return:
2650 A list of zero or more DistributionPoint objects
2651 """
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
2657 @property
2658 def delta_crl_distribution_points(self):
2659 """
2660 Returns delta CRL URLs - does not include complete CRLs
2662 :return:
2663 A list of zero or more DistributionPoint objects
2664 """
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
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
2675 :param crl_distribution_points:
2676 A CRLDistributionPoints object to grab the DistributionPoints from
2678 :return:
2679 A list of zero or more DistributionPoint objects
2680 """
2682 output = []
2684 if crl_distribution_points is None:
2685 return []
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)
2699 return output
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 """
2709 if not self.authority_information_access_value:
2710 return []
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
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 """
2731 if self._valid_domains is None:
2732 self._valid_domains = []
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)
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)
2756 return self._valid_domains
2758 @property
2759 def valid_ips(self):
2760 """
2761 :return:
2762 A list of unicode strings of valid IP addresses for the certificate
2763 """
2765 if self._valid_ips is None:
2766 self._valid_ips = []
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)
2773 return self._valid_ips
2775 @property
2776 def ca(self):
2777 """
2778 :return;
2779 A boolean - if the certificate is marked as a CA
2780 """
2782 return self.basic_constraints_value and self.basic_constraints_value['ca'].native
2784 @property
2785 def max_path_length(self):
2786 """
2787 :return;
2788 None or an integer of the maximum path length
2789 """
2791 if not self.ca:
2792 return None
2793 return self.basic_constraints_value['path_len_constraint'].native
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 """
2803 if self._self_issued is None:
2804 self._self_issued = self.subject == self.issuer
2805 return self._self_issued
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.
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 """
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
2833 @property
2834 def sha1(self):
2835 """
2836 :return:
2837 The SHA-1 hash of the DER-encoded bytes of this complete certificate
2838 """
2840 if self._sha1 is None:
2841 self._sha1 = hashlib.sha1(self.dump()).digest()
2842 return self._sha1
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 """
2852 return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))
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 """
2862 if self._sha256 is None:
2863 self._sha256 = hashlib.sha256(self.dump()).digest()
2864 return self._sha256
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 """
2874 return ' '.join('%02X' % c for c in bytes_to_list(self.sha256))
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
2881 :param domain_ip:
2882 A unicode string of a domain name or IP address
2884 :return:
2885 A boolean - if the domain or IP is valid for the certificate
2886 """
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 ))
2896 encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower()
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
2902 # Handle domain name checks
2903 if is_domain:
2904 if not self.valid_domains:
2905 return False
2907 domain_labels = encoded_domain_ip.split('.')
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('.')
2913 # The domain must be equal in label length to match
2914 if len(valid_domain_labels) != len(domain_labels):
2915 continue
2917 if valid_domain_labels == domain_labels:
2918 return True
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
2924 return False
2926 # Handle IP address checks
2927 if not self.valid_ips:
2928 return False
2930 family = socket.AF_INET if is_ipv4 else socket.AF_INET6
2931 normalized_ip = inet_pton(family, encoded_domain_ip)
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)
2937 if normalized_valid_ip == normalized_ip:
2938 return True
2940 return False
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
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
2951 :return:
2952 A boolean - if the domain is a valid wildcard domain
2953 """
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
2960 labels = domain.lower().split('.')
2962 if not labels:
2963 return False
2965 # Wildcards may only appear in the left-most label
2966 if labels[0].find('*') == -1:
2967 return False
2969 # Wildcards may not be embedded in an A-label from an IDN
2970 if labels[0][0:4] == 'xn--':
2971 return False
2973 return True
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
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
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
2988 :return:
2989 A boolean - if the domain matches the valid domain
2990 """
2992 first_domain_label = domain_labels[0]
2993 other_domain_labels = domain_labels[1:]
2995 wildcard_label = valid_domain_labels[0]
2996 other_valid_domain_labels = valid_domain_labels[1:]
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
3003 if wildcard_label == '*':
3004 return True
3006 wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$')
3007 if wildcard_regex.match(first_domain_label):
3008 return True
3010 return False
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.
3017class KeyPurposeIdentifiers(SequenceOf):
3018 _child_spec = KeyPurposeId
3021class SequenceOfAlgorithmIdentifiers(SequenceOf):
3022 _child_spec = AlgorithmIdentifier
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 ]
3035class TrustedCertificate(Concat):
3036 _child_specs = [Certificate, CertificateAux]