[Freeipa-devel] CSR autogeneration next steps

Jan Cholasta jcholast at redhat.com
Thu Jan 12 09:35:53 UTC 2017


On 11.1.2017 00:38, Ben Lipton wrote:
>
> On 01/10/2017 01:58 AM, Jan Cholasta wrote:
>> On 19.12.2016 21:59, Ben Lipton wrote:
>>>
>>> On 12/15/2016 11:11 PM, Ben Lipton wrote:
>>>>
>>>> On 12/12/2016 03:52 AM, Jan Cholasta wrote:
>>>>> On 5.12.2016 16:48, Ben Lipton wrote:
>>>>>> Hi Jan, thanks for the comments.
>>>>>>
>>>>>>
>>>>>> On 12/05/2016 04:25 AM, Jan Cholasta wrote:
>>>>>>> Hi Ben,
>>>>>>>
>>>>>>> On 3.11.2016 00:12, Ben Lipton wrote:
>>>>>>>> Hi everybody,
>>>>>>>>
>>>>>>>> Soon I'm going to have to reduce the amount of time I spend on new
>>>>>>>> development work for the CSR autogeneration project, and I want to
>>>>>>>> leave
>>>>>>>> the project in as organized a state as possible. So, I'm taking
>>>>>>>> inventory of the work I've done in order to make sure that what's
>>>>>>>> ready
>>>>>>>> for review can get reviewed and the ideas that have been discussed
>>>>>>>> get
>>>>>>>> prototyped or at least recorded so they won't be forgotten.
>>>>>>>
>>>>>>> Thanks, I have some questions and comments, see below.
>>>>>>>
>>>>>>>>
>>>>>>>> Code that's ready for review (I will continue to put in as much
>>>>>>>> time as
>>>>>>>> needed to help get these ready for submission):
>>>>>>>>
>>>>>>>> - Current PR: https://github.com/freeipa/freeipa/pull/10
>>>>>>>
>>>>>>> How hard would it be to update the PR to use the "new" interface
>>>>>>> from
>>>>>>> the design thread? By this I mean that currently there is a command
>>>>>>> (cert_get_requestdata), which creates a CSR from profile id +
>>>>>>> principal + helper, but in the design we discussed a command which
>>>>>>> creates a CertificationRequestInfo from profile id + principal +
>>>>>>> public key.
>>>>>>>
>>>>>>> Internally it could use the OpenSSL helper, no need to implement the
>>>>>>> full "new" design. With your build_requestinfo.c code below it looks
>>>>>>> like it should be pretty straightforward.
>>>>>>
>>>>>> This is probably doable with the cffi, but I'm concerned about
>>>>>> usability. A user can run the current command to get a (reusable)
>>>>>> script, and run the script to get a CSR. It works with keys in
>>>>>> both PEM
>>>>>> files and NSS databases already. If we change to outputting a
>>>>>> CertificationRequestInfo, in order to make this usable on the command
>>>>>> line, we'll need:
>>>>>> - An additional tool to sign a CSR given a CertificationRequestInfo
>>>>>> (for
>>>>>> both types of key storage).
>>>>>> - A way to extract a SubjectPublicKeyInfo structure from a key within
>>>>>> the ipa command (like [1] but we need it for both types of key
>>>>>> storage)
>>>>>> Since as far as I know there's no standard encoding for files
>>>>>> containing
>>>>>> only a CertificationRequestInfo or a SubjectPublicKeyInfo, we'll be
>>>>>> writing and distributing these ourselves. I think that's where
>>>>>> most of
>>>>>> the extra work will come in.
>>>>>
>>>>> For PEM files, this is easily doable using python-cryptography (to
>>>>> extract SubjectPublicKeyInfo and sign CertificationRequestInfo) and
>>>>> PyASN1 (to create a CSR from the CertificationRequestInfo and the
>>>>> signature).
>>>>
>>>> I didn't realize that python-cryptography knew about
>>>> SubjectPublicKeyInfo structures, but indeed this seems to be pretty
>>>> straightforward:
>>>>
>>>> key = load_pem_private_key(key_bytes, None, default_backend())
>>>> pubkey_info = key.public_key().public_bytes(Encoding.DER,
>>>> PublicFormat.SubjectPublicKeyInfo)
>>>>
>>>> Thanks for letting me know this functionality already existed.
>
> I'm currently working on the step of signing the
> CertificationRequestInfo and creating a CSR from it. I think I have it
> working with pyasn1, but of course the "signature algorithm" for the CSR
> needs to be specified and implemented within the code since I'm not
> using a library that understands CSRs natively. The code I have
> currently always produces CSRs with the sha256WithRSAEncryption
> algorithm (DER-encode request info, SHA256, PKCS #1v1.5 padding, RSA
> encryption), and the OID for that algorithm is hardcoded in the output
> CSR. Is this ok or will we need more flexibility than that?

