[Freeipa-devel] [Design Review Request] V4/Automatic_Certificate_Request_Generation

Ben Lipton blipton at redhat.com
Thu Oct 13 15:23:03 UTC 2016


Thank you, this was a really helpful clarification of your point. 
Comments below. Once again, I'm sorry I missed the email for so long.

Ben

On 09/05/2016 06:52 AM, Jan Cholasta wrote:
> On 27.8.2016 22:40, Ben Lipton wrote:
>> On 08/25/2016 04:11 PM, Rob Crittenden wrote:
>>> Ben Lipton wrote:
>>>> On 08/23/2016 03:54 AM, Jan Cholasta wrote:
>>>>> On 8.8.2016 22:23, Ben Lipton wrote:
>>>>>> On 07/25/2016 07:45 AM, Jan Cholasta wrote:
>>>>>>> On 25.7.2016 13:11, Alexander Bokovoy wrote:
>>>>>>>> On Mon, 25 Jul 2016, Jan Cholasta wrote:
>>>>>>>>> On 20.7.2016 16:05, Ben Lipton wrote:
>>>>>>>>>> Hi,
>>>>>>>>>>
>>>>>>>>>> Thanks very much for the feedback! Some responses below; I hope
>>>>>>>>>> you'll
>>>>>>>>>> let me know what you think of my reasoning.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 07/20/2016 04:20 AM, Jan Cholasta wrote:
>>>>>>>>>>> Hi,
>>>>>>>>>>>
>>>>>>>>>>> On 17.6.2016 00:06, Ben Lipton wrote:
>>>>>>>>>>>> On 06/14/2016 08:27 AM, Ben Lipton wrote:
>>>>>>>>>>>>> Hello all,
>>>>>>>>>>>>>
>>>>>>>>>>>>> I have written up a design proposal for making certificate
>>>>>>>>>>>>> requests
>>>>>>>>>>>>> easier to generate when using alternate certificate profiles:
>>>>>>>>>>>>> http://www.freeipa.org/page/V4/Automatic_Certificate_Request_Generation. 
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> The use case for this is described in
>>>>>>>>>>>>> https://fedorahosted.org/freeipa/ticket/4899. I will be
>>>>>>>>>>>>> working on
>>>>>>>>>>>>> implementing this design over the next couple of months. 
>>>>>>>>>>>>> If you
>>>>>>>>>>>>> have
>>>>>>>>>>>>> the time and interest, please take a look and share any
>>>>>>>>>>>>> comments or
>>>>>>>>>>>>> concerns that you have.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks!
>>>>>>>>>>>>>
>>>>>>>>>>>>> Ben
>>>>>>>>>>>>>
>>>>>>>>>>>> Just a quick update to say that I've created a new document 
>>>>>>>>>>>> that
>>>>>>>>>>>> covers
>>>>>>>>>>>> the proposed schema additions in a more descriptive way (with
>>>>>>>>>>>> diagrams!)
>>>>>>>>>>>> I'm very new to developing with LDAP, so some more experienced
>>>>>>>>>>>> eyes on
>>>>>>>>>>>> the proposal would be very helpful, even if you don't have
>>>>>>>>>>>> time to
>>>>>>>>>>>> absorb the full design. Please take a look at
>>>>>>>>>>>> http://www.freeipa.org/page/V4/Automatic_Certificate_Request_Generation/Schema 
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> if you have a chance.
>>>>>>>>>>>
>>>>>>>>>>> I finally had a chance to take a look at this, here are some
>>>>>>>>>>> comments:
>>>>>>>>>>>
>>>>>>>>>>> 1) I don't like how transformation rules are tied to a 
>>>>>>>>>>> particular
>>>>>>>>>>> helper and have to be duplicated for each of them. They 
>>>>>>>>>>> should be
>>>>>>>>>>> generic and work with any helper, as helpers are just an
>>>>>>>>>>> implementation detail and their resulting data is the same.
>>>>>>>>>>>
>>>>>>>>>>> In fact, I think I would prefer if the CSR was generated using
>>>>>>>>>>> python-cryptography's CertificateSigningRequestBuilder [1] 
>>>>>>>>>>> rather
>>>>>>>>>>> than
>>>>>>>>>>> openssl or certutil or any other command line tool.
>>>>>>>>>>>
>>>>>>>>>> There are lots of tools that users might want to use to manage
>>>>>>>>>> their
>>>>>>>>>> private keys, so I don't know if we can assume that whatever
>>>>>>>>>> library we
>>>>>>>>>> prefer will actually be able to access the private key to sign a
>>>>>>>>>> CSR,
>>>>>>>>>> which is why I thought it would be useful to support more than
>>>>>>>>>> one.
>>>>>>>>>
>>>>>>>>> python-cryptography has the notion of backends, which allow it to
>>>>>>>>> support multiple crypto implementations. Upstream it currently
>>>>>>>>> supports only OpenSSL [2], but some work has been done on PKCS#11
>>>>>>>>> backend [3], which provides support for HSMs and soft-tokens 
>>>>>>>>> (like
>>>>>>>>> NSS
>>>>>>>>> databases).
>>>>>>>>>
>>>>>>>>> Alternatively, for NSS databases (and other "simple" cases), you
>>>>>>>>> can
>>>>>>>>> generate the private key with python-cryptography using the 
>>>>>>>>> default
>>>>>>>>> backend, export it to a file and import the file to the target
>>>>>>>>> database, so you don't actually need the PKCS#11 backend for 
>>>>>>>>> them.
>>>>>>>>>
>>>>>>>>> So, the only thing that's currently lacking is HSM support, but
>>>>>>>>> given
>>>>>>>>> that we don't support HSMs in IPA nor in certmonger, I don't 
>>>>>>>>> think
>>>>>>>>> it's an issue for now.
>>>>>>>>>
>>>>>>>>>> The
>>>>>>>>>> purpose of the mapping rule is to tie together the 
>>>>>>>>>> transformation
>>>>>>>>>> rules
>>>>>>>>>> that produce the same data into an object that's
>>>>>>>>>> implementation-agnostic, so that profiles referencing those 
>>>>>>>>>> rules
>>>>>>>>>> are
>>>>>>>>>> automatically compatible with all the helper options.
>>>>>>>>>
>>>>>>>>> They are implementation-agnostic, as long as you consider 
>>>>>>>>> `openssl`
>>>>>>>>> and `certutil` the only implementations :-) But I don't think 
>>>>>>>>> this
>>>>>>>>> solution scales well to other possible implementations.
>>>>>>>>>
>>>>>>>>> Anyway, my main grudge is that the transformation rules shouldn't
>>>>>>>>> really be stored on and processed by the server. The server 
>>>>>>>>> should
>>>>>>>>> know the *what* (mapping rules), but not the *how* 
>>>>>>>>> (transformation
>>>>>>>>> rules). The *how* is an implementation detail and does not
>>>>>>>>> change in
>>>>>>>>> time, so there's no benefit in handling it on the server. It
>>>>>>>>> should be
>>>>>>>>> handled exclusively on the client, which I believe would also 
>>>>>>>>> make
>>>>>>>>> the
>>>>>>>>> whole thing more robust (it would not be possible for a bug on 
>>>>>>>>> the
>>>>>>>>> server to break all the clients).
>>>>>>>> This is a good point. However, for the scope of Ben's project 
>>>>>>>> can we
>>>>>>>> limit it by openssl and certutil support? Otherwise Ben 
>>>>>>>> wouldn't be
>>>>>>>> able
>>>>>>>> to complete the project in time.
>>>>>>>
>>>>>>> I'm fine with that, but I don't think it's up to me :-)
>>>>>>>
>>>>>>>>
>>>>>>>>>> This is turning out to be a common (and, I think, reasonable)
>>>>>>>>>> reaction
>>>>>>>>>> to the proposal. It is rather complex, and I worry that it 
>>>>>>>>>> will be
>>>>>>>>>> difficult to configure. On the other hand, there is some hidden
>>>>>>>>>> complexity to enabling a simpler config format, as well. One of
>>>>>>>>>> the
>>>>>>>>>> goals of the project as it was presented to me was to allow the
>>>>>>>>>> creation
>>>>>>>>>> of profiles that add certificate extensions *that FreeIPA 
>>>>>>>>>> doesn't
>>>>>>>>>> yet
>>>>>>>>>> know about*. With the current proposal, one only has to add a 
>>>>>>>>>> rule
>>>>>>>>>> generating text that the helper will understand.
>>>>>>>>>
>>>>>>>>> ... which will be possible only as long as the helper
>>>>>>>>> understands the
>>>>>>>>> extension. Which it might not, thus the current proposal works 
>>>>>>>>> only
>>>>>>>>> for *some* extensions that FreeIPA doesn't yet support.
>>>>>>>> We can go ad infinitum here but with any helper implementation,
>>>>>>>> be it
>>>>>>>> python-cryptography or anything else, you will need to have a
>>>>>>>> support
>>>>>>>> there as well.
>>>>>>>
>>>>>>> My point was that the current proposal is not any better than my
>>>>>>> proposal in this regard, as neither of them allows one to use an
>>>>>>> arbitrary extension.
>>>>>>>
>>>>>>>> The idea with unknown extensions was to allow mapping
>>>>>>>> their acceptance to a specific relationship between IPA objects
>>>>>>>> (optionally) and an input from the CSR. A simplest example would
>>>>>>>> be an
>>>>>>>> identity rule that would copy an ASN.1 encoded content from the
>>>>>>>> CSR to
>>>>>>>> the certificate.
>>>>>>>>
>>>>>>>> That's on the mapping side, not on the CSR generation side, but it
>>>>>>>> would
>>>>>>>> go similarly for the CSR if you would be able to enter unknown but
>>>>>>>> otherwise correct ASN.1 stream. There is no difference at which
>>>>>>>> helper
>>>>>>>> type we are talking about because all of them support inserting
>>>>>>>> ASN.1
>>>>>>>> content.
>>>>>>>>
>>>>>>>>>> With your suggestion,
>>>>>>>>>> if there's a mapping between "san_directoryname" and the
>>>>>>>>>> corresponding
>>>>>>>>>> API calls or configuration lines, we need some way for users to
>>>>>>>>>> augment
>>>>>>>>>> that mapping without changing the code. If there's no 
>>>>>>>>>> mapping, and
>>>>>>>>>> it's
>>>>>>>>>> just done with text processing, we need enough in the config
>>>>>>>>>> format to
>>>>>>>>>> be able to generate fairly complex structures:
>>>>>>>>>>
>>>>>>>>>> builder =
>>>>>>>>>> builder.subject_name(x509.Name(u'CN=user,O=EXAMPLE.COM'))
>>>>>>>>>> builder =
>>>>>>>>>> builder.add_extension(x509.SubjectAlternativeName([x509.RFC822Name(u'user at example.com'), 
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> x509.DirectoryName(x509.Name(u'CN=user,O=EXAMPLE.COM'))]), 
>>>>>>>>>> False)
>>>>>>>>>>
>>>>>>>>>> and we need to do it without it being equivalent to calling
>>>>>>>>>> eval() on
>>>>>>>>>> the config attributes. I'm not sure how to achieve this (is it
>>>>>>>>>> safe to
>>>>>>>>>> call getattr(x509, extensiontype)(value) where extensiontype and
>>>>>>>>>> value
>>>>>>>>>> are user-specified?) and it definitely would have to be tied 
>>>>>>>>>> to a
>>>>>>>>>> particular library/tool.
>>>>>>>>>
>>>>>>>>> As I pointed out above, this needs to be figured out for the
>>>>>>>>> generic
>>>>>>>>> case for both the current proposal and my suggestion.
>>>>>> I have a proof of concept[1] for using openssl-based rules to add a
>>>>>> subject alt name extension without using openssl's knowledge of that
>>>>>> extension. It's not extremely pretty, and it took some trial and
>>>>>> error,
>>>>>> but no code changes. So, I think this actually is a difference 
>>>>>> between
>>>>>> the two proposals.
>>>>>
>>>>> With the obvious catch being that it works only with OpenSSL, which
>>>>> might not work for everyone, e.g. when using HSMs or SmartCards, due
>>>>> to a limited PKCS#11 support in OpenSSL.
>>>>
>>>> Very true. Even certutil's equivalent feature (--extGeneric) doesn't
>>>> seem like it would work very well in this context, as you are supposed
>>>> to pass in an already-encoded extension, so text-based templating
>>>> wouldn't be able to do much.
>>>
>>> Yeah, I struggled with this myself. I ended up writing a pyasn1 script
>>> to generate the extension I needed, wrote that to a file, and passed
>>> it to certutil using:
>>>
>>> --extGeneric 2.5.29.17:not-critical:/path/to/msupn.der
>>>
>>>>>
>>>>>>
>>>>>> Next we have the easy case, extensions that we as FreeIPA developers
>>>>>> know are important and build support for. For these, the two 
>>>>>> proposals
>>>>>> work equivalently well, but yours is simpler to configure because 
>>>>>> the
>>>>>> knowledge of how to make a san_rfc822name is built into the library
>>>>>> instead of being stored on the server as a set of rules.
>>>>>>
>>>>>> Finally, we have the case of extensions that are known to the 
>>>>>> helper,
>>>>>> but not to FreeIPA. In the existing proposal, new rules can be 
>>>>>> written
>>>>>> to support these extensions under a particular helper. Further, 
>>>>>> those
>>>>>> rules can be used by reference in many profiles, reducing
>>>>>> duplication of
>>>>>> effort/data/errors.
>>>>>>
>>>>>> As I understand it, the main objections in this thread are that
>>>>>> transformation rules are implementation (i.e. helper) specific data
>>>>>> stored in the IPA server, and that the system has several levels of
>>>>>> schema when it could just embed rules in the profile. But without
>>>>>> helper-specific rules, administrators could not take advantage of 
>>>>>> the
>>>>>> additional extensions supported by the helper they are using.
>>>>>
>>>>> There is *no* advantage in forcing the user to choose between helpers
>>>>> which differ only in the set of limitations on the CSR they are able
>>>>> to produce. The user should specify a) where the private key is
>>>>> located and b) what profile to use, and that's it, it should just 
>>>>> work.
>>>> Ok, this is a good point about usability. The user creating the CSR
>>>> shouldn't have to care about helpers, and I agree that the current way
>>>> they are exposed is clunky. I do think that an administrator creating
>>>> custom rules might want to take advantage of a helper, so they 
>>>> wouldn't
>>>> need to understand the ASN.1 representation of their chosen 
>>>> certificate
>>>> extension. Of course, the desired extension might not be supported by
>>>> the helper either. Since I don't know what specific extensions people
>>>> will want to use this for, I don't know how to balance the better
>>>> administrator experience of adding extensions via a helper with the
>>>> limited extension support.
>>>>
>>>> The original reason we arrived at the concept of "helpers" was to
>>>> support different ways of getting at private keys, but perhaps this
>>>> should not be the concern of the CSR data generator. In your opinion,
>>>> would it be sufficient to support just one key format (PKCS#12? PEM?)
>>>> and let the user deal with putting those keys into whatever
>>>> formats/databases they need? If that's ok, maybe we can stop having
>>>> *multiple* helpers, but if we want to replace helpers entirely I'm 
>>>> still
>>>> not certain what to replace them with.
>>>
>>> I'd just add an option to specify the output format, e.g PEM, NSS,
>>> Java keystore, PKCS#12, whatever. You can probably get away with the
>>> first two for starters. Different output format is going to mean
>>> different options but that is probably not a big deal.
>>
>> My point was that if we want to get rid of all the helpers but one, or
>> replace helpers with something else entirely like somehow templating
>> ASN1 structures directly, it will get harder to support all those
>> formats (or even both of the first two). For example, if we drop
>> certutil as a helper, how will we sign CSRs with keys stored in NSS
>> databases?
>
> 1. get the public part of the key from the NSS database
> 2. construct a CertificationRequestInfo [1] from the template and the 
> public key
> 3. sign the CertificationRequestInfo with NSS using the private key to 
> get a CSR
>
> This is purely client side, will work with any crypto library (just 
> substitute NSS for something else) and, if done right, using very 
> little code.

Ok, I like this. If an encoded CertificationRequestInfo is something we 
can expect to be compatible with any reasonable library (it sounds like 
it should be) then the library can be used client-side to do the 
key-storage-specific parts. I'm going to try writing this data -> 
encoded CertificationRequestInfo -> CSR flow to make sure it works as 
well as it sounds. If it does, it will also be useful for the code I'm 
working on right now to connect certmonger with the current version of 
the CSR autogeneration tool.
>
>>>
>>> Remember that the private key will be at rest for some period of time
>>> while the CSR is being approved. The key needs to be protected at that
>>> time.
>>>
>>> rob
>>>
>>>>>
>>>>>> And
>>>>>> without the separation of profiles from mapping rules in the schema,
>>>>>> rules would need to be copy+pasted among profiles, and grouping 
>>>>>> rules
>>>>>> with the same effect under different helpers would be much 
>>>>>> uglier. We
>>>>>> can and should discuss whether these are the right tradeoffs, but 
>>>>>> this
>>>>>> is where those decisions came from.
>>>>>>
>>>>>>>>>
>>>>>>>>> OTOH, I think we could use GSER encoding of the extension value:
>>>>>>>>>
>>>>>>>>>    { rfc822Name:"user at example.com",
>>>>>>>>> directoryName:rdnSequence:"CN=user,O=EXAMPLE.COM" }
>>>>>>>> GSER is not really used widely and does not have standardized
>>>>>>>> encoding
>>>>>>>> rules beyond its own definition. If you want to allow 
>>>>>>>> transformation
>>>>>>>> rules in GSER that mention existing content in IPA objects, you
>>>>>>>> would
>>>>>>>> need to deal with templating anyway. At this point it becomes
>>>>>>>> irrelevant
>>>>>>>> what you are templating, though.
>>>>>>>
>>>>>>> True, but the goal here is not to avoid templating, but rather to
>>>>>>> avoid implementation-specific bits on the server, and GSER is the
>>>>>>> only
>>>>>>> thing that is textual, implementation-neutral and, as a bonus,
>>>>>>> standardized.
>>>>>>>
>>>>>> As I said elsewhere, we could use GSER as a textual output format
>>>>>> instead of openssl or certutil, but it still needs its own 
>>>>>> "helper" to
>>>>>> build the CSR, and unlike the other options, it seems like we might
>>>>>> need
>>>>>> to implement that helper. I'm not sure it's fair to call it
>>>>>> implementation-neutral if no implementation exists yet :)
>>>>>
>>>>> Right. Like I said, using GSER was just a quick idea off the top 
>>>>> of my
>>>>> head. I would actually rather use some sort of data structure
>>>>> templating rather than textual templating on top of any kind of
>>>>> textual representation of said data structures. I don't know if there
>>>>> is such a thing, though.
>>>>
>>>> This sounds interesting, can you give an example of what this might 
>>>> look
>>>> like?
>
> It would be something like XSLT, but for ASN.1 rather than XML.
>
>>>>
>>>> I learned that there's also an XML encoding for ASN.1, XER, but that's
>>>> still a textual representation and we'd have to insert the data
>>>> textually.
>
> Well, yes and no. While it's true that it's still a textual 
> representation, what really makes a difference is that for XML, there 
> is a templating mechanism which understands the structure of the data 
> (XLST, as mentioned above).
>
> Unforutantely, XER has the same shortcoming as GSER: to be able to 
> convert it to DER, you need to know the ASN.1 definition of the data 
> structure. If we used XER+XSLT, we would also have to provide means of 
> adding custom ASN.1 definitions and run them through ASN.1 compiler to 
> convert between XER and DER.

