Monthly Archives: May 2014

Dogtag profile definitions

In the previous post I began an exploration of Dogtag’s certificate profiles feature by looking at the certificate request process and the relationship between PKCS #10 CSRs and Dogtag certificate enrolment requests, which are used to submit CSRs in the context of a profile. In this post we will look at how Dogtag profiles are defined and learn a little about how Dogtag uses them in the certificate enrolment process.

Each instance of Dogtag or Certificate Server starts out with a default set of profiles; these are found in the Dogtag instance directory in /var/lib/pki/<instance-name>/ca/profiles/ca/. There are dozens of profiles, but since we are already familiar with caServerCert let’s open up caServerCert.cfg and have a look:

desc=This certificate profile is for enrolling server certificates.
visible=true
enable=true
enableBy=admin
auth.class_id=
name=Manual Server Certificate Enrollment
input.list=i1,i2
input.i1.class_id=certReqInputImpl
input.i2.class_id=submitterInfoInputImpl
output.list=o1
output.o1.class_id=certOutputImpl
policyset.list=serverCertSet
policyset.serverCertSet.list=1,2,3,4,5,6,7,8
policyset.serverCertSet.1.constraint.class_id=subjectNameConstraintImpl
policyset.serverCertSet.1.constraint.name=Subject Name Constraint
policyset.serverCertSet.1.constraint.params.pattern=.*CN=.*
policyset.serverCertSet.1.constraint.params.accept=true
policyset.serverCertSet.1.default.class_id=userSubjectNameDefaultImpl
policyset.serverCertSet.1.default.name=Subject Name Default
policyset.serverCertSet.1.default.params.name=
policyset.serverCertSet.2.constraint.class_id=validityConstraintImpl
policyset.serverCertSet.2.constraint.name=Validity Constraint
policyset.serverCertSet.2.constraint.params.range=720
policyset.serverCertSet.2.constraint.params.notBeforeCheck=false
policyset.serverCertSet.2.constraint.params.notAfterCheck=false
policyset.serverCertSet.2.default.class_id=validityDefaultImpl
policyset.serverCertSet.2.default.name=Validity Default
policyset.serverCertSet.2.default.params.range=720
policyset.serverCertSet.2.default.params.startTime=0
policyset.serverCertSet.3.constraint.class_id=keyConstraintImpl
policyset.serverCertSet.3.constraint.name=Key Constraint
policyset.serverCertSet.3.constraint.params.keyType=-
policyset.serverCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp384,nistp521
policyset.serverCertSet.3.default.class_id=userKeyDefaultImpl
policyset.serverCertSet.3.default.name=Key Default
... (on it goes, through to policyset.serverCertSet.8.*)

