Adventures in X.509: The Utterly Ignored nameConstraints

April 08, 2012 at 09:16 PM | OpenSSL, MintChip | View Comments

In yesterday's post looking at MintChip's hosted API's crypto, I noticed that MintChip's certificate authority does not have any name constraints. This was surprising, because this seemed like a simple and obvious issue with an otherwise well designed system... But after some experimentation, it seems that OpenSSL simply ignores nameConstraints, so they would be mostly worthless even if they were used.

Adding nameConstraints to MintChip's CA

This can be seen by adding some nameConstraints to MintChip's certificate authority and re-signing it using a new, trusted, certificate authority.

Note that all files referenced here can be found at /uploads/adventures_in_x509_ignored_nameconstraints/.

A new certificate authority is created and the add_name_constraints.py to add nameConstraints to MintChip's CA's certiificate, creating mintchip_with_constraints.crt, which will be signed by the new CA:

$ openssl req -new -x509 -keyout trusted_ca.pem -out trusted_ca.pem -nodes
...
Organizational Unit Name (eg, section) []: New Trusted Authority
...
$ ./add_name_constraints.py trusted_ca.pem mintchip_ca.crt > mintchip_with_constraints.crt
$ openssl x509 -noout -text -in mintchip_with_constraints.crt
Data:
    ...
    Issuer: OU=New Trusted Authority
    Subject: CN=Remote MintChip Certificate Authority, OU=Remote MintChip, O=Royal Canadian Mint, C=CA
    ...
    X509v3 extensions:
        ...
        X509v3 Name Constraints: critical
            Permitted:
              DNS:does_not_exist.com

Testing the Constraints

The constraints can now be tested using OpenSSL's s_client:

$ cat trusted_ca.pem mintchip_with_constraints.crt > ca_list.crt
$ openssl s_client -CAfile ca_list.crt -connect remote.mintchipchallenge.com:443
CONNECTED(00000003)
depth=2 /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/OU=New Trusted Authority
verify return:1
depth=1 /CN=Remote MintChip Certificate Authority/OU=Remote MintChip/O=Royal Canadian Mint/C=CA
verify return:1
depth=0 /CN=remote.mintchipchallenge.com/OU=Remote MintChip Server/O=Royal Canadian Mint/C=CA
verify return:1
...
SSL-Session:
    ...
    Verify return code: 0 (ok)
---

Note that:

  1. Our "New Trusted Authority" is used to verify MintChip's Certificate Authority (first six lines).
  2. The SSL session is successfully verified (the last line).

This suggests that OpenSSL is not checking name constraints.

Double Checking

To double check that our certificat authority is actually being consulted, the same test can be run, except using only the mintchip_with_constraints.crt certificate:

$ openssl s_client -CAfile mintchip_with_constraints.crt \
    -connect remote.mintchipchallenge.com:443
CONNECTED(00000003)
depth=1 /CN=Remote MintChip Certificate Authority/OU=Remote MintChip/O=Royal Canadian Mint/C=CA
verify error:num=2:unable to get issuer certificate
issuer= /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/OU=New Trusted Authority
verify return:0
...
SSL-Session:
    ...
    Verify return code: 2 (unable to get issuer certificate)
---

The verify now fails, so it's fairly certain that the certificate chain is working as expected.

Adding dirName nameConstraints

Applications conforming to this profile MUST be able to process name constraints that are imposed on the directoryName name form and SHOULD be able to process name constraints that are imposed on the rfc822Name, uniformResourceIdentifier, dNSName, and iPAddress name forms.

On closer inspection of RFC 5280, it turns out that applications are only required to check name constraints which are imposed on directory names.

This poses a slight challenge, as (for various reasons) pyOpenSSL cannot create a nameConstraints extension which imposes a dirName constraint[0].

The OpenSSL command line tools can be used to create a new certificate which does contain nameConstraints on a dirName (see the definition in name_constraints_on_dirNames.cfg), then pyOpenSSL can load that certificate and copy the constraints into the target:

$ openssl req -new -x509 -nodes -config name_constraints_on_dirNames.cfg \
>   -out dirName_constraints.crt
...
$ ./add_name_constraints.py trusted_ca.pem mintchip_ca.crt \
>   dirName_constraint.crt > mintchip_with_dirName_constraints.crt
...
$ openssl x509 -noout -text -in mintchip_with_dirName_constraints.crt
Certificate:
    ...
    X509v3 extensions:
        ...
        X509v3 Name Constraints: critical
            Permitted:
              DirName: CN = bad_common_name

Nifty!

[0]: OpenSSL expects the value of the dirName=... to be a name which is looked up in the configuration database (ex, nameConstraints = permitted;dirName=some_section_), but pyOpenSSL does not provide a configuration database.

Testing the dirName Constraint

Testing this new certificate with s_client:

$ cat trusted_ca.pem mintchip_with_dirName_constraints.crt \
>   > ca_list_with_dirName_constraints.crt
$ openssl s_client -CAfile ca_list_with_dirName_constraints.crt \
>   -connect remote.mintchipchallenge.com:443
CONNECTED(00000003)
depth=2 /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/OU=New Trusted Authority
verify return:1
depth=1 /CN=Remote MintChip Certificate Authority/OU=Remote MintChip/O=Royal Canadian Mint/C=CA
verify return:1
depth=0 /CN=remote.mintchipchallenge.com/OU=Remote MintChip Server/O=Royal Canadian Mint/C=CA
verify return:1
...
SSL-Session:
    ...
    Verify return code: 0 (ok)
---

Still no love. It sure looks like OpenSSL simply ignores the nameConstraints field on certificate authorities.

Post Script

On Twitter, Dan Kaminsky was kind enough to "confirm" my suspicions:

And suggests:

tl;dr

OpenSSL does not check nameConstraints. Yay.