PKISolutions/PSPKI

Question on X509CertificateRequest

defacto64 opened this issue · 10 comments

Hi, just a question: is your System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest class based on Window's CertEnroll component? I am asking this because I believe there is a problem in CertEnroll, regarding signature verification, that also seems present in the said class.

No, this class relies on a custom implementation with manual parser I wrote myself. So you can try your request against this class.

If issue persist, I would like to get your raw request for additional investigation.

I have already tried verifying a CSR with your class, that's why I was asking this question. After your reply, this should be regarded as a bug report, but you may view things otherwise :)

The issue I would like to point out to you is the following. I will not be surprised if you find it questionable, also because many SW (but not all) behave the same way, and you may not think it is worth working on. However, in my opinion this is a bug or at least a "limitation" that should be documented.

The attached CSR verifies OK with PSPKI 3.7 (and older), that is, as reported by the SignatureIsValid property of the X509CertificateRequest class:

problem-csr-20211109.txt

The result should be negative, though, as the signature on the attached CSR was not computed according to RFC2986. In particular, the attached CSR contains an unordered multi-value RDN, that is an unordered SET OF, in its Subject field, and this was apparently ignored when the CSR was generated (RFC2986 requires DER-encoding the certificationRequestInfo component prior to signing it). So the signature, although it's cryptographically correct, was computed over the wrong data, and I'd expect a CSR validator to detect that.

A number of other tool(kit)s report the signature as invalid, which IMO is the correct result, including GnuTLS and BouncyCastle.

I don't see what is wrong with your CSR. Subject field uses non-usual format (all RDNs placed under single SET tag), but the syntax is valid for X.500 distinguished name. Here is the definition of X.500 distinguished name:

DistinguishedName ::= RDNSequence

RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

RelativeDistinguishedName ::= SET SIZE (1..MAX) OF
    AttributeTypeAndValue

AttributeTypeAndValue ::= SEQUENCE {
    type  AttributeType,
    value AttributeValue }

and your subject name encoding conforms this definition. RDN is a SET OF AttributeTypeAndValue, so multiple AttributeTypeAndValue entries may appear under single SET tag.

SignatureIsValid reflects cryptographic validity of the signature. The signed data is not validated by that routine (which sets SignatureIsValid property) and it should not parse the TBS data, it operates on a pure binary data. Since the hash calculated over certificateRequestInfo structure matches the hash stored in signature -- the data was not tampered and there is no reason to invalidate the signature.

To summarize, your CSR conforms all RFC standards, its encoding conforms X.680 standards, the data is properly signed and is not tampered. This is why your request is successfully validated by CertEnroll and PSPKI.

Not quite, Vadims. In fact, X.690 [1] specifies the following:

11.6 Set-of components

The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared as octet strings with the shorter components being padded at their trailing end with 0-octets.
NOTE – The padding octets are for comparison purposes only and do not appear in the encodings.

If you go and examine the CSR that I attached with an ASN.1 parser, you'll see that the SET OF components (that is, the components of the multi-value RDN found in the Subject field) are not in the order prescribed by X.690. According to RFC2986, the signature must be computed over the DER encoding of the certificateRequestInfo structure, which implies re-ordering the SET OF components according to X.690 §11.6 before computing the signature. Of course, CSR verification software (such as PSPKI in this case) should do the same when verifying the signature. I am aware that most PKI tool(kit)s do not do this, but that's an error.

[1] https://www.itu.int/rec/T-REC-X.690-202102-I/en

are not in the order prescribed by X.690

it is application responsibility to sort attributes before encoding them into TBS data.

which implies re-ordering the SET OF components according to X.690 §11.6 before computing the signature

it does not imply that. Once TBS data is encoded by client, it is no longer modified and signature is calculated over encoded TBS data as is (i.e. context-agnostic). Same occurs during signature validation.

but that's an error

I can't agree that this is an error.

Okay, I'm not going to insist for now.

It should be kept in mind that this kind of interpretation (of how verifying a CSR signature should work) can, in some cases, lead to problems. For example, some CAs reject CSRs like the one attached here, as they verify CSR signatures using libraries that strictly implements RFC2986 in the sense I described above.

For example, some CAs would reject CSRs like the one here attached, as they verify CSRs using a well known and widely used library that strictly implements RFC2986 in the sense I described above.

And some CAs would accept CSRs like the one in this thread. It is more about different compatibility levels. Some are more restrictive, some are less. I agree that my implementation is less restrictive (or less strict), but I don't consider this a bug. Windows CA will be happy with such request (although the subject will be rebuilt in issued certificates to avoid multi-valued attributes), for example.

But it still has nothing to do with signature validation as you originally claimed, because TBS data is not modified in any way during signing and signature validation.

But it still has nothing to do with signature validation as you originally claimed, because TBS data is not modified in any way during signing and signature validation.

I never claimed that the TBS data is modified in any way during signing and signature validation; indeed, the signature in the attached CSR is cryptographically valid (i.e., it was actually computed with the private key corresponding to the public key within the CSR).

The problem is, the data that were signed are not the data that should have been signed, according to RFC2986. And this should be detected by a CSR validation software, IMO. Unfortunately, in order to detect this kind of problem (which, to be honest, is rather infrequent), the CSR validation software should DER-encode the TBS data, and not assume it is already DER encoded. And this is a bit tricky, but not so much.

the signature in the attached CSR is cryptographically valid

ok, this one is clear that the CSR signature is cryptographically valid.

The problem is, the data that were signed are not the data that should have been signed, according to RFC2986

and this falls to client application that generated and signed the content. And we figured out that Subject is RFC2986 compliant, while the DER encoding of SET OF is not.

the CSR validation software should DER-encode the TBS data

CSR validation does not assume or otherwise imply data re-encoding (I said this in my previous responses already). CSR validation function only validates existing content in existing encoding (i.e. "as is"). There is no requirement for validation logic to have any encoder at all (i.e. may have ASN.1 reader, but not builder/writer). Validation function may have partial RFC implementation (i.e. support only subset of it). If validation function can tolerate unsorted SET OF and it does not produce side effects, then there is little to no reason why request must be rejected by validation function. Even if application cannot understand the syntax and it is not critical to subsequent processing (does not produce negative side effects), validation logic may fail-safe on this error and ignore it.

Let's extend this particular example to the next practical level: CSR validation function is not required to parse X.500 subject in request, because it is in fact optional (encoded as empty SEQUENCE) if CA uses other means to construct the subject. This makes strict SET OF validation in DN double-useless. It is handy, but not strictly required by implementation.

Like, DER dictates that if field attributed with DEFAULT keyword and its value is default, the field must not be encoded (for example, critical field in X.509 extension). Should validation function fail if field with DEFAULT attribute is explicitly encoded and has default value? Or when CMS produced by OpenSSL use indefinite length which is not permitted by DER. But validation functions often decode CMS in BER mode, thus allow better compatibility level.

I would like to clarify that I too do not see major problems (except interoperability problems with other toolkits/libraries) deriving from an interpretation of RFC2986 like yours, which after all is the most common interpretation (with some notable exceptions). As to whether that is the correct interpretation, we have different opinions :)

I would also like to clarify that I really appreciate your articles at https://www.pkisolutions.com/thepkiblog/

Thank you for discussing, and keep up your excellent work at PKI Solutions!