cfssl icon indicating copy to clipboard operation
cfssl copied to clipboard

domainComponent (DC) RDN not included in subject of signed certificate

Open jgehrcke opened this issue 7 years ago • 5 comments

When cfssl receives a certifcate signing request (CSR) which contains one or multiple domainComponent (DC) relative distinguished names (RDNs) in the subject, then the subject of the signed certificate emitted by cfssl does not contain those DC RDNs.

Subject text representation of an example CSR:

$ openssl req -text -noout -verify -in client_cert.csr 
verify OK
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: CN=readonly, DC=los-pollos, DC=io
[...]

Note the subject: CN=readonly, DC=los-pollos, DC=io

After sending this CSR to the cfssl /sign endpoint, this is the text representation of the cert emitted by cfssl (issuer hidden):

$ openssl x509 -in client.crt -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            32:77:f7:7b:9f:e9:f6:a8:24:51:44:66:14:30:3d:38:8d:7b:f4:96
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=**, ST=**, L=**, O=**, CN=**
        Validity
            Not Before: Jul 13 13:58:00 2016 GMT
            Not After : Jul 11 13:58:00 2026 GMT
        Subject: CN=readonly
[...]

Note how the subject DN was reduced to CN=readonly, missing the DC (domainComponent) RDNs.

Maybe this is a cfssl configuration issue, in which case I'd be happy to receive pointers. Otherwise, this violates RFC 5280 section 4.1.2.4. (page 20):

As noted above, distinguished names are composed of attributes. This specification does not restrict the set of attribute types that may appear in names. However, conforming implementations MUST be prepared to receive certificates with issuer names containing the set of attribute types defined below. This specification RECOMMENDS support for additional attribute types. [...]

Standard sets of attributes have been defined in the X.500 series of specifications [X.520]. Implementations of this specification MUST be prepared to receive the following standard attribute types in issuer and subject (Section 4.1.2.6) names: [...] In addition, implementations of this specification MUST be prepared to receive the domainComponent attribute, as defined in [RFC4519].

Note especially the emphasized text.

Below you find a self-contained MWE for reproducing.

MWE method:

  • Generate CSR is using Python's cryptography (w/ OpenSSL bindings).
  • Submit CSR to a cfssl instance via requests, receive PEM-serialized signed cert.
  • Deserialize cert using cryptography, and print all subject attributes.

MWE output:

Subject attributes:
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='readonly')>

MWE code:

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID

import requests


cryptbackend = default_backend()


# Create certificate sining request (CSR).
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=cryptbackend
    )
builder = x509.CertificateSigningRequestBuilder()
builder = builder.subject_name(x509.Name([
    x509.NameAttribute(NameOID.COMMON_NAME, 'readonly'),
    x509.NameAttribute(NameOID.DOMAIN_COMPONENT, 'los-pollos'),
    x509.NameAttribute(NameOID.DOMAIN_COMPONENT, 'io')
    ]))
builder = builder.add_extension(
    x509.BasicConstraints(ca=False, path_length=None),
    critical=True,
).add_extension(
    x509.KeyUsage(
        digital_signature=True,
        content_commitment=False,
        key_encipherment=True,
        data_encipherment=False,
        key_agreement=False,
        key_cert_sign=False,
        crl_sign=False,
        encipher_only=False,
        decipher_only=False
    ),
    critical=False
).add_extension(
    x509.SubjectAlternativeName([x509.DNSName('los-pollos.io')]),
    critical=False
).add_extension(
    x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
    critical=False
)

csr = builder.sign(private_key, hashes.SHA256(), cryptbackend)
csr_pem = csr.public_bytes(encoding=serialization.Encoding.PEM).decode('ascii')

print("CSR:")
print(csr_pem)


# Send CSR to cfssl.
data = {
    'certificate_request': csr_pem,
    'profile': '',
    'crl_override': '',
    'label': ''
    }

r = requests.post(
    url='http://cfsslhost/ca/api/v2/sign',
    headers={'Accept': 'application/json', 'Accept-Charset': 'utf-8'},
    json=data
    )

cert_pem = r.json()['result']['certificate'].strip() + '\n'

print("Cert:")
print(cert_pem)

cert = x509.load_pem_x509_certificate(
    cert_pem.encode('ascii'), cryptbackend)

print("Subject attributes:")
for attr in cert.subject:
    print(attr)

jgehrcke avatar Jul 14 '16 10:07 jgehrcke