django_ca.utils - utility functions
Reusable utility functions used throughout django-ca.
- django_ca.utils.GENERAL_NAME_RE = re.compile('^(email|URI|IP|DNS|RID|dirName|otherName):(.*)', re.IGNORECASE)
Regular expression to match general names.
- django_ca.utils.SERIAL_RE = re.compile('^([0-9A-F][0-9A-F]:?)+[0-9A-F][0-9A-F]?$')
Regular expression matching certificate serials as hex
- django_ca.utils.add_colons(value: str, pad: str = '0') str[source]
Add colons after every second digit.
This function is used in functions to prettify serials.
>>> add_colons('teststring') 'te:st:st:ri:ng'
- Parameters:
- valuestr
The string to add colons to
- padstr, optional
If not an empty string, pad the string so that the last element always has two characters. The default is
"0".
- django_ca.utils.bytes_to_hex(value: bytes) str[source]
Convert a bytes array to hex.
>>> bytes_to_hex(b'test') '74:65:73:74'
- django_ca.utils.check_name(name: Name) Name[source]
Check if name is a valid x509 Name.
This method raises
ValueErrorif the CommonName contains an empty value or if any attribute not inMULTIPLE_OIDSoccurs multiple times.The method returns the name unchanged for convenience.
- django_ca.utils.format_general_name(name: GeneralName) str[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_name_rfc4514(subject: Name | RelativeDistinguishedName) str[source]
Format the given (relative distinguished) name as RFC4514 compatible string.
This function deviates from RFC 4514 by displaying the name attributes as they appear in the certificate, and not in reverse order (which is not used anywhere else). It also adds OID name mappings from
NAME_OID_NAMESto the output string.>>> from cryptography.x509.oid import NameOID >>> format_name_rfc4514( ... x509.Name( ... [ ... x509.NameAttribute(NameOID.COUNTRY_NAME, "AT"), ... x509.NameAttribute(NameOID.COMMON_NAME, "example.com"), ... x509.NameAttribute(NameOID.EMAIL_ADDRESS, "user@example.com"), ... ] ... ) ... ) 'C=AT,CN=example.com,emailAddress=user@example.com'
- django_ca.utils.format_other_name(other_name: OtherName) str[source]
Format a
OtherNameto a string.
- django_ca.utils.generate_private_key(key_size: int | None, key_type: Literal['DSA'], elliptic_curve: EllipticCurve | None) DSAPrivateKey[source]
- django_ca.utils.generate_private_key(key_size: int | None, key_type: Literal['RSA'], elliptic_curve: EllipticCurve | None) RSAPrivateKey
- django_ca.utils.generate_private_key(key_size: int | None, key_type: Literal['EC'], elliptic_curve: EllipticCurve | None) EllipticCurvePrivateKey
- django_ca.utils.generate_private_key(key_size: int | None, key_type: Literal['Ed25519'], elliptic_curve: EllipticCurve | None) Ed25519PrivateKey
- django_ca.utils.generate_private_key(key_size: int | None, key_type: Literal['Ed448'], elliptic_curve: EllipticCurve | None) Ed448PrivateKey
Generate a private key.
This function assumes that you called
validate_private_key_parameters()on the input values and does not do any sanity checks on its own.- Parameters:
- key_sizeint
The size of the private key. The value is ignored if
key_typeis not"DSA"or"RSA".- key_type{‘RSA’, ‘DSA’, ‘EC’, ‘Ed25519’, ‘Ed448’}
The type of the private key.
- elliptic_curve
EllipticCurve An elliptic curve to use for EC keys. This parameter is ignored if
key_typeis not"EC". Defaults to the CA_DEFAULT_ELLIPTIC_CURVE.
- Returns:
- key
A private key of the appropriate type.
- django_ca.utils.get_cert_builder(not_after: datetime, serial: int | None = None) CertificateBuilder[source]
Get a basic X.509 certificate builder object.
Changed in version 2.1.0: The
expiresparameter was renamed tonot_after.- Parameters:
- not_afterdatetime
When this certificate is supposed to expire, as a timezone-aware datetime object.
- serialint, optional
Serial for the certificate. If not passed, a serial will be randomly generated using
random_serial_number().
- django_ca.utils.get_crl_cache_key(serial: str, encoding: Encoding, *, only_contains_ca_certs: bool, only_contains_user_certs: bool, only_contains_attribute_certs: bool, only_some_reasons: Iterable[ReasonFlags] | None) str[source]
Get the cache key for a CRL with the given parameters.
Note that this function does not assert the consistency of only_contains_ca_certs and only_contains_user_certs and only_contains_attribute_certs, as this is expected to already be done by the caller.
Changed in version 2.1.0: The scope parameter was removed. Use only_contains_ca_certs, only_contains_user_certs and only_contains_attribute_certs instead.
- django_ca.utils.get_private_key_type(private_key: Ed25519PrivateKey | Ed448PrivateKey | RSAPrivateKey | DSAPrivateKey | EllipticCurvePrivateKey) Literal['RSA', 'DSA', 'EC', 'Ed25519', 'Ed448'][source]
Get the private key type as string from a given private key.
- django_ca.utils.hex_to_bytes(value: str) bytes[source]
Convert a hex number to bytes.
This should be the inverse of
bytes_to_hex().>>> hex_to_bytes('74:65:73:74') b'test'
- django_ca.utils.hex_to_int(value: str) int[source]
Create a hex number to an integer.
his should be the inverse of
int_to_hex().>>> hex_to_int('BC614E') 12345678
- django_ca.utils.int_to_hex(i: int) str[source]
Create a hex-representation of the given serial.
>>> int_to_hex(12345678) 'BC614E'
- django_ca.utils.merge_x509_names(base: Name, update: Name) Name[source]
Merge two
x509.Nameinstances.This function will return a new
x509.Namebased on base, with the attributes from update added. If an attribute type occurs in both names, the one from update take precedence.The resulting name will be sorted based on CA_DEFAULT_NAME_ORDER, regardless of order of base or update.
Example:
>>> from cryptography import x509 >>> from cryptography.x509.oid import NameOID >>> base = x509.Name([ ... x509.NameAttribute(NameOID.COUNTRY_NAME, "AT"), ... x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Example Org"), ... x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Example Org Unit"), ... ]) >>> update = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, 'example.com')]) >>> merge_x509_names(base, update) <Name(C=AT,O=Example Org,OU=Example Org Unit,CN=example.com)>
- django_ca.utils.name_for_display(name: Name | RelativeDistinguishedName) list[tuple[str, str]][source]
Convert a
NameorRelativeDistinguishedNameinto a list of key/value pairs for display.This function is used as a helper function to loop over the elements of a name to prepare them for consistent display.
The function converts the OID into a readable string (e.g. “commonName (CN)”) with any unknown OIDs converted to a dotted string. If the value is not a string, it is converted to a hex string.
>>> from cryptography.x509.oid import NameOID >>> name_for_display(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, 'example.com')])) [('commonName (CN)', 'example.com')]
- django_ca.utils.parse_encoding(value: str) Encoding[source]
Parse a value to a valid encoding.
The passed value is a string describing the encoding, either
"PEM"or"DER"."ASN1"is an alias for"DER".>>> parse_encoding("PEM") <Encoding.PEM: 'PEM'> >>> parse_encoding("ASN1") <Encoding.DER: 'DER'>
- django_ca.utils.parse_general_name(name: str) GeneralName[source]
Parse a general name from user input.
Changed in version 2.3.0: name can no longer by a GeneralName instance.
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)>
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): ... ValueError: Invalid domain: foo..bar`*123: ...
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(CN=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'\x0c\x0bexample.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_rfc4514(value: str) Name[source]
Parse an RFC 4514 formatted string into a
Name.This function is intended to be the inverse of
format_name_rfc4514(), and will also parse the name in the order as given in the string and understands the same OID mappings.>>> parse_name_rfc4514("CN=example.com") <Name(CN=example.com)> >>> parse_name_rfc4514("C=AT,O=MyOrg,OU=MyOrgUnit,CN=example.com") <Name(C=AT,O=MyOrg,OU=MyOrgUnit,CN=example.com)>
- django_ca.utils.parse_other_name(name: str) OtherName[source]
Parse a formatted
OtherNameinstance.
- django_ca.utils.sanitize_serial(value: str) str[source]
Sanitize a serial provided by user/untrusted input.
This function is intended to be used to get a serial as used internally by django-ca from untrusted user input. Internally, serials are stored in upper case and without
:and leading zeros, but user output adds at least:.Examples
>>> sanitize_serial('01:aB') '1AB'
- django_ca.utils.validate_hostname(hostname: str, allow_port: bool = False) str[source]
Validate a hostname, optionally with a given port.
>>> validate_hostname('example.com') 'example.com' >>> validate_hostname('example.com:8000', allow_port=True) 'example.com:8000'
- Parameters:
- hostnamestr
The hostname to validate.
- allow_portbool, optional
If
True, the hostname can also contain an optional port number, e.g. “example.com:8000”.
- Raises:
- ValueError
If hostname or port are not valid.
- django_ca.utils.validate_private_key_parameters(key_type: Literal['DSA', 'RSA'], key_size: int | None, elliptic_curve: EllipticCurve | None) tuple[int, None][source]
- django_ca.utils.validate_private_key_parameters(key_type: Literal['EC'], key_size: int | None, elliptic_curve: EllipticCurve | None) tuple[None, EllipticCurve]
- django_ca.utils.validate_private_key_parameters(key_type: Literal['Ed448', 'Ed25519'], key_size: int | None, elliptic_curve: EllipticCurve | None) tuple[None, None]
Validate parameters for private key generation.
This function can be used to fail early if invalid parameters are passed, before the private key is generated.
>>> validate_private_key_parameters("RSA", 4096, None) (4096, None) >>> validate_private_key_parameters("Ed448", 4096, None) # Ed448 does not care about the key size Traceback (most recent call last): ... ValueError: Key size is not supported for Ed448 keys. >>> validate_private_key_parameters('RSA', 4000, None) Traceback (most recent call last): ... ValueError: 4000: Key size must be a power of two
- django_ca.utils.validate_public_key_parameters(key_type: Literal['RSA', 'DSA', 'EC', 'Ed25519', 'Ed448'], algorithm: SHA224 | SHA256 | SHA384 | SHA512 | SHA3_224 | SHA3_256 | SHA3_384 | SHA3_512 | None) SHA224 | SHA256 | SHA384 | SHA512 | SHA3_224 | SHA3_256 | SHA3_384 | SHA3_512 | None[source]
Validate parameters for signing a certificate.
This function can be used to fail early if invalid parameters are passed.
>>> validate_public_key_parameters("RSA", hashes.SHA256()) <cryptography.hazmat.primitives.hashes.SHA256 object at 0x...> >>> validate_public_key_parameters("Ed448", None) >>> validate_public_key_parameters("Ed448", hashes.SHA256()) Traceback (most recent call last): ... ValueError: Ed448 keys do not allow an algorithm for signing.