IMO it's OK for starters.

>>>>>
>>>>> For NSS databases, this will be trickier and will require calling C
>>>>> functions, as neither certutil nor python-nss provide a way to a)
>>>>> address existing keys in the database by key ID b) get
>>>>> SubjectPublicKeyInfo for a given key.
>>
>> This can be worked around by:
>>
>> 1. Generating a key + temporary certificate:
>>
>>     n=$(head -c 40 /dev/urandom | base32)
>>     certutil -S -n $n -s CN=$n -x -t ,,
>>
>> 2. Extracting the public key from the certificate:
>>
>>     certutil -L -n $n -a >temp.crt
>>     (extract the public key using python-cryptography)
>>
>> 3. Deleting the temporary certificate:
>>
>>     certutil -D -n $n
>>
>> 4. Importing the newly issued certificate:
>>
>>     certutil -A -n $n -t ,, -a <new.crt
>>
> Oof, thanks, I'm not sure I would have been able to come up with that.
> Can you generate a key without a temporary certificate if you use the
> NSS API, or does their model require every key to belong to a cert?

I'm pretty sure it's possible, but it certainly won't be as simple as 
this. I gave up after a few hours of digging into NSS source code and 
not being able to figure out how.

>>>>>
>>>>> As for encoding, the obvious choice is DER. It does not really matter
>>>>> there is no standard file format, as we won't be transferring these
>>>>> as files anyway.
>>>>
>>>> Agreed. I just meant there aren't tools already because this isn't a
>>>> type of file one often needs to process.
>>>>>
>>>>>>
>>>>>> Would it be ok to stick with the current design in this PR? I'd feel
>>>>>> much better if we could get the basic functionality into the repo and
>>>>>> then iterate on it rather than changing the plan at this point. I can
>>>>>> create a separate PR to change cert_get_requestdata to this new
>>>>>> interface and at the same time add the necessary adapters (bullet
>>>>>> points
>>>>>> above) to make it user-friendly.
>>>>>
>>>>> Works for me.
>>>>
>>>> Updated the PR to fix conflicts with master. Had some trouble with CI
>>>> but creating a new PR with the same commits fixed it
>>>> (https://github.com/freeipa/freeipa/pull/337). Not sure if it's fixed
>>>> permanently, so I guess I'll just keep the two PRs synchronized now,
>>>> or we could close the old one.
>>
>> You can close the old one.
>>
>> Just to make sure we are on the same page, you want this PR to be
>> merged before submitting additional PRs built on top of it?
>
> Yes, I would like to merge this one to have as a starting point if
> you're comfortable with it: https://github.com/freeipa/freeipa/pull/337.
> I just did a force push to clean up the history, but the final diff
> should be the same as it was before.

OK.

>>
>>>>>
>>>>>>
>>>>>> I would probably just implement the adapters within the
>>>>>> cert_build/cert_request client code unless you think having
>>>>>> standalone
>>>>>> tools is valuable. I suppose certmonger is going to need these
>>>>>> features
>>>>>> too, but I don't know how well sharing code between them is going to
>>>>>> work.
>>>>>
>>>>> cert-request is exactly the place where it should be :-) I wouldn't
>>>>> bother with certmonger until we have a server-side csrgen.
>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> - Allow some fields to be specified by the user at creation time:
>>>>>>>> https://github.com/LiptonB/freeipa/commits/local-user-data
>>>>>>>
>>>>>>> Good idea :-)
>>>>>>>
>>>>>>>>
>>>>>>>> - Automation for the full process from getting CSR data to
>>>>>>>> requesting
>>>>>>>> cert: https://github.com/LiptonB/freeipa/commits/local-cert-build
>>>>>>>
>>>>>>> LGTM, although I would prefer if this was a client-side extension of
>>>>>>> cert-request rather than a completely new command.
>>>>>>
>>>>>> I did try that at first, but I struggled to figure out the interface
>>>>>> for
>>>>>> the modified cert-request. (Not that the current solution is so
>>>>>> great,
>>>>>> what with the copying of options from cert_request and certreq.) If I
>>>>>> remember correctly, I was uncertain how to implement parameters that
>>>>>> are
>>>>>> required/invalid based on other parameters: the current cert-request
>>>>>> takes a signed CSR (required), a principal (required), and a profile
>>>>>> ID;
>>>>>> the new cert-request (what I implemented as cert-build) takes a
>>>>>> principal (required), a profile ID (required), and a key location
>>>>>> (required). I can't remember if that was the only problem, but
>>>>>> I'll try
>>>>>> again to merge the commands and get back to you.
>>>>>
>>>>> To make the CSR argument optional on the client, you can do this:
>>>>>
>>>>>     def get_options(self):
>>>>>         for option in super(cert_request, self).get_options():
>>>>>             if option.name == 'csr':
>>>>>                 option = option.clone(required=False)
>>>>>             yield
>>>>>
>>>>> IMO profile ID should default to caIPAserviceCert on the client as
>>>>> well.
>>>>
>>>> I originally had it doing so, but changed it to a required option
>>>> based on feedback in this email:
>>>> https://www.redhat.com/archives/freeipa-devel/2016-August/msg00021.html:
>>>>
>>>> "In general use I think that 'caIPAserviceCert' is unlikely to be used
>>>> a majory of the time, and it is a new command so there are no
>>>> compatibility issues; therefore why not make the profile option
>>>> mandatory?" I guess since we're talking about cert-request now, the
>>>> compatibility issues are back.
>>>>
>>>> https://github.com/LiptonB/freeipa/commits/local-cert-build has now
>>>> been updated to change the cert_request command rather than adding a
>>>> new command. It seems to work now (thanks for the advice on making the
>>>> argument optional), the only thing I'm having trouble with is the
>>>> default for the profile_id argument. Previously, the default was
>>>> applied by this code in cert_request.execute:
>>>>
>>>> profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)
>>>>
>>>> But now, in the client, I need the default to pass to
>>>> cert_get_requestdata if no profile is specified. I'm not sure I can
>>>> access backends from the client to get it the same way the server code
>>>> does. Should I just import ipapython/dogtag.py and use the
>>>> DEFAULT_PROFILE set in there? Is there a way I can give the option a
>>>> default that will be seen in both the server and the client?
> Just wanted to call attention to this question. The code that's
> currently problematic is here:
> https://github.com/LiptonB/freeipa/blob/dda05b0b4dfa332569a8ca75632eaeceb95fbd6a/ipaclient/plugins/cert.py#L86
> (will pass None when in fact the argument default should be used).

