Skip to content

Commit

Permalink
fix parsing of ASN.1 OtherNames from the command line (inspired by #75)
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiasertl committed Oct 5, 2021
1 parent 2f39322 commit 067dd73
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 9 deletions.
7 changes: 6 additions & 1 deletion ca/django_ca/tests/tests_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,12 @@ def test_othername(self) -> None:
"""Test parsing an otherName name."""
self.assertEqual(
parse_general_name("otherName:2.5.4.3;UTF8:example.com"),
x509.OtherName(NameOID.COMMON_NAME, b"example.com"),
x509.OtherName(NameOID.COMMON_NAME, b"\x0c\x0bexample.com"),
)
# try to trick with delimiters
self.assertEqual(
parse_general_name("otherName:2.5.4.3;UTF8:example.com;wrong:something"),
x509.OtherName(NameOID.COMMON_NAME, b"\x0c\x1bexample.com;wrong:something"),
)

def test_unicode_domains(self) -> None:
Expand Down
11 changes: 8 additions & 3 deletions ca/django_ca/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import idna

from asn1crypto.core import OctetString
from asn1crypto.core import UTF8String
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
Expand Down Expand Up @@ -855,16 +856,20 @@ def parse_general_name(name: ParsableGeneralName) -> x509.GeneralName:
elif typ == "rid":
return x509.RegisteredID(x509.ObjectIdentifier(name))
elif typ == "othername":
match = re.match("(.*);(.*):(.*)", name)
match = re.match("(.*?);(.*?):(.*)", name)
if match is not None:
oid, asn_typ, val = match.groups()
if asn_typ == "UTF8":
parsed_value = val.encode("utf-8")

# Get DER representation of the value for x509.OtherName()
if asn_typ in ("UTF8", "UTF8String"):
parsed_value = UTF8String(val).dump()
elif asn_typ == "OctetString":
parsed_value = OctetString(bytes(bytearray.fromhex(val))).dump()
else:
raise ValueError(f"Unsupported ASN type in otherName: {asn_typ}")

# NOTE: cryptography docs are not really clear on what kind of bytes x509.OtherName() expects, but
# the test suite explicitly use b"derdata" as value, indicating DER encoded data.
return x509.OtherName(x509.ObjectIdentifier(oid), parsed_value)

raise ValueError(f"Incorrect otherName format: {name}")
Expand Down
1 change: 1 addition & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This version is currently under development.
* Ensure that a certificates ``issuer`` always matches the ``subject`` from the CA that signed it.
* Include a healthcheck script for uWSGI in the Docker image. Because the image is also shared for the
Celery worker, it is not enabled by default, but the docker-compose configuration enables it.
* Fix parsing of ASN.1 OtherNames from the command line.
* Add support for cryptography 35.0.0.
* Add support for idna 3.0, 3.1 and 3.2.

Expand Down
22 changes: 17 additions & 5 deletions docs/source/cli/certs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,44 @@ can update existing profiles and even add new ones.
Subject and alternative names
=============================

The Certificate's Subject (that is, it's CommonName) and the names given in the SubjectAlternativeName
The Certificate's Subject (that is, it's CommonName) and the names given in the ``SubjectAlternativeName``
extension define where the certificate is valid.

The CommonName is usually added to the ``subjectAltName`` extension as well and vice versa. This means that
The CommonName is usually added to the ``SubjectAlternativeName`` extension as well and vice versa. This means that
these two will give the same CommonName and ``subjectAltName``:

.. code-block:: console
$ python manage.py sign_cert --subject /C=AT/.../CN=example.com
$ python manage.py sign_cert --alt example.com
A given CommonName is only added to the SubjectAlternativeName extension if it is a valid :ref:`name
A given CommonName is only added to the ``SubjectAlternativeName`` extension if it is a valid :ref:`name
<names_on_cli>`. If you give multiple names via ``--alt`` but no CommonName, the first one will be used as
CommonName. Names passed via ``--alt`` are parsed as :ref:`names <names_on_cli>`, so you can also use e.g.:

.. code-block:: console
$ python manage.py sign_cert --alt IP:127.0.0.1
You can also disable adding the CommonName as ``subjectAltName``:
You can also disable adding the CommonName as ``subjectAlternativeName``:

.. code-block:: console
$ python manage.py sign_cert --cn-not-in-san --subject /C=AT/.../CN=example.com --alt=example.net
... this will only have "example.net" but not example.com as ``subjectAltName``.
... this will only have "example.net" but not example.com as ``subjectAlternativeName``.

Advanced subject alternative names
----------------------------------

You can add ``OtherName`` values to ``SubjectAlternativeName`` via the same format used by OpenSSL described
in :manpage:`ASN1_GENERATE_NCONF(3SSL)`:

.. code-block:: console
$ python manage.py sign_cert --subject /CN=example.com --alt="otherName:1.3.6.1.4.1.311.20.2.3;UTF8:dummy@domain.tld"
Note that currently only UTF8 strings are supported.

Using profiles
==============
Expand Down

0 comments on commit 067dd73

Please sign in to comment.