[Freeipa-devel] CSR autogeneration next steps

Ben Lipton blipton at redhat.com
Mon Dec 19 20:59:27 UTC 2016


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.
>>
>> 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.
>>
>> 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.
>>
>>>
>>> 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?
>>
>>>>
>>>>>
>>>>> 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: 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 
>>>
>>
>




More information about the Freeipa-devel mailing list