django_ca.utils
- utility functions¶
Central functions to load CA key and cert as PKey/X509 objects.
-
django_ca.utils.
GENERAL_NAME_RE
= re.compile('^(email|URI|IP|DNS|RID|dirName|otherName):(.*)', re.IGNORECASE)¶ Regular expression to match general names.
-
class
django_ca.utils.
LazyEncoder
(skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)[source]¶ Encoder that also encodes strings translated with ugettext_lazy.
-
default
(obj)[source]¶ Implement this method in a subclass such that it returns a serializable object for
o
, or calls the base implementation (to raise aTypeError
).For example, to support arbitrary iterators, you could implement default like this:
def default(self, o): try: iterable = iter(o) except TypeError: pass else: return list(iterable) # Let the base class default method raise the TypeError return JSONEncoder.default(self, o)
-
-
django_ca.utils.
NAME_RE
= re.compile('(?:/+|\\A)\\s*(?P<field>[^\\s]*?)\\s*=(?P<quote>[\\\'"])?\\s*(?P<content>(?(quote).*?|[^/]*))\\s*(?(quote)(?<!\\\\)(?P=quote))', re.IGNORECASE)¶ Regular expression to match RDNs out of a full x509 name.
-
django_ca.utils.
OID_NAME_MAPPINGS
= {<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>: 'ST', <ObjectIdentifier(oid=2.5.4.6, name=countryName)>: 'C', <ObjectIdentifier(oid=2.5.4.10, name=organizationName)>: 'O', <ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>: 'OU', <ObjectIdentifier(oid=2.5.4.3, name=commonName)>: 'CN', <ObjectIdentifier(oid=2.5.4.7, name=localityName)>: 'L', <ObjectIdentifier(oid=1.2.840.113549.1.9.1, name=emailAddress)>: 'emailAddress'}¶ Map OID objects to IDs used in subject strings
-
django_ca.utils.
add_colons
(s)[source]¶ Add colons after every second digit.
This function is used in functions to prettify serials.
>>> add_colons('teststring') 'te:st:st:ri:ng'
-
django_ca.utils.
format_general_name
(name)[source]¶ Format a single general name.
>>> import ipaddress >>> format_general_name(x509.DNSName('example.com')) 'DNS:example.com' >>> format_general_name(x509.IPAddress(ipaddress.IPv4Address('127.0.0.1'))) 'IP:127.0.0.1'
-
django_ca.utils.
format_general_names
(names)[source]¶ Format a list of general names.
>>> import ipaddress >>> format_general_names([x509.DNSName('example.com')]) 'DNS:example.com' >>> format_general_names([x509.IPAddress(ipaddress.IPv4Address('127.0.0.1'))]) 'IP:127.0.0.1' >>> format_general_names([x509.DirectoryName( ... x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, 'example.com')]))]) 'dirname:/CN=example.com' >>> format_general_names([x509.DNSName('example.com'), x509.DNSName('example.net')]) 'DNS:example.com, DNS:example.net'
-
django_ca.utils.
format_name
(subject)[source]¶ Convert a subject into the canonical form for distinguished names.
This function does not take care of sorting the subject in any meaningful order.
Examples:
>>> format_name([('CN', 'example.com'), ]) '/CN=example.com' >>> format_name([('CN', 'example.com'), ('O', "My Organization"), ]) '/CN=example.com/O=My Organization'
-
django_ca.utils.
get_cert_builder
(expires, now=None)[source]¶ Get a basic X509 cert object.
Parameters: - expires : datetime
When this certificate will expire.
- now : datetime
The functions notion of “now”, used for testing.
-
django_ca.utils.
get_cert_profile_kwargs
(name=None)[source]¶ Get kwargs suitable for get_cert X509 keyword arguments from the given profile.
-
django_ca.utils.
int_to_hex
(i)[source]¶ Create a hex-representation of the given serial.
>>> int_to_hex(12345678) 'BC:61:4E'
-
django_ca.utils.
is_power2
(num)[source]¶ Return True if num is a power of 2.
>>> is_power2(4) True >>> is_power2(3) False
-
django_ca.utils.
multiline_url_validator
(value)[source]¶ Validate that a TextField contains one valid URL per line.
-
django_ca.utils.
parse_general_name
(name)[source]¶ Parse a general name from user input.
This function will do its best to detect the intended type of any value passed to it:
>>> parse_general_name('example.com') <DNSName(value='example.com')> >>> parse_general_name('*.example.com') <DNSName(value='*.example.com')> >>> parse_general_name('.example.com') # Syntax used e.g. for NameConstraints: All levels of subdomains <DNSName(value='.example.com')> >>> parse_general_name('user@example.com') <RFC822Name(value='user@example.com')> >>> parse_general_name('https://example.com') <UniformResourceIdentifier(value='https://example.com')> >>> parse_general_name('1.2.3.4') <IPAddress(value=1.2.3.4)> >>> parse_general_name('fd00::1') <IPAddress(value=fd00::1)> >>> parse_general_name('/CN=example.com') <DirectoryName(value=<Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='example.com')>])>)>
The default fallback is to assume a
DNSName
. If this doesn’t work, an exception will be raised:>>> parse_general_name('foo..bar`*123') Traceback (most recent call last): ... idna.core.IDNAError: The label b'' is not a valid A-label >>> parse_general_name('foo bar') Traceback (most recent call last): ... idna.core.IDNAError: The label b'foo bar' is not a valid A-label
If you want to override detection, you can prefix the name to match
GENERAL_NAME_RE
:>>> parse_general_name('email:user@example.com') <RFC822Name(value='user@example.com')> >>> parse_general_name('URI:https://example.com') <UniformResourceIdentifier(value='https://example.com')> >>> parse_general_name('dirname:/CN=example.com') <DirectoryName(value=<Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='example.com')>])>)>
Some more exotic values can only be generated by using this prefix:
>>> parse_general_name('rid:2.5.4.3') <RegisteredID(value=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>)> >>> parse_general_name('otherName:2.5.4.3;UTF8:example.com') <OtherName(type_id=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=b'example.com')>
If you give a prefixed value, this function is less forgiving of any typos and does not catch any exceptions:
>>> parse_general_name('email:foo@bar com') Traceback (most recent call last): ... ValueError: Invalid domain: bar com
-
django_ca.utils.
parse_name
(name)[source]¶ Parses a subject string as used in OpenSSLs command line utilities.
The
name
is expected to be close to the subject format commonly used by OpenSSL, for example/C=AT/L=Vienna/CN=example.com/emailAddress=user@example.com
. The function does its best to be lenient on deviations from the format, object identifiers are case-insensitive (e.g.cn
is the same asCN
, whitespace at the start and end is stripped and the subject does not have to start with a slash (/
).>>> parse_name('/CN=example.com') OrderedDict([('CN', 'example.com')]) >>> parse_name('c=AT/l= Vienna/o="ex org"/CN=example.com') OrderedDict([('C', 'AT'), ('L', 'Vienna'), ('O', 'ex org'), ('CN', 'example.com')])
Dictionary keys are normalized to the values of
OID_NAME_MAPPINGS
and keys will be sorted based on x509 name specifications regardless of the given order:>>> parse_name('L="Vienna / District"/EMAILaddress=user@example.com') OrderedDict([('L', 'Vienna / District'), ('emailAddress', 'user@example.com')]) >>> parse_name('/C=AT/CN=example.com') == parse_name('/CN=example.com/C=AT') True
Due to the magic of
NAME_RE
, the function even supports quoting strings and including slashes, so strings like/OU="Org / Org Unit"/CN=example.com
will work as expected.>>> parse_name('L="Vienna / District"/CN=example.com') OrderedDict([('L', 'Vienna / District'), ('CN', 'example.com')])
But note that it’s still easy to trick this function, if you really want to. The following example is not a valid subject, the location is just bogus, and whatever you were expecting as output, it’s certainly different:
>>> parse_name('L="Vienna " District"/CN=example.com') OrderedDict([('L', 'Vienna'), ('CN', 'example.com')])
Examples of where this string is used are:
# openssl req -new -key priv.key -out csr -utf8 -batch -sha256 -subj '/C=AT/CN=example.com' # openssl x509 -in cert.pem -noout -subject -nameopt compat /C=AT/L=Vienna/CN=example.com
-
django_ca.utils.
sort_subject_dict
(d)[source]¶ Returns an itemized dictionary in the correct order for a x509 subject.
-
django_ca.utils.
validate_email
(addr)[source]¶ Validate an email address.
This function raises
ValueError
if the email address is not valid.>>> validate_email('foo@bar.com') 'foo@bar.com' >>> validate_email('foo@bar com') Traceback (most recent call last): ... ValueError: Invalid domain: bar com
-
django_ca.utils.
x509_name
(name)[source]¶ Parses a subject string into a
x509.Name
.If
name
is a string,parse_name()
is used to parse it. A list of tuples or adict
(preferrably anOrderedDict
) is also supported.>>> x509_name('/C=AT/CN=example.com') <Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value='AT')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='example.com')>])> >>> x509_name([('C', 'AT'), ('CN', 'example.com')]) <Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value='AT')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='example.com')>])> >>> x509_name(OrderedDict([('C', 'AT'), ('CN', 'example.com')])) <Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value='AT')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='example.com')>])> >>> x509_name(OrderedDict([('C', 'AT'), ('CN', 'example.com')])) <Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value='AT')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='example.com')>])>