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.


Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this:

def default(self, o):
        iterable = iter(o)
    except TypeError:
        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=, name=stateOrProvinceName)>: 'ST', <ObjectIdentifier(oid=, name=localityName)>: 'L', <ObjectIdentifier(oid=, name=countryName)>: 'C', <ObjectIdentifier(oid=1.2.840.113549.1.9.1, name=emailAddress)>: 'emailAddress', <ObjectIdentifier(oid=, name=commonName)>: 'CN', <ObjectIdentifier(oid=, name=organizationalUnitName)>: 'OU', <ObjectIdentifier(oid=, name=organizationName)>: 'O'}

Map OID objects to IDs used in subject strings


Add colons after every second digit.

This function is used in functions to prettify serials.

>>> add_colons('teststring')

Format a single general name.

>>> import ipaddress
>>> format_general_name(x509.DNSName(''))
>>> format_general_name(x509.IPAddress(ipaddress.IPv4Address('')))

Format a list of general names.

>>> import ipaddress
>>> format_general_names([x509.DNSName('')])
>>> format_general_names([x509.IPAddress(ipaddress.IPv4Address(''))])
>>> format_general_names([x509.DirectoryName(
...     x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, '')]))])
>>> format_general_names([x509.DNSName(''), x509.DNSName('')])

Convert a subject into the canonical form for distinguished names.

This function does not take care of sorting the subject in any meaningful order.


>>> format_name([('CN', ''), ])
>>> format_name([('CN', ''), ('O', "My Organization"), ])
'/ Organization'
django_ca.utils.get_cert_builder(expires, now=None)[source]

Get a basic X509 cert object.


expires : datetime

When this certificate will expire.

now : datetime

The functions notion of “now”, used for testing.


Get kwargs suitable for get_cert X509 keyword arguments from the given profile.


Create a hex-representation of the given serial.

>>> int_to_hex(12345678)

Return True if num is a power of 2.

>>> is_power2(4)
>>> is_power2(3)

Validate that a TextField contains one valid URL per line.


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('')
>>> parse_general_name('*')
>>> parse_general_name('')
>>> parse_general_name('')
>>> parse_general_name('')
>>> parse_general_name('fd00::1')
>>> parse_general_name('/')  
<DirectoryName(value=<Name([<NameAttribute(oid=<ObjectIdentifier(oid=, name=commonName)>,

The default fallback is to assume a DNSName. If this doesn’t work, an exception will be raised:

>>> parse_general_name('`*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('')
>>> parse_general_name('URI:')
>>> parse_general_name('dirname:/')  
<DirectoryName(value=<Name([<NameAttribute(oid=<ObjectIdentifier(oid=, name=commonName)>,

Some more exotic values can only be generated by using this prefix:

>>> parse_general_name('rid:')
<RegisteredID(value=<ObjectIdentifier(oid=, name=commonName)>)>
>>> parse_general_name('otherName:,')
<OtherName(type_id=<ObjectIdentifier(oid=, name=commonName)>, value=b'')>

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

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/ The function does its best to be lenient on deviations from the format, object identifiers are case-insensitive (e.g. cn is the same as CN, whitespace at the start and end is stripped and the subject does not have to start with a slash (/).

>>> parse_name('/')
OrderedDict([('CN', '')])
>>> parse_name('c=AT/l= Vienna/o="ex org"/')
OrderedDict([('C', 'AT'), ('L', 'Vienna'), ('O', 'ex org'), ('CN', '')])

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"/')
OrderedDict([('L', 'Vienna / District'), ('emailAddress', '')])
>>> parse_name('/C=AT/') == parse_name('/')

Due to the magic of NAME_RE, the function even supports quoting strings and including slashes, so strings like /OU="Org / Org Unit"/ will work as expected.

>>> parse_name('L="Vienna / District"/')
OrderedDict([('L', 'Vienna / District'), ('CN', '')])

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"/')
OrderedDict([('L', 'Vienna'), ('CN', '')])

Examples of where this string is used are:

# openssl req -new -key priv.key -out csr -utf8 -batch -sha256 -subj '/C=AT/'
# openssl x509 -in cert.pem -noout -subject -nameopt compat

Returns an itemized dictionary in the correct order for a x509 subject.


Validate an email address.

This function raises ValueError if the email address is not valid.

>>> validate_email('')
>>> validate_email('foo@bar com')
Traceback (most recent call last):
ValueError: Invalid domain: bar com

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 a dict (preferrably an OrderedDict) is also supported.

>>> x509_name('/C=AT/')  
<Name([<NameAttribute(oid=<ObjectIdentifier(oid=, name=countryName)>, value='AT')>,
       <NameAttribute(oid=<ObjectIdentifier(oid=, name=commonName)>, value='')>])>
>>> x509_name([('C', 'AT'), ('CN', '')])  
<Name([<NameAttribute(oid=<ObjectIdentifier(oid=, name=countryName)>, value='AT')>,
       <NameAttribute(oid=<ObjectIdentifier(oid=, name=commonName)>, value='')>])>
>>> x509_name(OrderedDict([('C', 'AT'), ('CN', '')]))  
<Name([<NameAttribute(oid=<ObjectIdentifier(oid=, name=countryName)>, value='AT')>,
       <NameAttribute(oid=<ObjectIdentifier(oid=, name=commonName)>, value='')>])>
>>> x509_name(OrderedDict([('C', 'AT'), ('CN', '')]))  
<Name([<NameAttribute(oid=<ObjectIdentifier(oid=, name=countryName)>, value='AT')>,
       <NameAttribute(oid=<ObjectIdentifier(oid=, name=commonName)>, value='')>])>