This is a little disappointing, but it makes sense. I don't think I 
realized that we'll need to compile the ASN.1 data definitions for any 
extensions we want to use in a cert. That limitation didn't come up when 
we were only talking about extensions that were supported by the helper 
utility. But providing the ASN.1 spec for unusual extensions an admin 
wants to use in their certs is probably a reasonable expectation.
>
>>>> It doesn't seem to be supported by any python libraries,
>>>> either, but it does look like it's supported by the asn1 compiler 
>>>> in the
>>>> IPA source distribution.I could imagine an implementation that builds
>>>> an XML representation of the CSR via python templating, then makes a
>>>> signed CSR out of it in C. I'm a little concerned about it because it
>>>> would have to implement the whole CSR structure from scratch, but is
>>>> this a prototype that you'd be interested in seeing?
>
> I can imagine something like this might work:
>
> 1. (client) generate a key pair
> 2. (client) get SubjectPublicKeyInfo [2] for the public key
> 3. (client) encode the SubjectPublicKeyInfo as XER using asn1c and 
> python-cffi in API mode [3]
> 4. (client) call server to construct CertificationRequestInfo for 
> specified subject from a specified template and the SubjectPublicKeyInfo
> 5. (server) get the subject's LDAP entry
> 6. (server) create a XML document which contains the subject's LDAP 
> attributes and the SubjectPublicKeyInfo
> 7. (server) use XSLT to transform the XML document to 
> CertificationRequestInfo using the specified template
> 8. (server) return the CertificationRequestInfo to the client
> 9. (client) convert the CertificationRequestInfo from XER to DER using 
> asn1c and python-cffi in API mode
> 10. (client) sign the CertificationRequestInfo using the private key 
> to get a CSR
>
> It would be better if the XER-DER conversion was done on the server, 
> but I don't think that compiling and running code on the fly on the 
> server is a particularly good idea. Apparently there is a ASN.1 
> compiler available for PyASN1 [4], maybe that could be used instead, 
> but we would have to write a XER codec for PyASN1 ourselves (which 
> shouldn't be too hard IMO).

Yeah, running programs compiled from arbitrary ASN.1 seems like a risk. 
Maybe a little better because the ASN.1 is provided by an administrator, 
but we'd still be depending a lot on the security of the generated code. 
On the other hand, if we compile on the client, the CSR generation 
feature is limited to platforms where asn1c can be installed. I wish I 
could think of a way to do the compilation once when the profile is 
created, but run it on the client. That seems like asking for 
compatibility problems, though...

>
>>>>
>> On further investigation, it turns out the version of
>> python-cryptography in F24 includes a feature allowing arbitrary
>> extensions to be added by adding an UnrecognizedExtension to the
>> CertificateSigningRequestBuilder. This makes me feel somewhat better
>> both about python-cryptography as a tool for this task and about the
>> solution I just proposed. But I still don't have a clear idea that
>> answers 1) how to make templates that we can turn into encoded
>> extensions, and 2) how to deal with all the desired key formats.
>
> I hope the above clarifies these a little bit.
>
> [1] <https://tools.ietf.org/html/rfc2986#section-4.1>
> [2] <https://tools.ietf.org/html/rfc5280#section-4.1.2.7>
> [3] <https://cffi.readthedocs.io/en/latest/overview.html#abi-versus-api>
> [4] <https://github.com/kimgr/asn1ate>
>




More information about the Freeipa-devel mailing list