123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036 |
- # coding: utf-8
- """
- ASN.1 type classes for X.509 certificates. Exports the following items:
- - Attributes()
- - Certificate()
- - Extensions()
- - GeneralName()
- - GeneralNames()
- - Name()
- Other type classes are defined that help compose the types listed above.
- """
- from __future__ import unicode_literals, division, absolute_import, print_function
- from contextlib import contextmanager
- from encodings import idna # noqa
- import hashlib
- import re
- import socket
- import stringprep
- import sys
- import unicodedata
- from ._errors import unwrap
- from ._iri import iri_to_uri, uri_to_iri
- from ._ordereddict import OrderedDict
- from ._types import type_name, str_cls, bytes_to_list
- from .algos import AlgorithmIdentifier, AnyAlgorithmIdentifier, DigestAlgorithm, SignedDigestAlgorithm
- from .core import (
- Any,
- BitString,
- BMPString,
- Boolean,
- Choice,
- Concat,
- Enumerated,
- GeneralizedTime,
- GeneralString,
- IA5String,
- Integer,
- Null,
- NumericString,
- ObjectIdentifier,
- OctetBitString,
- OctetString,
- ParsableOctetString,
- PrintableString,
- Sequence,
- SequenceOf,
- Set,
- SetOf,
- TeletexString,
- UniversalString,
- UTCTime,
- UTF8String,
- VisibleString,
- VOID,
- )
- from .keys import PublicKeyInfo
- from .util import int_to_bytes, int_from_bytes, inet_ntop, inet_pton
- # The structures in this file are taken from https://tools.ietf.org/html/rfc5280
- # and a few other supplementary sources, mostly due to extra supported
- # extension and name OIDs
- class DNSName(IA5String):
- _encoding = 'idna'
- _bad_tag = (12, 19)
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.2
- :param other:
- Another DNSName object
- :return:
- A boolean
- """
- if not isinstance(other, DNSName):
- return False
- return self.__unicode__().lower() == other.__unicode__().lower()
- def set(self, value):
- """
- Sets the value of the DNS name
- :param value:
- A unicode string
- """
- if not isinstance(value, str_cls):
- raise TypeError(unwrap(
- '''
- %s value must be a unicode string, not %s
- ''',
- type_name(self),
- type_name(value)
- ))
- if value.startswith('.'):
- encoded_value = b'.' + value[1:].encode(self._encoding)
- else:
- encoded_value = value.encode(self._encoding)
- self._unicode = value
- self.contents = encoded_value
- self._header = None
- if self._trailer != b'':
- self._trailer = b''
- class URI(IA5String):
- def set(self, value):
- """
- Sets the value of the string
- :param value:
- A unicode string
- """
- if not isinstance(value, str_cls):
- raise TypeError(unwrap(
- '''
- %s value must be a unicode string, not %s
- ''',
- type_name(self),
- type_name(value)
- ))
- self._unicode = value
- self.contents = iri_to_uri(value)
- self._header = None
- if self._trailer != b'':
- self._trailer = b''
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.4
- :param other:
- Another URI object
- :return:
- A boolean
- """
- if not isinstance(other, URI):
- return False
- return iri_to_uri(self.native, True) == iri_to_uri(other.native, True)
- def __unicode__(self):
- """
- :return:
- A unicode string
- """
- if self.contents is None:
- return ''
- if self._unicode is None:
- self._unicode = uri_to_iri(self._merge_chunks())
- return self._unicode
- class EmailAddress(IA5String):
- _contents = None
- # If the value has gone through the .set() method, thus normalizing it
- _normalized = False
- # In the wild we've seen this encoded as a UTF8String and PrintableString
- _bad_tag = (12, 19)
- @property
- def contents(self):
- """
- :return:
- A byte string of the DER-encoded contents of the sequence
- """
- return self._contents
- @contents.setter
- def contents(self, value):
- """
- :param value:
- A byte string of the DER-encoded contents of the sequence
- """
- self._normalized = False
- self._contents = value
- def set(self, value):
- """
- Sets the value of the string
- :param value:
- A unicode string
- """
- if not isinstance(value, str_cls):
- raise TypeError(unwrap(
- '''
- %s value must be a unicode string, not %s
- ''',
- type_name(self),
- type_name(value)
- ))
- if value.find('@') != -1:
- mailbox, hostname = value.rsplit('@', 1)
- encoded_value = mailbox.encode('ascii') + b'@' + hostname.encode('idna')
- else:
- encoded_value = value.encode('ascii')
- self._normalized = True
- self._unicode = value
- self.contents = encoded_value
- self._header = None
- if self._trailer != b'':
- self._trailer = b''
- def __unicode__(self):
- """
- :return:
- A unicode string
- """
- # We've seen this in the wild as a PrintableString, and since ascii is a
- # subset of cp1252, we use the later for decoding to be more user friendly
- if self._unicode is None:
- contents = self._merge_chunks()
- if contents.find(b'@') == -1:
- self._unicode = contents.decode('cp1252')
- else:
- mailbox, hostname = contents.rsplit(b'@', 1)
- self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
- return self._unicode
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.5
- :param other:
- Another EmailAddress object
- :return:
- A boolean
- """
- if not isinstance(other, EmailAddress):
- return False
- if not self._normalized:
- self.set(self.native)
- if not other._normalized:
- other.set(other.native)
- if self._contents.find(b'@') == -1 or other._contents.find(b'@') == -1:
- return self._contents == other._contents
- other_mailbox, other_hostname = other._contents.rsplit(b'@', 1)
- mailbox, hostname = self._contents.rsplit(b'@', 1)
- if mailbox != other_mailbox:
- return False
- if hostname.lower() != other_hostname.lower():
- return False
- return True
- class IPAddress(OctetString):
- def parse(self, spec=None, spec_params=None):
- """
- This method is not applicable to IP addresses
- """
- raise ValueError(unwrap(
- '''
- IP address values can not be parsed
- '''
- ))
- def set(self, value):
- """
- Sets the value of the object
- :param value:
- A unicode string containing an IPv4 address, IPv4 address with CIDR,
- an IPv6 address or IPv6 address with CIDR
- """
- if not isinstance(value, str_cls):
- raise TypeError(unwrap(
- '''
- %s value must be a unicode string, not %s
- ''',
- type_name(self),
- type_name(value)
- ))
- original_value = value
- has_cidr = value.find('/') != -1
- cidr = 0
- if has_cidr:
- parts = value.split('/', 1)
- value = parts[0]
- cidr = int(parts[1])
- if cidr < 0:
- raise ValueError(unwrap(
- '''
- %s value contains a CIDR range less than 0
- ''',
- type_name(self)
- ))
- if value.find(':') != -1:
- family = socket.AF_INET6
- if cidr > 128:
- raise ValueError(unwrap(
- '''
- %s value contains a CIDR range bigger than 128, the maximum
- value for an IPv6 address
- ''',
- type_name(self)
- ))
- cidr_size = 128
- else:
- family = socket.AF_INET
- if cidr > 32:
- raise ValueError(unwrap(
- '''
- %s value contains a CIDR range bigger than 32, the maximum
- value for an IPv4 address
- ''',
- type_name(self)
- ))
- cidr_size = 32
- cidr_bytes = b''
- if has_cidr:
- cidr_mask = '1' * cidr
- cidr_mask += '0' * (cidr_size - len(cidr_mask))
- cidr_bytes = int_to_bytes(int(cidr_mask, 2))
- cidr_bytes = (b'\x00' * ((cidr_size // 8) - len(cidr_bytes))) + cidr_bytes
- self._native = original_value
- self.contents = inet_pton(family, value) + cidr_bytes
- self._bytes = self.contents
- self._header = None
- if self._trailer != b'':
- self._trailer = b''
- @property
- def native(self):
- """
- The native Python datatype representation of this value
- :return:
- A unicode string or None
- """
- if self.contents is None:
- return None
- if self._native is None:
- byte_string = self.__bytes__()
- byte_len = len(byte_string)
- value = None
- cidr_int = None
- if byte_len in set([32, 16]):
- value = inet_ntop(socket.AF_INET6, byte_string[0:16])
- if byte_len > 16:
- cidr_int = int_from_bytes(byte_string[16:])
- elif byte_len in set([8, 4]):
- value = inet_ntop(socket.AF_INET, byte_string[0:4])
- if byte_len > 4:
- cidr_int = int_from_bytes(byte_string[4:])
- if cidr_int is not None:
- cidr_bits = '{0:b}'.format(cidr_int)
- cidr = len(cidr_bits.rstrip('0'))
- value = value + '/' + str_cls(cidr)
- self._native = value
- return self._native
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- :param other:
- Another IPAddress object
- :return:
- A boolean
- """
- if not isinstance(other, IPAddress):
- return False
- return self.__bytes__() == other.__bytes__()
- class Attribute(Sequence):
- _fields = [
- ('type', ObjectIdentifier),
- ('values', SetOf, {'spec': Any}),
- ]
- class Attributes(SequenceOf):
- _child_spec = Attribute
- class KeyUsage(BitString):
- _map = {
- 0: 'digital_signature',
- 1: 'non_repudiation',
- 2: 'key_encipherment',
- 3: 'data_encipherment',
- 4: 'key_agreement',
- 5: 'key_cert_sign',
- 6: 'crl_sign',
- 7: 'encipher_only',
- 8: 'decipher_only',
- }
- class PrivateKeyUsagePeriod(Sequence):
- _fields = [
- ('not_before', GeneralizedTime, {'implicit': 0, 'optional': True}),
- ('not_after', GeneralizedTime, {'implicit': 1, 'optional': True}),
- ]
- class NotReallyTeletexString(TeletexString):
- """
- OpenSSL (and probably some other libraries) puts ISO-8859-1
- into TeletexString instead of ITU T.61. We use Windows-1252 when
- decoding since it is a superset of ISO-8859-1, and less likely to
- cause encoding issues, but we stay strict with encoding to prevent
- us from creating bad data.
- """
- _decoding_encoding = 'cp1252'
- def __unicode__(self):
- """
- :return:
- A unicode string
- """
- if self.contents is None:
- return ''
- if self._unicode is None:
- self._unicode = self._merge_chunks().decode(self._decoding_encoding)
- return self._unicode
- @contextmanager
- def strict_teletex():
- try:
- NotReallyTeletexString._decoding_encoding = 'teletex'
- yield
- finally:
- NotReallyTeletexString._decoding_encoding = 'cp1252'
- class DirectoryString(Choice):
- _alternatives = [
- ('teletex_string', NotReallyTeletexString),
- ('printable_string', PrintableString),
- ('universal_string', UniversalString),
- ('utf8_string', UTF8String),
- ('bmp_string', BMPString),
- # This is an invalid/bad alternative, but some broken certs use it
- ('ia5_string', IA5String),
- ]
- class NameType(ObjectIdentifier):
- _map = {
- '2.5.4.3': 'common_name',
- '2.5.4.4': 'surname',
- '2.5.4.5': 'serial_number',
- '2.5.4.6': 'country_name',
- '2.5.4.7': 'locality_name',
- '2.5.4.8': 'state_or_province_name',
- '2.5.4.9': 'street_address',
- '2.5.4.10': 'organization_name',
- '2.5.4.11': 'organizational_unit_name',
- '2.5.4.12': 'title',
- '2.5.4.15': 'business_category',
- '2.5.4.17': 'postal_code',
- '2.5.4.20': 'telephone_number',
- '2.5.4.41': 'name',
- '2.5.4.42': 'given_name',
- '2.5.4.43': 'initials',
- '2.5.4.44': 'generation_qualifier',
- '2.5.4.45': 'unique_identifier',
- '2.5.4.46': 'dn_qualifier',
- '2.5.4.65': 'pseudonym',
- '2.5.4.97': 'organization_identifier',
- # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
- '2.23.133.2.1': 'tpm_manufacturer',
- '2.23.133.2.2': 'tpm_model',
- '2.23.133.2.3': 'tpm_version',
- '2.23.133.2.4': 'platform_manufacturer',
- '2.23.133.2.5': 'platform_model',
- '2.23.133.2.6': 'platform_version',
- # https://tools.ietf.org/html/rfc2985#page-26
- '1.2.840.113549.1.9.1': 'email_address',
- # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
- '1.3.6.1.4.1.311.60.2.1.1': 'incorporation_locality',
- '1.3.6.1.4.1.311.60.2.1.2': 'incorporation_state_or_province',
- '1.3.6.1.4.1.311.60.2.1.3': 'incorporation_country',
- # https://tools.ietf.org/html/rfc4519#section-2.39
- '0.9.2342.19200300.100.1.1': 'user_id',
- # https://tools.ietf.org/html/rfc2247#section-4
- '0.9.2342.19200300.100.1.25': 'domain_component',
- # http://www.alvestrand.no/objectid/0.2.262.1.10.7.20.html
- '0.2.262.1.10.7.20': 'name_distinguisher',
- }
- # This order is largely based on observed order seen in EV certs from
- # Symantec and DigiCert. Some of the uncommon name-related fields are
- # just placed in what seems like a reasonable order.
- preferred_order = [
- 'incorporation_country',
- 'incorporation_state_or_province',
- 'incorporation_locality',
- 'business_category',
- 'serial_number',
- 'country_name',
- 'postal_code',
- 'state_or_province_name',
- 'locality_name',
- 'street_address',
- 'organization_name',
- 'organizational_unit_name',
- 'title',
- 'common_name',
- 'user_id',
- 'initials',
- 'generation_qualifier',
- 'surname',
- 'given_name',
- 'name',
- 'pseudonym',
- 'dn_qualifier',
- 'telephone_number',
- 'email_address',
- 'domain_component',
- 'name_distinguisher',
- 'organization_identifier',
- 'tpm_manufacturer',
- 'tpm_model',
- 'tpm_version',
- 'platform_manufacturer',
- 'platform_model',
- 'platform_version',
- ]
- @classmethod
- def preferred_ordinal(cls, attr_name):
- """
- Returns an ordering value for a particular attribute key.
- Unrecognized attributes and OIDs will be sorted lexically at the end.
- :return:
- An orderable value.
- """
- attr_name = cls.map(attr_name)
- if attr_name in cls.preferred_order:
- ordinal = cls.preferred_order.index(attr_name)
- else:
- ordinal = len(cls.preferred_order)
- return (ordinal, attr_name)
- @property
- def human_friendly(self):
- """
- :return:
- A human-friendly unicode string to display to users
- """
- return {
- 'common_name': 'Common Name',
- 'surname': 'Surname',
- 'serial_number': 'Serial Number',
- 'country_name': 'Country',
- 'locality_name': 'Locality',
- 'state_or_province_name': 'State/Province',
- 'street_address': 'Street Address',
- 'organization_name': 'Organization',
- 'organizational_unit_name': 'Organizational Unit',
- 'title': 'Title',
- 'business_category': 'Business Category',
- 'postal_code': 'Postal Code',
- 'telephone_number': 'Telephone Number',
- 'name': 'Name',
- 'given_name': 'Given Name',
- 'initials': 'Initials',
- 'generation_qualifier': 'Generation Qualifier',
- 'unique_identifier': 'Unique Identifier',
- 'dn_qualifier': 'DN Qualifier',
- 'pseudonym': 'Pseudonym',
- 'email_address': 'Email Address',
- 'incorporation_locality': 'Incorporation Locality',
- 'incorporation_state_or_province': 'Incorporation State/Province',
- 'incorporation_country': 'Incorporation Country',
- 'domain_component': 'Domain Component',
- 'name_distinguisher': 'Name Distinguisher',
- 'organization_identifier': 'Organization Identifier',
- 'tpm_manufacturer': 'TPM Manufacturer',
- 'tpm_model': 'TPM Model',
- 'tpm_version': 'TPM Version',
- 'platform_manufacturer': 'Platform Manufacturer',
- 'platform_model': 'Platform Model',
- 'platform_version': 'Platform Version',
- 'user_id': 'User ID',
- }.get(self.native, self.native)
- class NameTypeAndValue(Sequence):
- _fields = [
- ('type', NameType),
- ('value', Any),
- ]
- _oid_pair = ('type', 'value')
- _oid_specs = {
- 'common_name': DirectoryString,
- 'surname': DirectoryString,
- 'serial_number': DirectoryString,
- 'country_name': DirectoryString,
- 'locality_name': DirectoryString,
- 'state_or_province_name': DirectoryString,
- 'street_address': DirectoryString,
- 'organization_name': DirectoryString,
- 'organizational_unit_name': DirectoryString,
- 'title': DirectoryString,
- 'business_category': DirectoryString,
- 'postal_code': DirectoryString,
- 'telephone_number': PrintableString,
- 'name': DirectoryString,
- 'given_name': DirectoryString,
- 'initials': DirectoryString,
- 'generation_qualifier': DirectoryString,
- 'unique_identifier': OctetBitString,
- 'dn_qualifier': DirectoryString,
- 'pseudonym': DirectoryString,
- # https://tools.ietf.org/html/rfc2985#page-26
- 'email_address': EmailAddress,
- # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
- 'incorporation_locality': DirectoryString,
- 'incorporation_state_or_province': DirectoryString,
- 'incorporation_country': DirectoryString,
- 'domain_component': DNSName,
- 'name_distinguisher': DirectoryString,
- 'organization_identifier': DirectoryString,
- 'tpm_manufacturer': UTF8String,
- 'tpm_model': UTF8String,
- 'tpm_version': UTF8String,
- 'platform_manufacturer': UTF8String,
- 'platform_model': UTF8String,
- 'platform_version': UTF8String,
- 'user_id': DirectoryString,
- }
- _prepped = None
- @property
- def prepped_value(self):
- """
- Returns the value after being processed by the internationalized string
- preparation as specified by RFC 5280
- :return:
- A unicode string
- """
- if self._prepped is None:
- self._prepped = self._ldap_string_prep(self['value'].native)
- return self._prepped
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
- :param other:
- Another NameTypeAndValue object
- :return:
- A boolean
- """
- if not isinstance(other, NameTypeAndValue):
- return False
- if other['type'].native != self['type'].native:
- return False
- return other.prepped_value == self.prepped_value
- def _ldap_string_prep(self, string):
- """
- Implements the internationalized string preparation algorithm from
- RFC 4518. https://tools.ietf.org/html/rfc4518#section-2
- :param string:
- A unicode string to prepare
- :return:
- A prepared unicode string, ready for comparison
- """
- # Map step
- string = re.sub('[\u00ad\u1806\u034f\u180b-\u180d\ufe0f-\uff00\ufffc]+', '', string)
- string = re.sub('[\u0009\u000a\u000b\u000c\u000d\u0085]', ' ', string)
- if sys.maxunicode == 0xffff:
- # Some installs of Python 2.7 don't support 8-digit unicode escape
- # ranges, so we have to break them into pieces
- # Original was: \U0001D173-\U0001D17A and \U000E0020-\U000E007F
- string = re.sub('\ud834[\udd73-\udd7a]|\udb40[\udc20-\udc7f]|\U000e0001', '', string)
- else:
- string = re.sub('[\U0001D173-\U0001D17A\U000E0020-\U000E007F\U000e0001]', '', string)
- string = re.sub(
- '[\u0000-\u0008\u000e-\u001f\u007f-\u0084\u0086-\u009f\u06dd\u070f\u180e\u200c-\u200f'
- '\u202a-\u202e\u2060-\u2063\u206a-\u206f\ufeff\ufff9-\ufffb]+',
- '',
- string
- )
- string = string.replace('\u200b', '')
- string = re.sub('[\u00a0\u1680\u2000-\u200a\u2028-\u2029\u202f\u205f\u3000]', ' ', string)
- string = ''.join(map(stringprep.map_table_b2, string))
- # Normalize step
- string = unicodedata.normalize('NFKC', string)
- # Prohibit step
- for char in string:
- if stringprep.in_table_a1(char):
- raise ValueError(unwrap(
- '''
- X.509 Name objects may not contain unassigned code points
- '''
- ))
- if stringprep.in_table_c8(char):
- raise ValueError(unwrap(
- '''
- X.509 Name objects may not contain change display or
- zzzzdeprecated characters
- '''
- ))
- if stringprep.in_table_c3(char):
- raise ValueError(unwrap(
- '''
- X.509 Name objects may not contain private use characters
- '''
- ))
- if stringprep.in_table_c4(char):
- raise ValueError(unwrap(
- '''
- X.509 Name objects may not contain non-character code points
- '''
- ))
- if stringprep.in_table_c5(char):
- raise ValueError(unwrap(
- '''
- X.509 Name objects may not contain surrogate code points
- '''
- ))
- if char == '\ufffd':
- raise ValueError(unwrap(
- '''
- X.509 Name objects may not contain the replacement character
- '''
- ))
- # Check bidirectional step - here we ensure that we are not mixing
- # left-to-right and right-to-left text in the string
- has_r_and_al_cat = False
- has_l_cat = False
- for char in string:
- if stringprep.in_table_d1(char):
- has_r_and_al_cat = True
- elif stringprep.in_table_d2(char):
- has_l_cat = True
- if has_r_and_al_cat:
- first_is_r_and_al = stringprep.in_table_d1(string[0])
- last_is_r_and_al = stringprep.in_table_d1(string[-1])
- if has_l_cat or not first_is_r_and_al or not last_is_r_and_al:
- raise ValueError(unwrap(
- '''
- X.509 Name object contains a malformed bidirectional
- sequence
- '''
- ))
- # Insignificant space handling step
- string = ' ' + re.sub(' +', ' ', string).strip() + ' '
- return string
- class RelativeDistinguishedName(SetOf):
- _child_spec = NameTypeAndValue
- @property
- def hashable(self):
- """
- :return:
- A unicode string that can be used as a dict key or in a set
- """
- output = []
- values = self._get_values(self)
- for key in sorted(values.keys()):
- output.append('%s: %s' % (key, values[key]))
- # Unit separator is used here since the normalization process for
- # values moves any such character, and the keys are all dotted integers
- # or under_score_words
- return '\x1F'.join(output)
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
- :param other:
- Another RelativeDistinguishedName object
- :return:
- A boolean
- """
- if not isinstance(other, RelativeDistinguishedName):
- return False
- if len(self) != len(other):
- return False
- self_types = self._get_types(self)
- other_types = self._get_types(other)
- if self_types != other_types:
- return False
- self_values = self._get_values(self)
- other_values = self._get_values(other)
- for type_name_ in self_types:
- if self_values[type_name_] != other_values[type_name_]:
- return False
- return True
- def _get_types(self, rdn):
- """
- Returns a set of types contained in an RDN
- :param rdn:
- A RelativeDistinguishedName object
- :return:
- A set object with unicode strings of NameTypeAndValue type field
- values
- """
- return set([ntv['type'].native for ntv in rdn])
- def _get_values(self, rdn):
- """
- Returns a dict of prepped values contained in an RDN
- :param rdn:
- A RelativeDistinguishedName object
- :return:
- A dict object with unicode strings of NameTypeAndValue value field
- values that have been prepped for comparison
- """
- output = {}
- [output.update([(ntv['type'].native, ntv.prepped_value)]) for ntv in rdn]
- return output
- class RDNSequence(SequenceOf):
- _child_spec = RelativeDistinguishedName
- @property
- def hashable(self):
- """
- :return:
- A unicode string that can be used as a dict key or in a set
- """
- # Record separator is used here since the normalization process for
- # values moves any such character, and the keys are all dotted integers
- # or under_score_words
- return '\x1E'.join(rdn.hashable for rdn in self)
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
- :param other:
- Another RDNSequence object
- :return:
- A boolean
- """
- if not isinstance(other, RDNSequence):
- return False
- if len(self) != len(other):
- return False
- for index, self_rdn in enumerate(self):
- if other[index] != self_rdn:
- return False
- return True
- class Name(Choice):
- _alternatives = [
- ('', RDNSequence),
- ]
- _human_friendly = None
- _sha1 = None
- _sha256 = None
- @classmethod
- def build(cls, name_dict, use_printable=False):
- """
- Creates a Name object from a dict of unicode string keys and values.
- The keys should be from NameType._map, or a dotted-integer OID unicode
- string.
- :param name_dict:
- A dict of name information, e.g. {"common_name": "Will Bond",
- "country_name": "US", "organization_name": "Codex Non Sufficit LC"}
- :param use_printable:
- A bool - if PrintableString should be used for encoding instead of
- UTF8String. This is for backwards compatibility with old software.
- :return:
- An x509.Name object
- """
- rdns = []
- if not use_printable:
- encoding_name = 'utf8_string'
- encoding_class = UTF8String
- else:
- encoding_name = 'printable_string'
- encoding_class = PrintableString
- # Sort the attributes according to NameType.preferred_order
- name_dict = OrderedDict(
- sorted(
- name_dict.items(),
- key=lambda item: NameType.preferred_ordinal(item[0])
- )
- )
- for attribute_name, attribute_value in name_dict.items():
- attribute_name = NameType.map(attribute_name)
- if attribute_name == 'email_address':
- value = EmailAddress(attribute_value)
- elif attribute_name == 'domain_component':
- value = DNSName(attribute_value)
- elif attribute_name in set(['dn_qualifier', 'country_name', 'serial_number']):
- value = DirectoryString(
- name='printable_string',
- value=PrintableString(attribute_value)
- )
- else:
- value = DirectoryString(
- name=encoding_name,
- value=encoding_class(attribute_value)
- )
- rdns.append(RelativeDistinguishedName([
- NameTypeAndValue({
- 'type': attribute_name,
- 'value': value
- })
- ]))
- return cls(name='', value=RDNSequence(rdns))
- @property
- def hashable(self):
- """
- :return:
- A unicode string that can be used as a dict key or in a set
- """
- return self.chosen.hashable
- def __len__(self):
- return len(self.chosen)
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
- :param other:
- Another Name object
- :return:
- A boolean
- """
- if not isinstance(other, Name):
- return False
- return self.chosen == other.chosen
- @property
- def native(self):
- if self._native is None:
- self._native = OrderedDict()
- for rdn in self.chosen.native:
- for type_val in rdn:
- field_name = type_val['type']
- if field_name in self._native:
- existing = self._native[field_name]
- if not isinstance(existing, list):
- existing = self._native[field_name] = [existing]
- existing.append(type_val['value'])
- else:
- self._native[field_name] = type_val['value']
- return self._native
- @property
- def human_friendly(self):
- """
- :return:
- A human-friendly unicode string containing the parts of the name
- """
- if self._human_friendly is None:
- data = OrderedDict()
- last_field = None
- for rdn in self.chosen:
- for type_val in rdn:
- field_name = type_val['type'].human_friendly
- last_field = field_name
- if field_name in data:
- data[field_name] = [data[field_name]]
- data[field_name].append(type_val['value'])
- else:
- data[field_name] = type_val['value']
- to_join = []
- keys = data.keys()
- if last_field == 'Country':
- keys = reversed(list(keys))
- for key in keys:
- value = data[key]
- native_value = self._recursive_humanize(value)
- to_join.append('%s: %s' % (key, native_value))
- has_comma = False
- for element in to_join:
- if element.find(',') != -1:
- has_comma = True
- break
- separator = ', ' if not has_comma else '; '
- self._human_friendly = separator.join(to_join[::-1])
- return self._human_friendly
- def _recursive_humanize(self, value):
- """
- Recursively serializes data compiled from the RDNSequence
- :param value:
- An Asn1Value object, or a list of Asn1Value objects
- :return:
- A unicode string
- """
- if isinstance(value, list):
- return ', '.join(
- reversed([self._recursive_humanize(sub_value) for sub_value in value])
- )
- return value.native
- @property
- def sha1(self):
- """
- :return:
- The SHA1 hash of the DER-encoded bytes of this name
- """
- if self._sha1 is None:
- self._sha1 = hashlib.sha1(self.dump()).digest()
- return self._sha1
- @property
- def sha256(self):
- """
- :return:
- The SHA-256 hash of the DER-encoded bytes of this name
- """
- if self._sha256 is None:
- self._sha256 = hashlib.sha256(self.dump()).digest()
- return self._sha256
- class AnotherName(Sequence):
- _fields = [
- ('type_id', ObjectIdentifier),
- ('value', Any, {'explicit': 0}),
- ]
- class CountryName(Choice):
- class_ = 1
- tag = 1
- _alternatives = [
- ('x121_dcc_code', NumericString),
- ('iso_3166_alpha2_code', PrintableString),
- ]
- class AdministrationDomainName(Choice):
- class_ = 1
- tag = 2
- _alternatives = [
- ('numeric', NumericString),
- ('printable', PrintableString),
- ]
- class PrivateDomainName(Choice):
- _alternatives = [
- ('numeric', NumericString),
- ('printable', PrintableString),
- ]
- class PersonalName(Set):
- _fields = [
- ('surname', PrintableString, {'implicit': 0}),
- ('given_name', PrintableString, {'implicit': 1, 'optional': True}),
- ('initials', PrintableString, {'implicit': 2, 'optional': True}),
- ('generation_qualifier', PrintableString, {'implicit': 3, 'optional': True}),
- ]
- class TeletexPersonalName(Set):
- _fields = [
- ('surname', TeletexString, {'implicit': 0}),
- ('given_name', TeletexString, {'implicit': 1, 'optional': True}),
- ('initials', TeletexString, {'implicit': 2, 'optional': True}),
- ('generation_qualifier', TeletexString, {'implicit': 3, 'optional': True}),
- ]
- class OrganizationalUnitNames(SequenceOf):
- _child_spec = PrintableString
- class TeletexOrganizationalUnitNames(SequenceOf):
- _child_spec = TeletexString
- class BuiltInStandardAttributes(Sequence):
- _fields = [
- ('country_name', CountryName, {'optional': True}),
- ('administration_domain_name', AdministrationDomainName, {'optional': True}),
- ('network_address', NumericString, {'implicit': 0, 'optional': True}),
- ('terminal_identifier', PrintableString, {'implicit': 1, 'optional': True}),
- ('private_domain_name', PrivateDomainName, {'explicit': 2, 'optional': True}),
- ('organization_name', PrintableString, {'implicit': 3, 'optional': True}),
- ('numeric_user_identifier', NumericString, {'implicit': 4, 'optional': True}),
- ('personal_name', PersonalName, {'implicit': 5, 'optional': True}),
- ('organizational_unit_names', OrganizationalUnitNames, {'implicit': 6, 'optional': True}),
- ]
- class BuiltInDomainDefinedAttribute(Sequence):
- _fields = [
- ('type', PrintableString),
- ('value', PrintableString),
- ]
- class BuiltInDomainDefinedAttributes(SequenceOf):
- _child_spec = BuiltInDomainDefinedAttribute
- class TeletexDomainDefinedAttribute(Sequence):
- _fields = [
- ('type', TeletexString),
- ('value', TeletexString),
- ]
- class TeletexDomainDefinedAttributes(SequenceOf):
- _child_spec = TeletexDomainDefinedAttribute
- class PhysicalDeliveryCountryName(Choice):
- _alternatives = [
- ('x121_dcc_code', NumericString),
- ('iso_3166_alpha2_code', PrintableString),
- ]
- class PostalCode(Choice):
- _alternatives = [
- ('numeric_code', NumericString),
- ('printable_code', PrintableString),
- ]
- class PDSParameter(Set):
- _fields = [
- ('printable_string', PrintableString, {'optional': True}),
- ('teletex_string', TeletexString, {'optional': True}),
- ]
- class PrintableAddress(SequenceOf):
- _child_spec = PrintableString
- class UnformattedPostalAddress(Set):
- _fields = [
- ('printable_address', PrintableAddress, {'optional': True}),
- ('teletex_string', TeletexString, {'optional': True}),
- ]
- class E1634Address(Sequence):
- _fields = [
- ('number', NumericString, {'implicit': 0}),
- ('sub_address', NumericString, {'implicit': 1, 'optional': True}),
- ]
- class NAddresses(SetOf):
- _child_spec = OctetString
- class PresentationAddress(Sequence):
- _fields = [
- ('p_selector', OctetString, {'explicit': 0, 'optional': True}),
- ('s_selector', OctetString, {'explicit': 1, 'optional': True}),
- ('t_selector', OctetString, {'explicit': 2, 'optional': True}),
- ('n_addresses', NAddresses, {'explicit': 3}),
- ]
- class ExtendedNetworkAddress(Choice):
- _alternatives = [
- ('e163_4_address', E1634Address),
- ('psap_address', PresentationAddress, {'implicit': 0})
- ]
- class TerminalType(Integer):
- _map = {
- 3: 'telex',
- 4: 'teletex',
- 5: 'g3_facsimile',
- 6: 'g4_facsimile',
- 7: 'ia5_terminal',
- 8: 'videotex',
- }
- class ExtensionAttributeType(Integer):
- _map = {
- 1: 'common_name',
- 2: 'teletex_common_name',
- 3: 'teletex_organization_name',
- 4: 'teletex_personal_name',
- 5: 'teletex_organization_unit_names',
- 6: 'teletex_domain_defined_attributes',
- 7: 'pds_name',
- 8: 'physical_delivery_country_name',
- 9: 'postal_code',
- 10: 'physical_delivery_office_name',
- 11: 'physical_delivery_office_number',
- 12: 'extension_of_address_components',
- 13: 'physical_delivery_personal_name',
- 14: 'physical_delivery_organization_name',
- 15: 'extension_physical_delivery_address_components',
- 16: 'unformatted_postal_address',
- 17: 'street_address',
- 18: 'post_office_box_address',
- 19: 'poste_restante_address',
- 20: 'unique_postal_name',
- 21: 'local_postal_attributes',
- 22: 'extended_network_address',
- 23: 'terminal_type',
- }
- class ExtensionAttribute(Sequence):
- _fields = [
- ('extension_attribute_type', ExtensionAttributeType, {'implicit': 0}),
- ('extension_attribute_value', Any, {'explicit': 1}),
- ]
- _oid_pair = ('extension_attribute_type', 'extension_attribute_value')
- _oid_specs = {
- 'common_name': PrintableString,
- 'teletex_common_name': TeletexString,
- 'teletex_organization_name': TeletexString,
- 'teletex_personal_name': TeletexPersonalName,
- 'teletex_organization_unit_names': TeletexOrganizationalUnitNames,
- 'teletex_domain_defined_attributes': TeletexDomainDefinedAttributes,
- 'pds_name': PrintableString,
- 'physical_delivery_country_name': PhysicalDeliveryCountryName,
- 'postal_code': PostalCode,
- 'physical_delivery_office_name': PDSParameter,
- 'physical_delivery_office_number': PDSParameter,
- 'extension_of_address_components': PDSParameter,
- 'physical_delivery_personal_name': PDSParameter,
- 'physical_delivery_organization_name': PDSParameter,
- 'extension_physical_delivery_address_components': PDSParameter,
- 'unformatted_postal_address': UnformattedPostalAddress,
- 'street_address': PDSParameter,
- 'post_office_box_address': PDSParameter,
- 'poste_restante_address': PDSParameter,
- 'unique_postal_name': PDSParameter,
- 'local_postal_attributes': PDSParameter,
- 'extended_network_address': ExtendedNetworkAddress,
- 'terminal_type': TerminalType,
- }
- class ExtensionAttributes(SequenceOf):
- _child_spec = ExtensionAttribute
- class ORAddress(Sequence):
- _fields = [
- ('built_in_standard_attributes', BuiltInStandardAttributes),
- ('built_in_domain_defined_attributes', BuiltInDomainDefinedAttributes, {'optional': True}),
- ('extension_attributes', ExtensionAttributes, {'optional': True}),
- ]
- class EDIPartyName(Sequence):
- _fields = [
- ('name_assigner', DirectoryString, {'implicit': 0, 'optional': True}),
- ('party_name', DirectoryString, {'implicit': 1}),
- ]
- class GeneralName(Choice):
- _alternatives = [
- ('other_name', AnotherName, {'implicit': 0}),
- ('rfc822_name', EmailAddress, {'implicit': 1}),
- ('dns_name', DNSName, {'implicit': 2}),
- ('x400_address', ORAddress, {'implicit': 3}),
- ('directory_name', Name, {'explicit': 4}),
- ('edi_party_name', EDIPartyName, {'implicit': 5}),
- ('uniform_resource_identifier', URI, {'implicit': 6}),
- ('ip_address', IPAddress, {'implicit': 7}),
- ('registered_id', ObjectIdentifier, {'implicit': 8}),
- ]
- def __ne__(self, other):
- return not self == other
- def __eq__(self, other):
- """
- Does not support other_name, x400_address or edi_party_name
- :param other:
- The other GeneralName to compare to
- :return:
- A boolean
- """
- if self.name in ('other_name', 'x400_address', 'edi_party_name'):
- raise ValueError(unwrap(
- '''
- Comparison is not supported for GeneralName objects of
- choice %s
- ''',
- self.name
- ))
- if other.name in ('other_name', 'x400_address', 'edi_party_name'):
- raise ValueError(unwrap(
- '''
- Comparison is not supported for GeneralName objects of choice
- %s''',
- other.name
- ))
- if self.name != other.name:
- return False
- return self.chosen == other.chosen
- class GeneralNames(SequenceOf):
- _child_spec = GeneralName
- class Time(Choice):
- _alternatives = [
- ('utc_time', UTCTime),
- ('general_time', GeneralizedTime),
- ]
- class Validity(Sequence):
- _fields = [
- ('not_before', Time),
- ('not_after', Time),
- ]
- class BasicConstraints(Sequence):
- _fields = [
- ('ca', Boolean, {'default': False}),
- ('path_len_constraint', Integer, {'optional': True}),
- ]
- class AuthorityKeyIdentifier(Sequence):
- _fields = [
- ('key_identifier', OctetString, {'implicit': 0, 'optional': True}),
- ('authority_cert_issuer', GeneralNames, {'implicit': 1, 'optional': True}),
- ('authority_cert_serial_number', Integer, {'implicit': 2, 'optional': True}),
- ]
- class DistributionPointName(Choice):
- _alternatives = [
- ('full_name', GeneralNames, {'implicit': 0}),
- ('name_relative_to_crl_issuer', RelativeDistinguishedName, {'implicit': 1}),
- ]
- class ReasonFlags(BitString):
- _map = {
- 0: 'unused',
- 1: 'key_compromise',
- 2: 'ca_compromise',
- 3: 'affiliation_changed',
- 4: 'superseded',
- 5: 'cessation_of_operation',
- 6: 'certificate_hold',
- 7: 'privilege_withdrawn',
- 8: 'aa_compromise',
- }
- class GeneralSubtree(Sequence):
- _fields = [
- ('base', GeneralName),
- ('minimum', Integer, {'implicit': 0, 'default': 0}),
- ('maximum', Integer, {'implicit': 1, 'optional': True}),
- ]
- class GeneralSubtrees(SequenceOf):
- _child_spec = GeneralSubtree
- class NameConstraints(Sequence):
- _fields = [
- ('permitted_subtrees', GeneralSubtrees, {'implicit': 0, 'optional': True}),
- ('excluded_subtrees', GeneralSubtrees, {'implicit': 1, 'optional': True}),
- ]
- class DistributionPoint(Sequence):
- _fields = [
- ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}),
- ('reasons', ReasonFlags, {'implicit': 1, 'optional': True}),
- ('crl_issuer', GeneralNames, {'implicit': 2, 'optional': True}),
- ]
- _url = False
- @property
- def url(self):
- """
- :return:
- None or a unicode string of the distribution point's URL
- """
- if self._url is False:
- self._url = None
- name = self['distribution_point']
- if name.name != 'full_name':
- raise ValueError(unwrap(
- '''
- CRL distribution points that are relative to the issuer are
- not supported
- '''
- ))
- for general_name in name.chosen:
- if general_name.name == 'uniform_resource_identifier':
- url = general_name.native
- if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
- self._url = url
- break
- return self._url
- class CRLDistributionPoints(SequenceOf):
- _child_spec = DistributionPoint
- class DisplayText(Choice):
- _alternatives = [
- ('ia5_string', IA5String),
- ('visible_string', VisibleString),
- ('bmp_string', BMPString),
- ('utf8_string', UTF8String),
- ]
- class NoticeNumbers(SequenceOf):
- _child_spec = Integer
- class NoticeReference(Sequence):
- _fields = [
- ('organization', DisplayText),
- ('notice_numbers', NoticeNumbers),
- ]
- class UserNotice(Sequence):
- _fields = [
- ('notice_ref', NoticeReference, {'optional': True}),
- ('explicit_text', DisplayText, {'optional': True}),
- ]
- class PolicyQualifierId(ObjectIdentifier):
- _map = {
- '1.3.6.1.5.5.7.2.1': 'certification_practice_statement',
- '1.3.6.1.5.5.7.2.2': 'user_notice',
- }
- class PolicyQualifierInfo(Sequence):
- _fields = [
- ('policy_qualifier_id', PolicyQualifierId),
- ('qualifier', Any),
- ]
- _oid_pair = ('policy_qualifier_id', 'qualifier')
- _oid_specs = {
- 'certification_practice_statement': IA5String,
- 'user_notice': UserNotice,
- }
- class PolicyQualifierInfos(SequenceOf):
- _child_spec = PolicyQualifierInfo
- class PolicyIdentifier(ObjectIdentifier):
- _map = {
- '2.5.29.32.0': 'any_policy',
- }
- class PolicyInformation(Sequence):
- _fields = [
- ('policy_identifier', PolicyIdentifier),
- ('policy_qualifiers', PolicyQualifierInfos, {'optional': True})
- ]
- class CertificatePolicies(SequenceOf):
- _child_spec = PolicyInformation
- class PolicyMapping(Sequence):
- _fields = [
- ('issuer_domain_policy', PolicyIdentifier),
- ('subject_domain_policy', PolicyIdentifier),
- ]
- class PolicyMappings(SequenceOf):
- _child_spec = PolicyMapping
- class PolicyConstraints(Sequence):
- _fields = [
- ('require_explicit_policy', Integer, {'implicit': 0, 'optional': True}),
- ('inhibit_policy_mapping', Integer, {'implicit': 1, 'optional': True}),
- ]
- class KeyPurposeId(ObjectIdentifier):
- _map = {
- # https://tools.ietf.org/html/rfc5280#page-45
- '2.5.29.37.0': 'any_extended_key_usage',
- '1.3.6.1.5.5.7.3.1': 'server_auth',
- '1.3.6.1.5.5.7.3.2': 'client_auth',
- '1.3.6.1.5.5.7.3.3': 'code_signing',
- '1.3.6.1.5.5.7.3.4': 'email_protection',
- '1.3.6.1.5.5.7.3.5': 'ipsec_end_system',
- '1.3.6.1.5.5.7.3.6': 'ipsec_tunnel',
- '1.3.6.1.5.5.7.3.7': 'ipsec_user',
- '1.3.6.1.5.5.7.3.8': 'time_stamping',
- '1.3.6.1.5.5.7.3.9': 'ocsp_signing',
- # http://tools.ietf.org/html/rfc3029.html#page-9
- '1.3.6.1.5.5.7.3.10': 'dvcs',
- # http://tools.ietf.org/html/rfc6268.html#page-16
- '1.3.6.1.5.5.7.3.13': 'eap_over_ppp',
- '1.3.6.1.5.5.7.3.14': 'eap_over_lan',
- # https://tools.ietf.org/html/rfc5055#page-76
- '1.3.6.1.5.5.7.3.15': 'scvp_server',
- '1.3.6.1.5.5.7.3.16': 'scvp_client',
- # https://tools.ietf.org/html/rfc4945#page-31
- '1.3.6.1.5.5.7.3.17': 'ipsec_ike',
- # https://tools.ietf.org/html/rfc5415#page-38
- '1.3.6.1.5.5.7.3.18': 'capwap_ac',
- '1.3.6.1.5.5.7.3.19': 'capwap_wtp',
- # https://tools.ietf.org/html/rfc5924#page-8
- '1.3.6.1.5.5.7.3.20': 'sip_domain',
- # https://tools.ietf.org/html/rfc6187#page-7
- '1.3.6.1.5.5.7.3.21': 'secure_shell_client',
- '1.3.6.1.5.5.7.3.22': 'secure_shell_server',
- # https://tools.ietf.org/html/rfc6494#page-7
- '1.3.6.1.5.5.7.3.23': 'send_router',
- '1.3.6.1.5.5.7.3.24': 'send_proxied_router',
- '1.3.6.1.5.5.7.3.25': 'send_owner',
- '1.3.6.1.5.5.7.3.26': 'send_proxied_owner',
- # https://tools.ietf.org/html/rfc6402#page-10
- '1.3.6.1.5.5.7.3.27': 'cmc_ca',
- '1.3.6.1.5.5.7.3.28': 'cmc_ra',
- '1.3.6.1.5.5.7.3.29': 'cmc_archive',
- # https://tools.ietf.org/html/draft-ietf-sidr-bgpsec-pki-profiles-15#page-6
- '1.3.6.1.5.5.7.3.30': 'bgpspec_router',
- # https://www.ietf.org/proceedings/44/I-D/draft-ietf-ipsec-pki-req-01.txt
- '1.3.6.1.5.5.8.2.2': 'ike_intermediate',
- # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378132(v=vs.85).aspx
- # and https://support.microsoft.com/en-us/kb/287547
- '1.3.6.1.4.1.311.10.3.1': 'microsoft_trust_list_signing',
- '1.3.6.1.4.1.311.10.3.2': 'microsoft_time_stamp_signing',
- '1.3.6.1.4.1.311.10.3.3': 'microsoft_server_gated',
- '1.3.6.1.4.1.311.10.3.3.1': 'microsoft_serialized',
- '1.3.6.1.4.1.311.10.3.4': 'microsoft_efs',
- '1.3.6.1.4.1.311.10.3.4.1': 'microsoft_efs_recovery',
- '1.3.6.1.4.1.311.10.3.5': 'microsoft_whql',
- '1.3.6.1.4.1.311.10.3.6': 'microsoft_nt5',
- '1.3.6.1.4.1.311.10.3.7': 'microsoft_oem_whql',
- '1.3.6.1.4.1.311.10.3.8': 'microsoft_embedded_nt',
- '1.3.6.1.4.1.311.10.3.9': 'microsoft_root_list_signer',
- '1.3.6.1.4.1.311.10.3.10': 'microsoft_qualified_subordination',
- '1.3.6.1.4.1.311.10.3.11': 'microsoft_key_recovery',
- '1.3.6.1.4.1.311.10.3.12': 'microsoft_document_signing',
- '1.3.6.1.4.1.311.10.3.13': 'microsoft_lifetime_signing',
- '1.3.6.1.4.1.311.10.3.14': 'microsoft_mobile_device_software',
- # https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography
- '1.3.6.1.4.1.311.20.2.2': 'microsoft_smart_card_logon',
- # https://opensource.apple.com/source
- # - /Security/Security-57031.40.6/Security/libsecurity_keychain/lib/SecPolicy.cpp
- # - /libsecurity_cssm/libsecurity_cssm-36064/lib/oidsalg.c
- '1.2.840.113635.100.1.2': 'apple_x509_basic',
- '1.2.840.113635.100.1.3': 'apple_ssl',
- '1.2.840.113635.100.1.4': 'apple_local_cert_gen',
- '1.2.840.113635.100.1.5': 'apple_csr_gen',
- '1.2.840.113635.100.1.6': 'apple_revocation_crl',
- '1.2.840.113635.100.1.7': 'apple_revocation_ocsp',
- '1.2.840.113635.100.1.8': 'apple_smime',
- '1.2.840.113635.100.1.9': 'apple_eap',
- '1.2.840.113635.100.1.10': 'apple_software_update_signing',
- '1.2.840.113635.100.1.11': 'apple_ipsec',
- '1.2.840.113635.100.1.12': 'apple_ichat',
- '1.2.840.113635.100.1.13': 'apple_resource_signing',
- '1.2.840.113635.100.1.14': 'apple_pkinit_client',
- '1.2.840.113635.100.1.15': 'apple_pkinit_server',
- '1.2.840.113635.100.1.16': 'apple_code_signing',
- '1.2.840.113635.100.1.17': 'apple_package_signing',
- '1.2.840.113635.100.1.18': 'apple_id_validation',
- '1.2.840.113635.100.1.20': 'apple_time_stamping',
- '1.2.840.113635.100.1.21': 'apple_revocation',
- '1.2.840.113635.100.1.22': 'apple_passbook_signing',
- '1.2.840.113635.100.1.23': 'apple_mobile_store',
- '1.2.840.113635.100.1.24': 'apple_escrow_service',
- '1.2.840.113635.100.1.25': 'apple_profile_signer',
- '1.2.840.113635.100.1.26': 'apple_qa_profile_signer',
- '1.2.840.113635.100.1.27': 'apple_test_mobile_store',
- '1.2.840.113635.100.1.28': 'apple_otapki_signer',
- '1.2.840.113635.100.1.29': 'apple_test_otapki_signer',
- '1.2.840.113625.100.1.30': 'apple_id_validation_record_signing_policy',
- '1.2.840.113625.100.1.31': 'apple_smp_encryption',
- '1.2.840.113625.100.1.32': 'apple_test_smp_encryption',
- '1.2.840.113635.100.1.33': 'apple_server_authentication',
- '1.2.840.113635.100.1.34': 'apple_pcs_escrow_service',
- # http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.201-2.pdf
- '2.16.840.1.101.3.6.8': 'piv_card_authentication',
- '2.16.840.1.101.3.6.7': 'piv_content_signing',
- # https://tools.ietf.org/html/rfc4556.html
- '1.3.6.1.5.2.3.4': 'pkinit_kpclientauth',
- '1.3.6.1.5.2.3.5': 'pkinit_kpkdc',
- # https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/changes.html
- '1.2.840.113583.1.1.5': 'adobe_authentic_documents_trust',
- # https://www.idmanagement.gov/wp-content/uploads/sites/1171/uploads/fpki-pivi-cert-profiles.pdf
- '2.16.840.1.101.3.8.7': 'fpki_pivi_content_signing'
- }
- class ExtKeyUsageSyntax(SequenceOf):
- _child_spec = KeyPurposeId
- class AccessMethod(ObjectIdentifier):
- _map = {
- '1.3.6.1.5.5.7.48.1': 'ocsp',
- '1.3.6.1.5.5.7.48.2': 'ca_issuers',
- '1.3.6.1.5.5.7.48.3': 'time_stamping',
- '1.3.6.1.5.5.7.48.5': 'ca_repository',
- }
- class AccessDescription(Sequence):
- _fields = [
- ('access_method', AccessMethod),
- ('access_location', GeneralName),
- ]
- class AuthorityInfoAccessSyntax(SequenceOf):
- _child_spec = AccessDescription
- class SubjectInfoAccessSyntax(SequenceOf):
- _child_spec = AccessDescription
- # https://tools.ietf.org/html/rfc7633
- class Features(SequenceOf):
- _child_spec = Integer
- class EntrustVersionInfo(Sequence):
- _fields = [
- ('entrust_vers', GeneralString),
- ('entrust_info_flags', BitString)
- ]
- class NetscapeCertificateType(BitString):
- _map = {
- 0: 'ssl_client',
- 1: 'ssl_server',
- 2: 'email',
- 3: 'object_signing',
- 4: 'reserved',
- 5: 'ssl_ca',
- 6: 'email_ca',
- 7: 'object_signing_ca',
- }
- class Version(Integer):
- _map = {
- 0: 'v1',
- 1: 'v2',
- 2: 'v3',
- }
- class TPMSpecification(Sequence):
- _fields = [
- ('family', UTF8String),
- ('level', Integer),
- ('revision', Integer),
- ]
- class SetOfTPMSpecification(SetOf):
- _child_spec = TPMSpecification
- class TCGSpecificationVersion(Sequence):
- _fields = [
- ('major_version', Integer),
- ('minor_version', Integer),
- ('revision', Integer),
- ]
- class TCGPlatformSpecification(Sequence):
- _fields = [
- ('version', TCGSpecificationVersion),
- ('platform_class', OctetString),
- ]
- class SetOfTCGPlatformSpecification(SetOf):
- _child_spec = TCGPlatformSpecification
- class EKGenerationType(Enumerated):
- _map = {
- 0: 'internal',
- 1: 'injected',
- 2: 'internal_revocable',
- 3: 'injected_revocable',
- }
- class EKGenerationLocation(Enumerated):
- _map = {
- 0: 'tpm_manufacturer',
- 1: 'platform_manufacturer',
- 2: 'ek_cert_signer',
- }
- class EKCertificateGenerationLocation(Enumerated):
- _map = {
- 0: 'tpm_manufacturer',
- 1: 'platform_manufacturer',
- 2: 'ek_cert_signer',
- }
- class EvaluationAssuranceLevel(Enumerated):
- _map = {
- 1: 'level1',
- 2: 'level2',
- 3: 'level3',
- 4: 'level4',
- 5: 'level5',
- 6: 'level6',
- 7: 'level7',
- }
- class EvaluationStatus(Enumerated):
- _map = {
- 0: 'designed_to_meet',
- 1: 'evaluation_in_progress',
- 2: 'evaluation_completed',
- }
- class StrengthOfFunction(Enumerated):
- _map = {
- 0: 'basic',
- 1: 'medium',
- 2: 'high',
- }
- class URIReference(Sequence):
- _fields = [
- ('uniform_resource_identifier', IA5String),
- ('hash_algorithm', DigestAlgorithm, {'optional': True}),
- ('hash_value', BitString, {'optional': True}),
- ]
- class CommonCriteriaMeasures(Sequence):
- _fields = [
- ('version', IA5String),
- ('assurance_level', EvaluationAssuranceLevel),
- ('evaluation_status', EvaluationStatus),
- ('plus', Boolean, {'default': False}),
- ('strengh_of_function', StrengthOfFunction, {'implicit': 0, 'optional': True}),
- ('profile_oid', ObjectIdentifier, {'implicit': 1, 'optional': True}),
- ('profile_url', URIReference, {'implicit': 2, 'optional': True}),
- ('target_oid', ObjectIdentifier, {'implicit': 3, 'optional': True}),
- ('target_uri', URIReference, {'implicit': 4, 'optional': True}),
- ]
- class SecurityLevel(Enumerated):
- _map = {
- 1: 'level1',
- 2: 'level2',
- 3: 'level3',
- 4: 'level4',
- }
- class FIPSLevel(Sequence):
- _fields = [
- ('version', IA5String),
- ('level', SecurityLevel),
- ('plus', Boolean, {'default': False}),
- ]
- class TPMSecurityAssertions(Sequence):
- _fields = [
- ('version', Version, {'default': 'v1'}),
- ('field_upgradable', Boolean, {'default': False}),
- ('ek_generation_type', EKGenerationType, {'implicit': 0, 'optional': True}),
- ('ek_generation_location', EKGenerationLocation, {'implicit': 1, 'optional': True}),
- ('ek_certificate_generation_location', EKCertificateGenerationLocation, {'implicit': 2, 'optional': True}),
- ('cc_info', CommonCriteriaMeasures, {'implicit': 3, 'optional': True}),
- ('fips_level', FIPSLevel, {'implicit': 4, 'optional': True}),
- ('iso_9000_certified', Boolean, {'implicit': 5, 'default': False}),
- ('iso_9000_uri', IA5String, {'optional': True}),
- ]
- class SetOfTPMSecurityAssertions(SetOf):
- _child_spec = TPMSecurityAssertions
- class SubjectDirectoryAttributeId(ObjectIdentifier):
- _map = {
- # https://tools.ietf.org/html/rfc2256#page-11
- '2.5.4.52': 'supported_algorithms',
- # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
- '2.23.133.2.16': 'tpm_specification',
- '2.23.133.2.17': 'tcg_platform_specification',
- '2.23.133.2.18': 'tpm_security_assertions',
- # https://tools.ietf.org/html/rfc3739#page-18
- '1.3.6.1.5.5.7.9.1': 'pda_date_of_birth',
- '1.3.6.1.5.5.7.9.2': 'pda_place_of_birth',
- '1.3.6.1.5.5.7.9.3': 'pda_gender',
- '1.3.6.1.5.5.7.9.4': 'pda_country_of_citizenship',
- '1.3.6.1.5.5.7.9.5': 'pda_country_of_residence',
- # https://holtstrom.com/michael/tools/asn1decoder.php
- '1.2.840.113533.7.68.29': 'entrust_user_role',
- }
- class SetOfGeneralizedTime(SetOf):
- _child_spec = GeneralizedTime
- class SetOfDirectoryString(SetOf):
- _child_spec = DirectoryString
- class SetOfPrintableString(SetOf):
- _child_spec = PrintableString
- class SupportedAlgorithm(Sequence):
- _fields = [
- ('algorithm_identifier', AnyAlgorithmIdentifier),
- ('intended_usage', KeyUsage, {'explicit': 0, 'optional': True}),
- ('intended_certificate_policies', CertificatePolicies, {'explicit': 1, 'optional': True}),
- ]
- class SetOfSupportedAlgorithm(SetOf):
- _child_spec = SupportedAlgorithm
- class SubjectDirectoryAttribute(Sequence):
- _fields = [
- ('type', SubjectDirectoryAttributeId),
- ('values', Any),
- ]
- _oid_pair = ('type', 'values')
- _oid_specs = {
- 'supported_algorithms': SetOfSupportedAlgorithm,
- 'tpm_specification': SetOfTPMSpecification,
- 'tcg_platform_specification': SetOfTCGPlatformSpecification,
- 'tpm_security_assertions': SetOfTPMSecurityAssertions,
- 'pda_date_of_birth': SetOfGeneralizedTime,
- 'pda_place_of_birth': SetOfDirectoryString,
- 'pda_gender': SetOfPrintableString,
- 'pda_country_of_citizenship': SetOfPrintableString,
- 'pda_country_of_residence': SetOfPrintableString,
- }
- def _values_spec(self):
- type_ = self['type'].native
- if type_ in self._oid_specs:
- return self._oid_specs[type_]
- return SetOf
- _spec_callbacks = {
- 'values': _values_spec
- }
- class SubjectDirectoryAttributes(SequenceOf):
- _child_spec = SubjectDirectoryAttribute
- class ExtensionId(ObjectIdentifier):
- _map = {
- '2.5.29.9': 'subject_directory_attributes',
- '2.5.29.14': 'key_identifier',
- '2.5.29.15': 'key_usage',
- '2.5.29.16': 'private_key_usage_period',
- '2.5.29.17': 'subject_alt_name',
- '2.5.29.18': 'issuer_alt_name',
- '2.5.29.19': 'basic_constraints',
- '2.5.29.30': 'name_constraints',
- '2.5.29.31': 'crl_distribution_points',
- '2.5.29.32': 'certificate_policies',
- '2.5.29.33': 'policy_mappings',
- '2.5.29.35': 'authority_key_identifier',
- '2.5.29.36': 'policy_constraints',
- '2.5.29.37': 'extended_key_usage',
- '2.5.29.46': 'freshest_crl',
- '2.5.29.54': 'inhibit_any_policy',
- '1.3.6.1.5.5.7.1.1': 'authority_information_access',
- '1.3.6.1.5.5.7.1.11': 'subject_information_access',
- # https://tools.ietf.org/html/rfc7633
- '1.3.6.1.5.5.7.1.24': 'tls_feature',
- '1.3.6.1.5.5.7.48.1.5': 'ocsp_no_check',
- '1.2.840.113533.7.65.0': 'entrust_version_extension',
- '2.16.840.1.113730.1.1': 'netscape_certificate_type',
- # https://tools.ietf.org/html/rfc6962.html#page-14
- '1.3.6.1.4.1.11129.2.4.2': 'signed_certificate_timestamp_list',
- # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/3aec3e50-511a-42f9-a5d5-240af503e470
- '1.3.6.1.4.1.311.20.2': 'microsoft_enroll_certtype',
- }
- class Extension(Sequence):
- _fields = [
- ('extn_id', ExtensionId),
- ('critical', Boolean, {'default': False}),
- ('extn_value', ParsableOctetString),
- ]
- _oid_pair = ('extn_id', 'extn_value')
- _oid_specs = {
- 'subject_directory_attributes': SubjectDirectoryAttributes,
- 'key_identifier': OctetString,
- 'key_usage': KeyUsage,
- 'private_key_usage_period': PrivateKeyUsagePeriod,
- 'subject_alt_name': GeneralNames,
- 'issuer_alt_name': GeneralNames,
- 'basic_constraints': BasicConstraints,
- 'name_constraints': NameConstraints,
- 'crl_distribution_points': CRLDistributionPoints,
- 'certificate_policies': CertificatePolicies,
- 'policy_mappings': PolicyMappings,
- 'authority_key_identifier': AuthorityKeyIdentifier,
- 'policy_constraints': PolicyConstraints,
- 'extended_key_usage': ExtKeyUsageSyntax,
- 'freshest_crl': CRLDistributionPoints,
- 'inhibit_any_policy': Integer,
- 'authority_information_access': AuthorityInfoAccessSyntax,
- 'subject_information_access': SubjectInfoAccessSyntax,
- 'tls_feature': Features,
- 'ocsp_no_check': Null,
- 'entrust_version_extension': EntrustVersionInfo,
- 'netscape_certificate_type': NetscapeCertificateType,
- 'signed_certificate_timestamp_list': OctetString,
- # Not UTF8String as Microsofts docs claim, see:
- # https://www.alvestrand.no/objectid/1.3.6.1.4.1.311.20.2.html
- 'microsoft_enroll_certtype': BMPString,
- }
- class Extensions(SequenceOf):
- _child_spec = Extension
- class TbsCertificate(Sequence):
- _fields = [
- ('version', Version, {'explicit': 0, 'default': 'v1'}),
- ('serial_number', Integer),
- ('signature', SignedDigestAlgorithm),
- ('issuer', Name),
- ('validity', Validity),
- ('subject', Name),
- ('subject_public_key_info', PublicKeyInfo),
- ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}),
- ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}),
- ('extensions', Extensions, {'explicit': 3, 'optional': True}),
- ]
- class Certificate(Sequence):
- _fields = [
- ('tbs_certificate', TbsCertificate),
- ('signature_algorithm', SignedDigestAlgorithm),
- ('signature_value', OctetBitString),
- ]
- _processed_extensions = False
- _critical_extensions = None
- _subject_directory_attributes_value = None
- _key_identifier_value = None
- _key_usage_value = None
- _subject_alt_name_value = None
- _issuer_alt_name_value = None
- _basic_constraints_value = None
- _name_constraints_value = None
- _crl_distribution_points_value = None
- _certificate_policies_value = None
- _policy_mappings_value = None
- _authority_key_identifier_value = None
- _policy_constraints_value = None
- _freshest_crl_value = None
- _inhibit_any_policy_value = None
- _extended_key_usage_value = None
- _authority_information_access_value = None
- _subject_information_access_value = None
- _private_key_usage_period_value = None
- _tls_feature_value = None
- _ocsp_no_check_value = None
- _issuer_serial = None
- _authority_issuer_serial = False
- _crl_distribution_points = None
- _delta_crl_distribution_points = None
- _valid_domains = None
- _valid_ips = None
- _self_issued = None
- _self_signed = None
- _sha1 = None
- _sha256 = None
- def _set_extensions(self):
- """
- Sets common named extensions to private attributes and creates a list
- of critical extensions
- """
- self._critical_extensions = set()
- for extension in self['tbs_certificate']['extensions']:
- name = extension['extn_id'].native
- attribute_name = '_%s_value' % name
- if hasattr(self, attribute_name):
- setattr(self, attribute_name, extension['extn_value'].parsed)
- if extension['critical'].native:
- self._critical_extensions.add(name)
- self._processed_extensions = True
- @property
- def critical_extensions(self):
- """
- Returns a set of the names (or OID if not a known extension) of the
- extensions marked as critical
- :return:
- A set of unicode strings
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._critical_extensions
- @property
- def private_key_usage_period_value(self):
- """
- This extension is used to constrain the period over which the subject
- private key may be used
- :return:
- None or a PrivateKeyUsagePeriod object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._private_key_usage_period_value
- @property
- def subject_directory_attributes_value(self):
- """
- This extension is used to contain additional identification attributes
- about the subject.
- :return:
- None or a SubjectDirectoryAttributes object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._subject_directory_attributes_value
- @property
- def key_identifier_value(self):
- """
- This extension is used to help in creating certificate validation paths.
- It contains an identifier that should generally, but is not guaranteed
- to, be unique.
- :return:
- None or an OctetString object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._key_identifier_value
- @property
- def key_usage_value(self):
- """
- This extension is used to define the purpose of the public key
- contained within the certificate.
- :return:
- None or a KeyUsage
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._key_usage_value
- @property
- def subject_alt_name_value(self):
- """
- This extension allows for additional names to be associate with the
- subject of the certificate. While it may contain a whole host of
- possible names, it is usually used to allow certificates to be used
- with multiple different domain names.
- :return:
- None or a GeneralNames object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._subject_alt_name_value
- @property
- def issuer_alt_name_value(self):
- """
- This extension allows associating one or more alternative names with
- the issuer of the certificate.
- :return:
- None or an x509.GeneralNames object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._issuer_alt_name_value
- @property
- def basic_constraints_value(self):
- """
- This extension is used to determine if the subject of the certificate
- is a CA, and if so, what the maximum number of intermediate CA certs
- after this are, before an end-entity certificate is found.
- :return:
- None or a BasicConstraints object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._basic_constraints_value
- @property
- def name_constraints_value(self):
- """
- This extension is used in CA certificates, and is used to limit the
- possible names of certificates issued.
- :return:
- None or a NameConstraints object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._name_constraints_value
- @property
- def crl_distribution_points_value(self):
- """
- This extension is used to help in locating the CRL for this certificate.
- :return:
- None or a CRLDistributionPoints object
- extension
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._crl_distribution_points_value
- @property
- def certificate_policies_value(self):
- """
- This extension defines policies in CA certificates under which
- certificates may be issued. In end-entity certificates, the inclusion
- of a policy indicates the issuance of the certificate follows the
- policy.
- :return:
- None or a CertificatePolicies object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._certificate_policies_value
- @property
- def policy_mappings_value(self):
- """
- This extension allows mapping policy OIDs to other OIDs. This is used
- to allow different policies to be treated as equivalent in the process
- of validation.
- :return:
- None or a PolicyMappings object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._policy_mappings_value
- @property
- def authority_key_identifier_value(self):
- """
- This extension helps in identifying the public key with which to
- validate the authenticity of the certificate.
- :return:
- None or an AuthorityKeyIdentifier object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._authority_key_identifier_value
- @property
- def policy_constraints_value(self):
- """
- This extension is used to control if policy mapping is allowed and
- when policies are required.
- :return:
- None or a PolicyConstraints object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._policy_constraints_value
- @property
- def freshest_crl_value(self):
- """
- This extension is used to help locate any available delta CRLs
- :return:
- None or an CRLDistributionPoints object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._freshest_crl_value
- @property
- def inhibit_any_policy_value(self):
- """
- This extension is used to prevent mapping of the any policy to
- specific requirements
- :return:
- None or a Integer object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._inhibit_any_policy_value
- @property
- def extended_key_usage_value(self):
- """
- This extension is used to define additional purposes for the public key
- beyond what is contained in the basic constraints.
- :return:
- None or an ExtKeyUsageSyntax object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._extended_key_usage_value
- @property
- def authority_information_access_value(self):
- """
- This extension is used to locate the CA certificate used to sign this
- certificate, or the OCSP responder for this certificate.
- :return:
- None or an AuthorityInfoAccessSyntax object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._authority_information_access_value
- @property
- def subject_information_access_value(self):
- """
- This extension is used to access information about the subject of this
- certificate.
- :return:
- None or a SubjectInfoAccessSyntax object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._subject_information_access_value
- @property
- def tls_feature_value(self):
- """
- This extension is used to list the TLS features a server must respond
- with if a client initiates a request supporting them.
- :return:
- None or a Features object
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._tls_feature_value
- @property
- def ocsp_no_check_value(self):
- """
- This extension is used on certificates of OCSP responders, indicating
- that revocation information for the certificate should never need to
- be verified, thus preventing possible loops in path validation.
- :return:
- None or a Null object (if present)
- """
- if not self._processed_extensions:
- self._set_extensions()
- return self._ocsp_no_check_value
- @property
- def signature(self):
- """
- :return:
- A byte string of the signature
- """
- return self['signature_value'].native
- @property
- def signature_algo(self):
- """
- :return:
- A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa"
- """
- return self['signature_algorithm'].signature_algo
- @property
- def hash_algo(self):
- """
- :return:
- A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
- "sha384", "sha512", "sha512_224", "sha512_256"
- """
- return self['signature_algorithm'].hash_algo
- @property
- def public_key(self):
- """
- :return:
- The PublicKeyInfo object for this certificate
- """
- return self['tbs_certificate']['subject_public_key_info']
- @property
- def subject(self):
- """
- :return:
- The Name object for the subject of this certificate
- """
- return self['tbs_certificate']['subject']
- @property
- def issuer(self):
- """
- :return:
- The Name object for the issuer of this certificate
- """
- return self['tbs_certificate']['issuer']
- @property
- def serial_number(self):
- """
- :return:
- An integer of the certificate's serial number
- """
- return self['tbs_certificate']['serial_number'].native
- @property
- def key_identifier(self):
- """
- :return:
- None or a byte string of the certificate's key identifier from the
- key identifier extension
- """
- if not self.key_identifier_value:
- return None
- return self.key_identifier_value.native
- @property
- def issuer_serial(self):
- """
- :return:
- A byte string of the SHA-256 hash of the issuer concatenated with
- the ascii character ":", concatenated with the serial number as
- an ascii string
- """
- if self._issuer_serial is None:
- self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii')
- return self._issuer_serial
- @property
- def not_valid_after(self):
- """
- :return:
- A datetime of latest time when the certificate is still valid
- """
- return self['tbs_certificate']['validity']['not_after'].native
- @property
- def not_valid_before(self):
- """
- :return:
- A datetime of the earliest time when the certificate is valid
- """
- return self['tbs_certificate']['validity']['not_before'].native
- @property
- def authority_key_identifier(self):
- """
- :return:
- None or a byte string of the key_identifier from the authority key
- identifier extension
- """
- if not self.authority_key_identifier_value:
- return None
- return self.authority_key_identifier_value['key_identifier'].native
- @property
- def authority_issuer_serial(self):
- """
- :return:
- None or a byte string of the SHA-256 hash of the isser from the
- authority key identifier extension concatenated with the ascii
- character ":", concatenated with the serial number from the
- authority key identifier extension as an ascii string
- """
- if self._authority_issuer_serial is False:
- akiv = self.authority_key_identifier_value
- if akiv and akiv['authority_cert_issuer'].native:
- issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen
- # We untag the element since it is tagged via being a choice from GeneralName
- issuer = issuer.untag()
- authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native
- self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii')
- else:
- self._authority_issuer_serial = None
- return self._authority_issuer_serial
- @property
- def crl_distribution_points(self):
- """
- Returns complete CRL URLs - does not include delta CRLs
- :return:
- A list of zero or more DistributionPoint objects
- """
- if self._crl_distribution_points is None:
- self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value)
- return self._crl_distribution_points
- @property
- def delta_crl_distribution_points(self):
- """
- Returns delta CRL URLs - does not include complete CRLs
- :return:
- A list of zero or more DistributionPoint objects
- """
- if self._delta_crl_distribution_points is None:
- self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value)
- return self._delta_crl_distribution_points
- def _get_http_crl_distribution_points(self, crl_distribution_points):
- """
- Fetches the DistributionPoint object for non-relative, HTTP CRLs
- referenced by the certificate
- :param crl_distribution_points:
- A CRLDistributionPoints object to grab the DistributionPoints from
- :return:
- A list of zero or more DistributionPoint objects
- """
- output = []
- if crl_distribution_points is None:
- return []
- for distribution_point in crl_distribution_points:
- distribution_point_name = distribution_point['distribution_point']
- if distribution_point_name is VOID:
- continue
- # RFC 5280 indicates conforming CA should not use the relative form
- if distribution_point_name.name == 'name_relative_to_crl_issuer':
- continue
- # This library is currently only concerned with HTTP-based CRLs
- for general_name in distribution_point_name.chosen:
- if general_name.name == 'uniform_resource_identifier':
- output.append(distribution_point)
- return output
- @property
- def ocsp_urls(self):
- """
- :return:
- A list of zero or more unicode strings of the OCSP URLs for this
- cert
- """
- if not self.authority_information_access_value:
- return []
- output = []
- for entry in self.authority_information_access_value:
- if entry['access_method'].native == 'ocsp':
- location = entry['access_location']
- if location.name != 'uniform_resource_identifier':
- continue
- url = location.native
- if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
- output.append(url)
- return output
- @property
- def valid_domains(self):
- """
- :return:
- A list of unicode strings of valid domain names for the certificate.
- Wildcard certificates will have a domain in the form: *.example.com
- """
- if self._valid_domains is None:
- self._valid_domains = []
- # For the subject alt name extension, we can look at the name of
- # the choice selected since it distinguishes between domain names,
- # email addresses, IPs, etc
- if self.subject_alt_name_value:
- for general_name in self.subject_alt_name_value:
- if general_name.name == 'dns_name' and general_name.native not in self._valid_domains:
- self._valid_domains.append(general_name.native)
- # If there was no subject alt name extension, and the common name
- # in the subject looks like a domain, that is considered the valid
- # list. This is done because according to
- # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common
- # name should not be used if the subject alt name is present.
- else:
- pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$')
- for rdn in self.subject.chosen:
- for name_type_value in rdn:
- if name_type_value['type'].native == 'common_name':
- value = name_type_value['value'].native
- if pattern.match(value):
- self._valid_domains.append(value)
- return self._valid_domains
- @property
- def valid_ips(self):
- """
- :return:
- A list of unicode strings of valid IP addresses for the certificate
- """
- if self._valid_ips is None:
- self._valid_ips = []
- if self.subject_alt_name_value:
- for general_name in self.subject_alt_name_value:
- if general_name.name == 'ip_address':
- self._valid_ips.append(general_name.native)
- return self._valid_ips
- @property
- def ca(self):
- """
- :return;
- A boolean - if the certificate is marked as a CA
- """
- return self.basic_constraints_value and self.basic_constraints_value['ca'].native
- @property
- def max_path_length(self):
- """
- :return;
- None or an integer of the maximum path length
- """
- if not self.ca:
- return None
- return self.basic_constraints_value['path_len_constraint'].native
- @property
- def self_issued(self):
- """
- :return:
- A boolean - if the certificate is self-issued, as defined by RFC
- 5280
- """
- if self._self_issued is None:
- self._self_issued = self.subject == self.issuer
- return self._self_issued
- @property
- def self_signed(self):
- """
- :return:
- A unicode string of "no" or "maybe". The "maybe" result will
- be returned if the certificate issuer and subject are the same.
- If a key identifier and authority key identifier are present,
- they will need to match otherwise "no" will be returned.
- To verify is a certificate is truly self-signed, the signature
- will need to be verified. See the certvalidator package for
- one possible solution.
- """
- if self._self_signed is None:
- self._self_signed = 'no'
- if self.self_issued:
- if self.key_identifier:
- if not self.authority_key_identifier:
- self._self_signed = 'maybe'
- elif self.authority_key_identifier == self.key_identifier:
- self._self_signed = 'maybe'
- else:
- self._self_signed = 'maybe'
- return self._self_signed
- @property
- def sha1(self):
- """
- :return:
- The SHA-1 hash of the DER-encoded bytes of this complete certificate
- """
- if self._sha1 is None:
- self._sha1 = hashlib.sha1(self.dump()).digest()
- return self._sha1
- @property
- def sha1_fingerprint(self):
- """
- :return:
- A unicode string of the SHA-1 hash, formatted using hex encoding
- with a space between each pair of characters, all uppercase
- """
- return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))
- @property
- def sha256(self):
- """
- :return:
- The SHA-256 hash of the DER-encoded bytes of this complete
- certificate
- """
- if self._sha256 is None:
- self._sha256 = hashlib.sha256(self.dump()).digest()
- return self._sha256
- @property
- def sha256_fingerprint(self):
- """
- :return:
- A unicode string of the SHA-256 hash, formatted using hex encoding
- with a space between each pair of characters, all uppercase
- """
- return ' '.join('%02X' % c for c in bytes_to_list(self.sha256))
- def is_valid_domain_ip(self, domain_ip):
- """
- Check if a domain name or IP address is valid according to the
- certificate
- :param domain_ip:
- A unicode string of a domain name or IP address
- :return:
- A boolean - if the domain or IP is valid for the certificate
- """
- if not isinstance(domain_ip, str_cls):
- raise TypeError(unwrap(
- '''
- domain_ip must be a unicode string, not %s
- ''',
- type_name(domain_ip)
- ))
- encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower()
- is_ipv6 = encoded_domain_ip.find(':') != -1
- is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip)
- is_domain = not is_ipv6 and not is_ipv4
- # Handle domain name checks
- if is_domain:
- if not self.valid_domains:
- return False
- domain_labels = encoded_domain_ip.split('.')
- for valid_domain in self.valid_domains:
- encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower()
- valid_domain_labels = encoded_valid_domain.split('.')
- # The domain must be equal in label length to match
- if len(valid_domain_labels) != len(domain_labels):
- continue
- if valid_domain_labels == domain_labels:
- return True
- is_wildcard = self._is_wildcard_domain(encoded_valid_domain)
- if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels):
- return True
- return False
- # Handle IP address checks
- if not self.valid_ips:
- return False
- family = socket.AF_INET if is_ipv4 else socket.AF_INET6
- normalized_ip = inet_pton(family, encoded_domain_ip)
- for valid_ip in self.valid_ips:
- valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6
- normalized_valid_ip = inet_pton(valid_family, valid_ip)
- if normalized_valid_ip == normalized_ip:
- return True
- return False
- def _is_wildcard_domain(self, domain):
- """
- Checks if a domain is a valid wildcard according to
- https://tools.ietf.org/html/rfc6125#section-6.4.3
- :param domain:
- A unicode string of the domain name, where any U-labels from an IDN
- have been converted to A-labels
- :return:
- A boolean - if the domain is a valid wildcard domain
- """
- # The * character must be present for a wildcard match, and if there is
- # most than one, it is an invalid wildcard specification
- if domain.count('*') != 1:
- return False
- labels = domain.lower().split('.')
- if not labels:
- return False
- # Wildcards may only appear in the left-most label
- if labels[0].find('*') == -1:
- return False
- # Wildcards may not be embedded in an A-label from an IDN
- if labels[0][0:4] == 'xn--':
- return False
- return True
- def _is_wildcard_match(self, domain_labels, valid_domain_labels):
- """
- Determines if the labels in a domain are a match for labels from a
- wildcard valid domain name
- :param domain_labels:
- A list of unicode strings, with A-label form for IDNs, of the labels
- in the domain name to check
- :param valid_domain_labels:
- A list of unicode strings, with A-label form for IDNs, of the labels
- in a wildcard domain pattern
- :return:
- A boolean - if the domain matches the valid domain
- """
- first_domain_label = domain_labels[0]
- other_domain_labels = domain_labels[1:]
- wildcard_label = valid_domain_labels[0]
- other_valid_domain_labels = valid_domain_labels[1:]
- # The wildcard is only allowed in the first label, so if
- # The subsequent labels are not equal, there is no match
- if other_domain_labels != other_valid_domain_labels:
- return False
- if wildcard_label == '*':
- return True
- wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$')
- if wildcard_regex.match(first_domain_label):
- return True
- return False
- # The structures are taken from the OpenSSL source file x_x509a.c, and specify
- # extra information that is added to X.509 certificates to store trust
- # information about the certificate.
- class KeyPurposeIdentifiers(SequenceOf):
- _child_spec = KeyPurposeId
- class SequenceOfAlgorithmIdentifiers(SequenceOf):
- _child_spec = AlgorithmIdentifier
- class CertificateAux(Sequence):
- _fields = [
- ('trust', KeyPurposeIdentifiers, {'optional': True}),
- ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}),
- ('alias', UTF8String, {'optional': True}),
- ('keyid', OctetString, {'optional': True}),
- ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}),
- ]
- class TrustedCertificate(Concat):
- _child_specs = [Certificate, CertificateAux]
|