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

Viktor Dukhovni openssl-users at dukhovni.org
Tue Mar 19 16:00:12 UTC 2019


On Tue, Mar 19, 2019 at 02:04:14PM +0200, Graham Leggett wrote:

> > Why do you need to do the encode and decode?  What's wrong with the original
> > request object?
> 
> The code is a modular ca, and different modules communicate with each other
> generically using the standard DER encoded structures.

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).

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.

> > > While I can see a d2i_X509_REQ_INFO() function, I can’t find a
> > > corresponding function in openssl 1.1.0+ that assigns this to a
> > > X509_REQ, unless I am missing it?
> > 
> > It should not be needed.
> 
> 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).

> > Can you be more specific about these "module boundaries”?
> 
> 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?

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

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

Your immediate choices are to sign a CSR, or to embed your CRI in
an outer DER wrapper that simulates a CSR.  Signing with some
key (ideally the subject key) seems simplest.

Even if we provide the "missing" CRI accessors in OpenSSL 1.1.1c,
I don't think that would practically solve your problem, because
some users would have 1.1.0, 1.1.1, 1.1.1a or 1.1.1b.

The DER wrapper is also simple (commented hex):

    $ openssl req -config <(
	printf "distinguished_name = dn\n[dn]\nprompt=yes\n[v3req]\n%s\n" \
		       "subjectAltName = DNS:example.com"
	    ) \
	-reqexts v3req -new -newkey rsa:1024 -keyout /dev/null \
        -nodes -subj / 2>/dev/null |
        openssl req -outform DER |
        hexdump -ve '16/1 " %02x" "\n"'
     30 82 01 68 30 81 d2 02 01 00 30 00 30 81 9f 30
#    wrapper -->|<-- CRI DER begins here
     0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 81
     8d 00 30 81 89 02 81 81 00 d0 61 3e 71 9c f6 a1
     d0 bd cf 7c f2 e6 be a2 5b 59 4a f5 8f 32 9d d6
     47 38 43 1a 57 51 18 bd e1 a2 1d 52 d0 8d c5 79
     07 c4 3c 78 19 5c ca c7 37 69 2a 22 70 05 e1 ab
     ec 35 b2 2b 1d 46 f7 8f b9 bd 30 27 08 81 1c ac
     81 a1 c3 ed 5b 9f 9e 46 22 d3 28 45 3c 85 36 30
     7e 11 46 ae 65 d0 8b 1d 19 28 3b 76 d6 f0 fd 59
     b3 62 5a e9 45 21 92 29 1d 0f af ad 87 c9 33 fc
     3d 87 5f 68 98 96 89 a6 b3 02 03 01 00 01 a0 29
     30 27 06 09 2a 86 48 86 f7 0d 01 09 0e 31 1a 30
     18 30 16 06 03 55 1d 11 04 0f 30 0d 82 0b 65 78
     61 6d 70 6c 65 2e 63 6f 6d 30 0d 06 09 2a 86 48
#    CRI DER ends here ------->|<--- begin sig OID
     86 f7 0d 01 01 0b 05 00 03 81 81 00 00 00 00 00
#    end sig OID ---->| null|<- 1024 zeroed sig bits
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     00 00 00 00 00 00 00 00 00 00 00 00
#                                       |<-- end sig

Appending a fixed 147 byte suffix, and prepending an outer sequence
tag and length is not difficult.  But, since the signature is fake
anyway, it could be much shorter, for example truncating it to an
8-bit "0" signature (19 byte suffix with the OID and null parameters),
I get:

    $ perl -pe 's/\s//g; s/(..)/chr(hex($1))/eg;' <<-EOF | openssl req -inform DER -text
	 30 81 e8 30 81 d2 02 01 00 30 00 30 81 9f 30
	 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 81
	 8d 00 30 81 89 02 81 81 00 d0 61 3e 71 9c f6 a1
	 d0 bd cf 7c f2 e6 be a2 5b 59 4a f5 8f 32 9d d6
	 47 38 43 1a 57 51 18 bd e1 a2 1d 52 d0 8d c5 79
	 07 c4 3c 78 19 5c ca c7 37 69 2a 22 70 05 e1 ab
	 ec 35 b2 2b 1d 46 f7 8f b9 bd 30 27 08 81 1c ac
	 81 a1 c3 ed 5b 9f 9e 46 22 d3 28 45 3c 85 36 30
	 7e 11 46 ae 65 d0 8b 1d 19 28 3b 76 d6 f0 fd 59
	 b3 62 5a e9 45 21 92 29 1d 0f af ad 87 c9 33 fc
	 3d 87 5f 68 98 96 89 a6 b3 02 03 01 00 01 a0 29
	 30 27 06 09 2a 86 48 86 f7 0d 01 09 0e 31 1a 30
	 18 30 16 06 03 55 1d 11 04 0f 30 0d 82 0b 65 78
	 61 6d 70 6c 65 2e 63 6f 6d 30 0d 06 09 2a 86 48
	 86 f7 0d 01 01 0b 05 00 03 02 00 00
	EOF
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: 
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (1024 bit)
                Modulus:
                    00:d0:61:3e:71:9c:f6:a1:d0:bd:cf:7c:f2:e6:be:
                    a2:5b:59:4a:f5:8f:32:9d:d6:47:38:43:1a:57:51:
                    18:bd:e1:a2:1d:52:d0:8d:c5:79:07:c4:3c:78:19:
                    5c:ca:c7:37:69:2a:22:70:05:e1:ab:ec:35:b2:2b:
                    1d:46:f7:8f:b9:bd:30:27:08:81:1c:ac:81:a1:c3:
                    ed:5b:9f:9e:46:22:d3:28:45:3c:85:36:30:7e:11:
                    46:ae:65:d0:8b:1d:19:28:3b:76:d6:f0:fd:59:b3:
                    62:5a:e9:45:21:92:29:1d:0f:af:ad:87:c9:33:fc:
                    3d:87:5f:68:98:96:89:a6:b3
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name: 
                DNS:example.com
    Signature Algorithm: sha256WithRSAEncryption
         00
-----BEGIN CERTIFICATE REQUEST-----
MIHoMIHSAgEAMAAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANBhPnGc9qHQ
vc988ua+oltZSvWPMp3WRzhDGldRGL3hoh1S0I3FeQfEPHgZXMrHN2kqInAF4avs
NbIrHUb3j7m9MCcIgRysgaHD7VufnkYi0yhFPIU2MH4RRq5l0IsdGSg7dtbw/Vmz
YlrpRSGSKR0Pr62HyTP8PYdfaJiWiaazAgMBAAGgKTAnBgkqhkiG9w0BCQ4xGjAY
MBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAAwIAAA==
-----END CERTIFICATE REQUEST-----

The prefix would often be 4 bytes (i.e. 16-bit rather than an 8-bit
length), rather than 3.  As, for example always with 2048-bit RSA
subject key.  [ The above CSR is atypically short. ]

-- 
	Viktor.


More information about the openssl-users mailing list