There is an obvious relationship between the profile configuration, the certificate enrolment request template retrieved via pki cert-request-profile-show and the behaviour of the CA when submitting or approving enrolment requests. For example, there are two inputs: one for a certificate request (PKCS #10 CSR) and one for submitter information. These are the same two inputs we had to fill out in the XML certificate enrolment request template. And there are constraint declarations; again, we have observed the effects of these declarations when non-conformant enrolment requests were rejected.

Let’s break down the profile configuration. The top-level settings such as name, desc and enable are self-explanatory. Moving down, we see the input.list key specifying the list i1,i2, followed by keys input.i1.class_id and input.i2.class_id. This pattern of foo.list=f1,f2,.. followed by foo.f1..., foo.f2..., and so on also occurs further down for output and policyset, and seems to provide a simple, deterministic way to read ordered declarations from the profile configuration.

The class_id key also occurs in the output and policy set contexts. To what does its value refer? The file /etc/pki/<instance-name>/ca/registry.cfg holds the answer, mapping the values in the profile configuration to Java classes. These classes implement interfaces relevant to their role in the profile system: IProfileInput, IProfileOutput, IPolicyConstraint for inputs, outputs and policy constraints, and IPolicyDefault and ICertInfoPolicyDefault for policy defaults.

Whilst inputs and outputs have no further configuration beyond the class_id, policy set constraints and defaults are parameterised, with each class offering named parameters that relate to its function. For example, subjectNameConstraintImpl has parameters pattern (a regular expression) and accept (boolean; I infer that this controls whether to accept or reject a CSR on match). When a profile is used, e.g. to generate an enrolment request template, submit an enrolment request, or to generate a certificate, Dogtag instantiates the classes according to the profile configuration and uses their behaviours to carry out the requested action – or to decide how or whether to carry it out.

Armed with an understanding of how profiles are configured, let’s try and define a new profile. My first action was to simply copy caServerCert.cfg to caServerCertTest.cfg (ensuring the new file can be read by pkiuser). The name and desc values were changed and the subject name constraint pattern was updated to .*CN=test.* to make it easy to verify that the new profile is being used correctly. Let’s restart the server (the service name depends on the Dogtag instance name) and see if Dogtag has learned about the new profile:

$ sudo systemctl restart pki-tomcatd@pki-tomcat.service
$ pki cert-request-profile-show caServerCertTest
BadRequestException: Cannot provide enrollment template for profile `caServerCertTest`.  Profile not found

There must be more to configure. A thorough search turns up a few references to caServerCert in /etc/pki/<instance-name>/ca/CS.cfg:

...
profile.caServerCert.class_id=caEnrollImpl
profile.caServerCert.config=/var/lib/pki/<instance-name>/ca/profiles/ca/caServerCert.cfg
...
profile.list=caUserCert,caECUserCert,...,caServerCert,...
...

We have found what appears to be the canonical list of profiles and furthermore can see that the full path to the profile is configurable and that each profile specifies a class_id. The class_id values that can be used here appear in the same registry.cfg we learned about above. The classes referred to implement the IProfile interface.

After adding the profile.caServerCertTest configuration, appending caServerCertTest to profile.list and restarting Dogtag again, we can finally use our new profile:

$ pki cert-request-profile-show caServerCertTest
--------------------------------------------------
Enrollment Template for Profile "caServerCertTest"
--------------------------------------------------
  Profile ID: caServerCertTest
  Renewal: false

  Input ID: i1
  Name: Certificate Request Input
  Class: certReqInputImpl

    Attribute Name: cert_request_type
    Attribute Description: Certificate Request Type
    Attribute Syntax: cert_request_type

    Attribute Name: cert_request
    Attribute Description: Certificate Request
    Attribute Syntax: cert_request

  Input ID: i2
  Name: Requestor Information
  Class: submitterInfoInputImpl

    Attribute Name: requestor_name
    Attribute Description: Requestor Name
    Attribute Syntax: string

    Attribute Name: requestor_email
    Attribute Description: Requestor Email
    Attribute Syntax: string

    Attribute Name: requestor_phone
    Attribute Description: Requestor Phone
    Attribute Syntax: string

Adding the --output <filename> argument to the above command downloads the certificate enrolment request template for our new caServerCertTest profile. Using it to submit a CSR with a subject common name (CN) not starting with test. results in summary rejection as hoped, and submission succeeds when the CN does satisfy our constraint.

In the next post we’ll dive into some code to look at how inputs, constraints and defaults are actually implemented, and perhaps implement one or two of our own.

Dogtag certificate profiles – certificate requests

The certificate enrolment profiles feature of Dogtag PKI can be used to specify default values and constraints for X.509 certificate fields. This post explores Dogtag certificate profiles and their relationship with the PKCS #10 certificate signing request (CSR) format with a focus on signing request submission. Future posts in this series will focus on the Certificate Authority (CA) side of the profiles feature, and on modifying and defining profiles for specialised use cases.

Let us begin by generating a CSR. This occurs in isolation from Dogtag profiles or certificate enrolment, and is done using certutil (CSRs can also be generated with openssl req).

certutil -R -d .pki/nssdb -o no-CN.req -a -s 'C=AU, ST=Queensland, L=Brisbane, O=Red Hat'

The -o no-CN.req instructs certutil to output the CSR to a file, while -a specifies ASCII output. Note that the subject (given by -s) does not contain a common name (CN) component.

CSRs are submitted to Dogtag in the context of some certificate profile. Available profiles can be listed via pki cert-request-profile-find "", and the Certificate Enrolment Request template for a profile can be retrieved via pki cert-request-profile-show <profile ID> --output <filename>. Let’s have a look at the caServerCert profile template:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CertEnrollmentRequest>
    <ProfileID>caServerCert</ProfileID>
    <Renewal>false</Renewal>
    <SerialNumber></SerialNumber>
    <RemoteHost></RemoteHost>
    <RemoteAddress></RemoteAddress>
    <Input id="i1">
        <ClassID>certReqInputImpl</ClassID>
        <Name>Certificate Request Input</Name>
        <Attribute name="cert_request_type">
            <Value></Value>
            <Descriptor>
                <Syntax>cert_request_type</Syntax>
                <Description>Certificate Request Type</Description>
            </Descriptor>
        </Attribute>
        <Attribute name="cert_request">
            <Value></Value>
            <Descriptor>
                <Syntax>cert_request</Syntax>
                <Description>Certificate Request</Description>
            </Descriptor>
        </Attribute>
    </Input>
    <Input id="i2">
        <ClassID>submitterInfoInputImpl</ClassID>
        <Name>Requestor Information</Name>
        <Attribute name="requestor_name">
            <Value></Value>
            <Descriptor>
                <Syntax>string</Syntax>
                <Description>Requestor Name</Description>
            </Descriptor>
        </Attribute>
        <Attribute name="requestor_email">
            <Value></Value>
            <Descriptor>
                <Syntax>string</Syntax>
                <Description>Requestor Email</Description>
            </Descriptor>
        </Attribute>
        <Attribute name="requestor_phone">
            <Value></Value>
            <Descriptor>
                <Syntax>string</Syntax>
                <Description>Requestor Phone</Description>
            </Descriptor>
        </Attribute>
    </Input>
</CertEnrollmentRequest>

The template is XML, containing fields with attributes whose values are not yet specified. Filling out these attributes with the content of the CSR generated earlier along with some ancillary information, we end up with the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CertEnrollmentRequest>
    <ProfileID>caServerCert</ProfileID>
    <Renewal>false</Renewal>
    <SerialNumber></SerialNumber>
    <RemoteHost></RemoteHost>
    <RemoteAddress></RemoteAddress>
    <Input id="i1">
        <ClassID>certReqInputImpl</ClassID>
        <Name>Certificate Request Input</Name>
        <Attribute name="cert_request_type">
            <Value>pkcs10</Value>
            <Descriptor>
                <Syntax>cert_request_type</Syntax>
                <Description>Certificate Request Type</Description>
            </Descriptor>
        </Attribute>
        <Attribute name="cert_request">
                <Value>
MIIBhjCB8AIBADBHMRAwDgYDVQQKEwdSZWQgSGF0MREwDwYDVQQHEwhCcmlzYmFu
ZTETMBEGA1UECBMKUXVlZW5zbGFuZDELMAkGA1UEBhMCQVUwgZ8wDQYJKoZIhvcN
AQEBBQADgY0AMIGJAoGBAJvkY6CyMdY0u7hwFzfG9ZdajT+69bbRh1vqFIArGhhv
vL09Em2MrlAhQEKF6PuAcdED7U7ryoBByeXDRfivFwQS5W5msVBkA5gZ1i9LyH82
xULvkdnNFu6He8QnxLr8+bl/r9tdlktP/3k79hHmWRpqBtOqVKtBCwMqEdPltF7H
AgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQB5Slu71g30osgQd25puSrUxNf6+eQk
KEpWfrsrpRh7nOkAo3QmBmR4L7i5tUChnIv6UGi8qTeEWNHnMBcwgoe56tg5vqpK
mmaz3W1w8hxima/cSqzqWgw4U/JMDU1nBSYz2WJTyEUUvdDD1lSsWzrqFi5f/vC3
VjjWvio/DSvrgw==
                </Value>
            <Descriptor>
                <Syntax>cert_request</Syntax>
                <Description>Certificate Request</Description>
            </Descriptor>
        </Attribute>
    </Input>
    <Input id="i2">
        <ClassID>submitterInfoInputImpl</ClassID>
        <Name>Requestor Information</Name>
        <Attribute name="requestor_name">
            <Value>ftweedal</Value>
            <Descriptor>
                <Syntax>string</Syntax>
                <Description>Requestor Name</Description>
            </Descriptor>
        </Attribute>
        <Attribute name="requestor_email">
            <Value>ftweedal@redhat.com</Value>
            <Descriptor>
                <Syntax>string</Syntax>
                <Description>Requestor Email</Description>
            </Descriptor>
        </Attribute>
        <Attribute name="requestor_phone">
            <Value></Value>
            <Descriptor>
                <Syntax>string</Syntax>
                <Description>Requestor Phone</Description>
            </Descriptor>
        </Attribute>
    </Input>
</CertEnrollmentRequest>

With these fields filled out, the enrolment request can now be submitted to Dogtag:

$ pki cert-request-submit no-CN-req.xml
-----------------------------
Submitted certificate request
-----------------------------
  Request ID: 12
  Type: enrollment
  Request Status: rejected
  Operation Result: success

Boo! The enrolment request was rejected. Why? Certificate profiles can specify constraints on user-supplied values in a certificate request. In this case, it was the lack of a CN field in the subject, but profiles can also summarily reject an enrolment request based on other aspects of the embedded CSR, including key type and size.

Let’s now bring some extensions into the mix by generating a new signing request – this time with a valid subject, and with the Key Usage extension configured to indicate a certificate signing certificate (i.e., an intermediate CA). It obviously makes no sense to have this extensions on a server certificate, but let’s submit it with the caServerCert profile again and see what happens.

$ certutil -R -d .pki/nssdb -o usage-ca.req -a --keyUsage certSigning -s 'CN=c2.vm-096.idm.lab.bos.redhat.com'
...
$ openssl req -text < usage-ca.req
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: CN=c2.vm-096.idm.lab.bos.redhat.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (1024 bit)
                Modulus:
                    00:bc:6e:11:11:6f:e5:3c:34:03:8a:5f:92:41:44:
                    ...
                    9b:bf:86:8e:df:96:9e:e6:ef
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Key Usage:
                Certificate Sign
    Signature Algorithm: sha1WithRSAEncryption
         b0:4a:19:2c:c1:36:07:db:6a:bb:a9:36:0b:a4:53:c9:39:6d:
         ...

We can see that Key Usage extension is present in the request, and contains (only) the Certificate Sign declaration. We fill out and submit the enrolment request with this CSR:

$ pki cert-request-submit usage-ca-req.xml
-----------------------------
Submitted certificate request
-----------------------------
  Request ID: 14
  Type: enrollment
  Request Status: pending
  Operation Result: success

Perhaps surprisingly, this succeeds and the enrolment request is now pending, waiting for approval (or rejection) by a CA agent. It seems that, at least for the caServerCert profile, the value of the Key Usage extension in a CSR is ignored. The agent interface does allow adjustment of the Key Usage extension, however, and enforces sensible constraints, so no request submitted in the caServerCert profile will ever result in a certificate that could be used as an intermediate CA.

We have seen that Dogtag ignores the Key Usage extension information present in a CSR, but in fact, Dogtag ignores all information in the CSR except for what it specifically extracts. Therefore, requesting a particular key signing algorithm does not necessarily result in a certificate signed using that algorithm, and requesting some extension unknown in the selected profile (e.g., the Certificate Policies extension, which can be included in a CSR via the --extCP argument to certmonger) will certainly not be present in the certificate.

As a newcomer to the Dogtag PKI I find this behaviour somewhat limiting and would like to investigate whether the profiles system supports profiles that afford more control over the presense of extensions or the signing process, or what it would take to get this support.

The next post in this series will investigate how profiles are defined and the kinds of inputs and constraints they support.