i2d_X509_REQ() -> d2i_X509_REQ() = asn1 encoding routines:c2i_ASN1_OBJECT:invalid object encoding:a_object.c:287

Graham Leggett minfrin at sharp.fm
Thu Mar 21 00:36:12 UTC 2019

On 19 Mar 2019, at 18:00, Viktor Dukhovni <openssl-users at dukhovni.org> wrote
> Well, the *standard* structure for passing around just the unsigned
> data underlying a CSR (X509_REQ), is a CertificationRequestInfo
> (X509_REQ_INFO).  So if the modules are to use *standard* structures
> to communicate.  The object being passed needs to be either a CSR
> (signed) or the enclosed CRI (unsigned).

I agree - it is the ideal structure to use, however translating this into real world implementation there aren’t any APIs in openssl that allow us to do this today, and it is very likely that the same limitation exists in other APIs we would like to support in future (NSS, native APIs, etc).

This software however is 7 years old, we’re not in a position to drop everything and rewrite it.

> You could, for example, sign the request with some suitable key
> (ideally the private key corresponding to the public key in the
> CSR, if available) before handing it off.  If the signing key is
> not the enclosed public key, it would not pass "req -verify" (it
> never did before either, for lack of a signature), but the called
> module would be able to decode a CSR, and work as before.

In our world we’re translating from various protocols (scep, spkac, etc etc) where proof of possession isn’t a signed X509_REQ, but is rather a challenge passphrase, previous certificate, etc etc, to a standard object (CSR) that can then be signed by a range of modules (simple local signing, signing on a smartcard, etc etc).

As a result while in the ideal world we would be dealing with signed CSRs, in our world we have to support a CSR with proof of possession supplied alongside in a range of possible formats.

We might have to go with the sign-with-a-dummy-signature route, but this would be unfortunate.

>> I don’t follow - in order to get access to the data inside the X509_REQ_INFO
>> structure, I need to first wrap it in a X509_REQ, otherwise I have no API
>> calls to get access to the data inside it.
> No need to get access to the data inside an X509_REQ_INFO is expected.
> That object's sole purpose is to be serializable for signing.
> You have rather an edge-case, where for some reason you're delegating
> signing to a CA module by passing it a non-standard structure that
> *resembles* a CSR, which is however for some reason not signed with
> the subject key (not signed at all), and you expect the CA to apply
> policy, by decoding the CRI inside  this non-CSR.
> OpenSSL 1.1.x does not have structure member accessors for a CRI,
> but they would be easy to add, that's essentially what the X509_REQ
> accessors do:
>    long X509_REQ_get_version(const X509_REQ *req)
>    {
> 	return ASN1_INTEGER_get(req->req_info.version);
>    }
>    X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req)
>    {
> 	return req->req_info.subject;
>    }
>    int X509_REQ_get_attr_count(const X509_REQ *req)
>    {
> 	return X509at_get_attr_count(req->req_info.attributes);
>    }
>    int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid, int lastpos)
>    {
> 	return X509at_get_attr_by_NID(req->req_info.attributes, nid, lastpos);
>    }
>    int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, const ASN1_OBJECT *obj,
> 				 int lastpos)
>    {
> 	return X509at_get_attr_by_OBJ(req->req_info.attributes, obj, lastpos);
>    }
>    X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc)
>    {
> 	return X509at_get_attr(req->req_info.attributes, loc);
>    }
>    X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc)
>    {
> 	return X509at_delete_attr(req->req_info.attributes, loc);
>    }
> If one were to "void the warranty", one could cast the (X509_REQ_INFO
> *) as an (X509_REQ *), and the accessors would just work, but you
> must not do that, the internal details might change some day, as
> they did between 1.1.x and 1.0.2 (where the X509_REQ_INFO is a
> separately allocated structure pointed to by the X509_REQ).

We’re a modular CA, we don’t dictate to our modules what they can and can’t do, so to say “that object’s sole purpose” is a contradiction.

It turns out that the module that checks the proof of possession against an LDAP server needs to pull out the subject of the CSR in order to make the LDAP query, which in turn means it needs to be able to read the contents of the X509_REQ (or X509_REQ_INFO).

X509_REQ_INFO prevents us from seeing the information, and so this won’t work for us.

>> The modules are Apache httpd modules, and the boundaries between the modules
>> are hooks that pass DER encoded structures between each module.
> Well, so the key question is, why not pass an actual CSR.  What's
> preventing the CSR from being signed?

Protocols like scep and spkac don’t supply a CSR, that’s not how they work.

>>> This isn't pretty, and perhaps we need some new functions to explicitly
>>> embed a CRI in a CSR, but it is certainly something you can do in the
>>> short term.
>> Can we not rather fix the initialisation of the X509_REQ in X509_REQ_new()
>> so that it works like it used to? It seems like a massive headache to do
>> something that used to be trivial.
> No, because it is not broken.  The "massive headache" is peculiar to
> the rather odd choice of "RPC" between these Apache modules, where
> a non-CSR is masquerading as a CSR.

DER encoding is well standardised, and modularity is well understood. None of this is an odd choice in a modular CA.

>> I see there have been changes to openssl code relating to how structures
>> are initialised, I suspect an error has crept in where an ASN.1 object is
>> missing instead of empty, thus the malformed CSR.
> There is no error.  The X509_REQ_INFO and X509_ALGOR (signature
> algorithm) are now embedded in the CSR, and are no longer optional.

And yet here we are.

The problem seems that at some point during the 1.0.2 releases, the X509_REQ was previously serialised with what looks like an empty X509_ALGOR structure:

 507:d=2  hl=2 l=   1 prim: OBJECT            :itu-t

and at some point openssl changed to serialise with a bad object instead:

 508:d=2  hl=2 l=   0 prim: OBJECT            :BAD OBJECT

A change from something from optional to mandatory should have happened in the 1.1.0 release, not within the 1.0.2 point releases.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 3260 bytes
Desc: not available
URL: <http://mta.openssl.org/pipermail/openssl-users/attachments/20190321/c7a131c9/attachment.bin>

More information about the openssl-users mailing list