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 8 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

Thanks for the MWE. This is due to how DNs are handled in CFSSL; names are constructed from the other fields in the pkix.Name structure provided by the Go standard lib. While technically cfssl is conformant (it won't error when reading certificates and CSRs with DCs), there's no mechanism provided to pass these on from a CSR to a certificate. This will take some work to figure out how to adapt it into our current system properly.

kisom avatar Jul 18 '16 22:07 kisom

Hi @kisom, im looking into the same issue here to support signing intermediate certificates for active directory use which requires DC in subject. Is this still on the roadmap for 1.3? If not any thoughts you can share around implementation would speed up my work.

jacob-salassi avatar Mar 15 '18 16:03 jacob-salassi

@jacob-salassi We already shipped 1.3, but it's probably going to require some work in the signer package (e.g. here) as well as an update to config.CSRWhitelist. I don't know that I have the bandwidth in the near future to work on this, though.

kisom avatar Mar 15 '18 17:03 kisom

@kisom Ive been focused on csr.go up to this point, my thought is when creating the pkix name struct the DC components needs to go in name.ExtraNames as an ASN.1 oid/values since the go x509 lib itself has no explicit dc support either.

I'll look into the signer piece.

jacob-salassi avatar Mar 16 '18 14:03 jacob-salassi

Hi, I'm facing the same (8 years old) issue. I set DC and generated a self-signed certificate but DC does not show up there.

HerrMuellerluedenscheid avatar Apr 26 '24 07:04 HerrMuellerluedenscheid