self.get_default_of('profile_id')

>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Other prototypes and design ideas that aren't ready for submission
>>>>>>>> yet:
>>>>>>>>
>>>>>>>> - Utility written in C to build a CertificationRequestInfo from a
>>>>>>>> SubjectPublicKeyInfo and an openssl-style config file. The
>>>>>>>> purpose of
>>>>>>>> this is to take a config that my code already knows how to
>>>>>>>> generate, and
>>>>>>>> put it in a form that certmonger can use. This is nearly done and
>>>>>>>> available at:
>>>>>>>> https://github.com/LiptonB/freeipa-prototypes/blob/master/build_requestinfo.c
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> Nice! As I said above, this could really make implementing the "new"
>>>>>>> csrgen interface simple.
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> - Ideally it should be possible to use this tool to reimplement
>>>>>>>> the full
>>>>>>>> cert-request automation (local-cert-build branch) without a
>>>>>>>> dependency
>>>>>>>> on the certutil/openssl tools. However, I don't think any of the
>>>>>>>> python
>>>>>>>> crypto libraries have bindings for the functions that deal with
>>>>>>>> CertificationRequestInfo objects, so I don't think I can do this
>>>>>>>> in the
>>>>>>>> short term.
>>>>>>>
>>>>>>> You can use python-cffi to write your own minimal bindings. It's
>>>>>>> fairly straightforward, take a look at FreeIPA commit 500ee7e2
>>>>>>> for an
>>>>>>> example of how to port C code to Python with python-cffi.
>>>>>>
>>>>>> Thank you for the example. I will take a look.
>>>>>>>
>>>>>>>>
>>>>>>>> - Certmonger "helper" program that takes in the
>>>>>>>> CertificationRequestInfo
>>>>>>>> that certmonger generates, calls out to IPA for profile-specific
>>>>>>>> data,
>>>>>>>> and returns an updated CertificationRequestInfo built from the
>>>>>>>> data.
>>>>>>>> Certmonger doesn't currently support this type of helper, but (if I
>>>>>>>> understood correctly) this is the architecture Nalin believed
>>>>>>>> would be
>>>>>>>> simplest to fit in. This is not done yet, but I intend to
>>>>>>>> complete it
>>>>>>>> soon - it shouldn't require much code beyond what's in
>>>>>>>> build_requestinfo.c.
>>>>>>>
>>>>>>> To me this sounds like it should be a new operation of the current
>>>>>>> helper rather than a completely new helper.
>>>>>>
>>>>>> Maybe so. I certainly wouldn't call this a finished design, I just
>>>>>> wanted to have some kind of proof of concept for how the certmonger
>>>>>> integration could work. For what it's worth, that prototype is now
>>>>>> available at [2].
>>>>>
>>>>> OK.
>>>>>
>>>>>>>
>>>>>>> Anyway, the ultimate goal is to move the csrgen code to the server,
>>>>>>> which means everything the helper will have to do is call a command
>>>>>>> over RPC.
>>>>>>>
>>>>>>>>
>>>>>>>> - Tool to convert an XER-encoded cert extension to DER, given the
>>>>>>>> ASN.1
>>>>>>>> description of the extension. This would unblock Jan Cholasta's
>>>>>>>> idea of
>>>>>>>> using XSLT for templates rather than text-based formatting. I
>>>>>>>> should be
>>>>>>>> able to implement the conversion tool, but it may be a while
>>>>>>>> before I
>>>>>>>> have time to demo the full XSLT idea.
>>>>>>>
>>>>>>> Was there any progress on this?
>>>>>>
>>>>>> I have started working on implementing it with asn1c, and I'm already
>>>>>> seeing some of the inconvenience (security issues aside) of
>>>>>> building on
>>>>>> the server. Libtasn1 seems like a much better model, but doesn't
>>>>>> seem to
>>>>>> have XER support. Anyway, don't quite have results here yet but I
>>>>>> think
>>>>>> I should have the XER->DER demo with asn1c ready in a week or two.
>>>>>
>>>>> Implementing XER codec on top of libtasn1 shouldn't be too hard; I
>>>>> have a WIP which I will post soon.
>>>
>>> It took me some experimentation to get this to work, but the solution
>>> with asn1c is actually quite simple because the tool automatically
>>> provides a sample C file that converts between different formats. So,
>>> this very basic shell script is able to do the conversion:
>>> https://github.com/LiptonB/freeipa-prototypes/blob/master/xer2der.sh
>>>
>>> $ cat ExtKeyUsage.xer
>>> <ExtKeyUsageSyntax>
>>>     <KeyPurposeId>1.3.6.1.5.5.7.3.2</KeyPurposeId>
>>>     <KeyPurposeId>1.3.6.1.5.5.7.3.4</KeyPurposeId>
>>> </ExtKeyUsageSyntax>
>>>
>>> $ cat KeyUsage.asn1
>>> KUModule DEFINITIONS ::=
>>> BEGIN
>>>
>>> KeyUsage ::= BIT STRING {
>>>      digitalSignature        (0),
>>>      nonRepudiation          (1),  -- recent editions of X.509 have
>>>                                 -- renamed this bit to contentCommitment
>>>      keyEncipherment         (2),
>>>      dataEncipherment        (3),
>>>      keyAgreement            (4),
>>>      keyCertSign             (5),
>>>      cRLSign                 (6),
>>>      encipherOnly            (7),
>>>      decipherOnly            (8) }
>>>
>>> ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
>>>
>>> KeyPurposeId ::= OBJECT IDENTIFIER
>>>
>>> END
>>>
>>> $ ./xer2der.sh KeyUsage.asn1 ExtKeyUsageSyntax ExtKeyUsage.xer
>>> 2>/dev/null | xxd
>>> 00000000: 3014 0608 2b06 0105 0507 0302 0608 2b06 0...+.........+.
>>> 00000010: 0105 0507 0304                           ......
>>
>> So far I don't have a working example using libtasn1. I have something
>> close to it, but it's hacky, as the libtasn1 API is pretty limited,
>> and I didn't have time to work on it in the last few weeks.

I got it working, needs just a little polishing. It's still ugly hacky 
though.

>>
>>>
>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> So: currently on my to do list are the certmonger helper and the
>>>>>>>> XER->DER conversion tool. Do you have any comments about these
>>>>>>>> plans,
>>>>>>>> and is there anything else I can do to wrap up the project neatly?
>>>>>>>>
>>>>>>>> Thanks,
>>>>>>>> Ben
>>>>>>>>
>>>>>>>
>>>>>>> Honza
>>>>>>>
>>>>>> [1]
>>>>>> https://github.com/LiptonB/freeipa-prototypes/blob/master/key2spki.c
>>>>>> [2]
>>>>>> https://github.com/LiptonB/freeipa-prototypes/blob/master/cm_ipa_csrgen.c
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>>
>


-- 
Jan Cholasta




More information about the Freeipa-devel mailing list