[RESOLVED] Various errors while generating a CSR, Not sure which is "better", need help finding a bug

I'm trying to figure out what these errors mean and what i am doing wrong


 const out = JSON.stringify({csr: jose.base64url.encode(await generateCSRWithExistingKeys(subject, keyPair.publicKey, keyPair.privateKey))});

{"csr":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQkx6Q0IxUUlCQURCaU1Ba0dBMVVFQmhNQ1ZWTXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGhNQlFHQTFVRQpCeE1OVTJGdUlFWnlZVzVqYVhOamJ6QWFCZ05WQkFvVEUwVjRZVzF3YkdVZ1EyOXljRzl5WVhScGIyNHdFQVlEClZRUURFd2x6YzJ3dVltOWhkSE13YWpBS0JnZ3Foa2pPUFFNQkJ3TmNBREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUcKU000OUF3RUhBMElBQlB1VkJsOGhjSU95US9HenVOaDk2UlhEYUlaa2pZcDNUVkd2Qkc3YTZ2c3FSTnlnUEsvcwpweUptOE5MS2RXdHlwZG9xaWVWVzk2Yy9vNi81cHpTSXM2b3dBREFLQmdncWhrak9QUVFEQWdOSkFEQkdBaUVBCmd2clo4d3J5KzdGSStKVXVPMjUrOXdKYSt0WW1vNm1lQU5Jcm51Tnh0cmtDSVFERDZNS1lPa0Qwb0V2MFlzUVQKdUlBK29EdkJpL2t2dEsyVm93WURramVYUmc9PQotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0"}

Error getting order {
  type: 'urn:ietf:params:acme:error:malformed',
  detail: "Error parsing certificate request: asn1: structure error: tags don't match (16 vs {class:0 tag:13 length:45 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} certificateRequest @2"

Does this mean I shouldn't be calling jose.base64url.encode or is there just something wrong with the CSR ?


const out = JSON.stringify({csr: await generateCSRWithExistingKeys(subject, keyPair.publicKey, keyPair.privateKey)});

{"csr":"-----BEGIN CERTIFICATE REQUEST-----\nMIIBLjCB1QIBADBiMAkGA1UEBhMCVVMwEQYDVQQIEwpDYWxpZm9ybmlhMBQGA1UE\nBxMNU2FuIEZyYW5jaXNjbzAaBgNVBAoTE0V4YW1wbGUgQ29ycG9yYXRpb24wEAYD\nVQQDEwlzc2wuYm9hdHMwajAKBggqhkjOPQMBBwNcADBZMBMGByqGSM49AgEGCCqG\nSM49AwEHA0IABPuVBl8hcIOyQ/GzuNh96RXDaIZkjYp3TVGvBG7a6vsqRNygPK/s\npyJm8NLKdWtypdoqieVW96c/o6/5pzSIs6owADAKBggqhkjOPQQDAgNIADBFAiEA\nji7UICP+mHi70eAgsjOufDLVlBuLQfjEFiSnrSP4hJcCIEKzOtclGdX0vvIb6DuX\nRmNPgX742pKHL2jL6Sy8eYIN\n-----END CERTIFICATE REQUEST-----"}
Error getting order {
  type: 'urn:ietf:params:acme:error:malformed',
  detail: 'Error unmarshaling finalize order request',
  status: 400
}

If I don't encode the CSR then I get a new error Error unmarshaling finalize order request and i have no idea what that means even though I have looked around.

const subject = {
    commonName: 'ssl.boats', // Lets Encrypt! only supports commonName @aarongable 
};

const out = JSON.stringify({
    csr: await generateCSRWithExistingKeys(subject, keyPair.publicKey, keyPair.privateKey)
});

const protectedHeader = {
    alg: ALG_ECDSA,
    kid: kid,
    nonce: nonce,
    url: finalizeUrl,
};

const jws = new jose.FlattenedSign(new TextEncoder().encode(out));

jws.setProtectedHeader(protectedHeader);

const signed = JSON.stringify(await jws.sign(keyPair.privateKey));

const request = {
    method: 'POST',
    headers: {
        'Content-Type': CONTENT_TYPE_JOSE
    },
    body: signed
};

const response = await fetch(finalizeUrl, request);

If I do something like below I also get Error unmarshaling finalize order request which makes me think encoding it was the correct thing to do...


 const out = JSON.stringify({csr: { }});

If I do something like below I get Error parsing certificate request: asn1: syntax error: sequence truncated, which makes me think encoding it would be wrong.

const out = JSON.stringify({csr: ""});

I'm just worried there is nothing wrong with the CSR and I need to do something differently compared to the other things, any help would be appreciated, If you think it might be generateCSRWithExistingKeys I can post that, but I'm wondering if its simpler than that.


Thanks everyone who helped.

If you want help and you understand Javascript you can use server-ssl as an example

What you want to send is essentially this but without the header, footer, and line breaks.

4 Likes

Okay when I do that I get Error unmarshaling finalize order request

{"csr":"MIIBLjCB1QIBADBiMAkGA1UEBhMCVVMwEQYDVQQIEwpDYWxpZm9ybmlhMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzAaBgNVBAoTE0V4YW1wbGUgQ29ycG9yYXRpb24wEAYDVQQDEwlzc2wuYm9hdHMwajAKBggqhkjOPQMBBwNcADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPuVBl8hcIOyQ/GzuNh96RXDaIZkjYp3TVGvBG7a6vsqRNygPK/spyJm8NLKdWtypdoqieVW96c/o6/5pzSIs6owADAKBggqhkjOPQQDAgNIADBFAiEAx20lGvUmm7CEAV9BgXMIpycW9bN98qpGIfEPN1VR6e4CIAqPlUV0mF99iS+B+qC8ZewqSPzNTuQU7hJk4mVPrcYs"}
Error getting order {
  type: 'urn:ietf:params:acme:error:malformed',
  detail: 'Error unmarshaling finalize order request',
  status: 400
}

Do you see anything wrong with the CSR generation?

Also the spec would make me think the header/footer/linebreaks are required?

would Error unmarshaling finalize order request occur if identifiers were missing?

//const altNames = ['www.ssl.boats', 'ssl.boats'];

I haven't added the altNames yet, I'm worried I'm not even encoding it right because the error message keeps changing and I'm not really sure what it means and sometimes its quite vexing.

I guess what I'm really asking is what error messages are actually "bad" and which ones mean "CSR is wrong"

So the value still needs to be Base64Url encoded rather than just Base64. But I don't think that's all that's wrong. Just putting the original lines into a PEM file and parsing it with openssl doesn't work. There must be something else wrong with the CSR generation code.

I put the following in a file called test.csr

-----BEGIN CERTIFICATE REQUEST-----
MIIBLjCB1QIBADBiMAkGA1UEBhMCVVMwEQYDVQQIEwpDYWxpZm9ybmlhMBQGA1UE
BxMNU2FuIEZyYW5jaXNjbzAaBgNVBAoTE0V4YW1wbGUgQ29ycG9yYXRpb24wEAYD
VQQDEwlzc2wuYm9hdHMwajAKBggqhkjOPQMBBwNcADBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABPuVBl8hcIOyQ/GzuNh96RXDaIZkjYp3TVGvBG7a6vsqRNygPK/s
pyJm8NLKdWtypdoqieVW96c/o6/5pzSIs6owADAKBggqhkjOPQQDAgNIADBFAiEA
ji7UICP+mHi70eAgsjOufDLVlBuLQfjEFiSnrSP4hJcCIEKzOtclGdX0vvIb6DuX
RmNPgX742pKHL2jL6Sy8eYIN
-----END CERTIFICATE REQUEST-----

Then I ran this openssl command against it.

> openssl req -noout -text -in .\test.csr
unable to load X509 request
11892:error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag:crypto\asn1\tasn_dec.c:1149:
11892:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto\asn1\tasn_dec.c:572:
11892:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto\asn1\tasn_dec.c:615:
11892:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto\asn1\tasn_dec.c:646:Field=subject, Type=X509_REQ_INFO
11892:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto\asn1\tasn_dec.c:646:Field=req_info, Type=X509_REQ
11892:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:crypto\pem\pem_oth.c:33:

It obviously shouldn't error like that if it's a valid CSR. It should be showing the human readable details about it.

3 Likes

I did jose.base64url.encode on the result

-----BEGIN CERTIFICATE REQUEST-----
TUlJQkJEQ0JyQUlCQURBNU1Ba0dBMVVFQmhNQ1ZWTXdHZ1lEVlFRS0V4TkZlR0Z0Y0d4bElFTnZjbkJ2Y21GMGFXOXVNQkFHQTFVRUF4TUpjM05zTG1KdllYUnpNR293Q2dZSUtvWkl6ajBEQVFjRFhBQXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUWVJWTdUU1RhRUtNNTNqRGNvVXFaK2ZRc1J3YnZVT1YzbnhSNDVqYnBDN2RjUkN1OUt4aUxIaHpFcXl2Szc5bU1hUVU5V1BvZWVtQk10L0tGNlZLS2lNQUF3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVGtzNzR3eGhvdUJ1d1Z1ZnB4dFoySzlma1Ezejd0NzFXblFaWWhrRHNkSUNJSFFaOXZoUm42S1hGWGY0VkZNSmZ3SjljRDd0cG8zdW1hSG5jZEpnUE4vbQ
-----END CERTIFICATE REQUEST-----

now i get the message

{
  type: 'urn:ietf:params:acme:error:malformed',
  detail: "Error parsing certificate request: asn1: structure error: tags don't match (16 vs {class:1 tag:13 length:73 isCompound:false}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} certificateRequest @2",
  status: 400
}

Is this error message "better" than Error unmarshaling finalize order request ??
Can I focus on fixing the CSR if its complaining about ASN1? or does that mean it can't even read it?

Thanks for explaining how to test a CSR

I would think so. You should be able to sort out the CSR generation issues completely outside the scope of ACME. If it helps, here's an example ec-256 CSR I use in my own client's test suite.

-----BEGIN CERTIFICATE REQUEST-----
MIIBfTCCASMCAQAwGDEWMBQGA1UEAwwNKi5leGFtcGxlLmNvbTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABFSmbmxUTUw/r9Is8o0FfxnBtkNyi9hh0bMxQfEkjF+a
zXxzogp687LCIvYxO4FTbFec/oD0DvwRwX3N/YmyuBiggagwgaUGCSqGSIb3DQEJ
DjGBlzCBlDAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwOQYDVR0RBDIwMIINKi5leGFtcGxlLmNvbYILZXhh
bXBsZS5jb22CEiouc3ViMS5leGFtcGxlLmNvbTAdBgNVHQ4EFgQUcynVm4eI1v4L
gwrSPY9aDUtpObkwCgYIKoZIzj0EAwIDSAAwRQIgDy37lkfojwhEggLsduTST6eT
ztRyXDfMFA8U4TwG3BUCIQCV7vOSkjbtaZTfu/z51Q77mrYRzDlbLZFEeoElfdnr
pg==
-----END CERTIFICATE REQUEST-----

And the associated openssl output:

Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: CN = *.example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:54:a6:6e:6c:54:4d:4c:3f:af:d2:2c:f2:8d:05:
                    7f:19:c1:b6:43:72:8b:d8:61:d1:b3:31:41:f1:24:
                    8c:5f:9a:cd:7c:73:a2:0a:7a:f3:b2:c2:22:f6:31:
                    3b:81:53:6c:57:9c:fe:80:f4:0e:fc:11:c1:7d:cd:
                    fd:89:b2:b8:18
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
        Requested Extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Subject Alternative Name:
                DNS:*.example.com, DNS:example.com, DNS:*.sub1.example.com
            X509v3 Subject Key Identifier:
                73:29:D5:9B:87:88:D6:FE:0B:83:0A:D2:3D:8F:5A:0D:4B:69:39:B9
    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:20:0f:2d:fb:96:47:e8:8f:08:44:82:02:ec:76:e4:
         d2:4f:a7:93:ce:d4:72:5c:37:cc:14:0f:14:e1:3c:06:dc:15:
         02:21:00:95:ee:f3:92:92:36:ed:69:94:df:bb:fc:f9:d5:0e:
         fb:9a:b6:11:cc:39:5b:2d:91:44:7a:81:25:7d:d9:eb:a6

And here's the Base64Url equiv that gets sent in the finalize call:

MIIBfTCCASMCAQAwGDEWMBQGA1UEAwwNKi5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFSmbmxUTUw_r9Is8o0FfxnBtkNyi9hh0bMxQfEkjF-azXxzogp687LCIvYxO4FTbFec_oD0DvwRwX3N_YmyuBiggagwgaUGCSqGSIb3DQEJDjGBlzCBlDAJBgNVHRMEAjAAMA4GA1UdDwEB_wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwOQYDVR0RBDIwMIINKi5leGFtcGxlLmNvbYILZXhhbXBsZS5jb22CEiouc3ViMS5leGFtcGxlLmNvbTAdBgNVHQ4EFgQUcynVm4eI1v4LgwrSPY9aDUtpObkwCgYIKoZIzj0EAwIDSAAwRQIgDy37lkfojwhEggLsduTST6eTztRyXDfMFA8U4TwG3BUCIQCV7vOSkjbtaZTfu_z51Q77mrYRzDlbLZFEeoElfdnrpg
2 Likes

I still don't know what It means yet but i can create these tests automatically now so I can stop sending junk to the server until I figure it out

Screenshot 2024-11-23 203022

J@BUD MINGW64 ~/Desktop/server-ssl (question)
$ openssl req -noout -text -in test.csr
40270000:error:068000A8:asn1 encoding routines:asn1_check_tlen:wrong tag:../openssl-3.2.3/crypto/asn1/tasn_dec.c:1186:
40270000:error:0688010A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:../openssl-3.2.3/crypto/asn1/tasn_dec.c:349:Type=X509_REQ
error: unable to load X509 request from file 'test.csr'

Yes: it means that we've parsed the JSON and parsed the base64 PEM, but are failing to parse the DER. In other words, you've sent something that looks like a CSR, but doesn't quite match the RFC5280 profile of exactly which fields must be present and which fields must be missing.

2 Likes

That is very very interesting, maybe I missed some padding somewhere?

I didn't send the SANs but I thought they were optional? would that cause it to fail?

I think this error encoding routines:asn1_check_tlen:wrong tag: might mean I'm doing something in the wrong order?

Thanks for the help, Its easier to work on things when you know you are at least going in the right direction.

It's not just padding. I can tell that your PEM doesn't encode a proper CSR because it doesn't start with the correct sequence of base64 characters -- all certs and CSRs start with the same first few characters (MII, but yours starts with LS0) due to the way ASN.1 DER encoding works.

I think you need to familiarize yourself with this format more. I recommend A Warm Welcome to ASN.1 and DER - Let's Encrypt

Edit: ah wait, I see you do have a CSR that starts with MII (and it parses successfully!) -- it's just that you were sending it wrapped in the PEM headers. Looks like you need to find a happy medium where you're sending the right PEM, but not wrapped in headers.

2 Likes
Ready to Finalize
[
  '-----BEGIN CERTIFICATE REQUEST-----',
  'MIIBBTCBrAIBADA5MAkGA1UEBhMCVVMwGgYDVQQKExNFeGFtcGxlIENvcnBvcmF0\n' +
    'aW9uMBAGA1UEAxMJc3NsLmJvYXRzMGowCgYIKoZIzj0DAQcDXAAwWTATBgcqhkjO\n' +
    'PQIBBggqhkjOPQMBBwNCAAQeIY7TSTaEKM53jDcoUqZ+fQsRwbvUOV3nxR45jbpC\n' +
    '7dcRCu9KxiLHhzEqyvK79mMaQU9WPoeemBMt/KF6VKKiMAAwCgYIKoZIzj0EAwID\n' +
    'SAAwRQIgRTiJFtXg6ZTwVY7UVv4BFhA7NbqDZ59rAE21NfSiZc0CIQDTWZiJ00N0\n' +
    '0SjPfD9/7SjGp9115RlAmVlRbhXbbWr95g==',
  '-----END CERTIFICATE REQUEST-----'
]

So this at least starts correctly?

meaning I have at least parsed the CSR into the request correctly and these errors aren't caused because I'm just sending garbage?

1 Like

My advice is always: don't flail, go read the spec again. RFC 8555 says that a Finalize request needs to look like this:

   POST /acme/order/TOlocE8rfgo/finalize HTTP/1.1
   Host: example.com
   Content-Type: application/jose+json

   {
     "protected": base64url({
       "alg": "ES256",
       "kid": "https://example.com/acme/acct/evOfKhNU60wg",
       "nonce": "MSF2j2nawWHPxxkE3ZJtKQ",
       "url": "https://example.com/acme/order/TOlocE8rfgo/finalize"
     }),
     "payload": base64url({
       "csr": "MIIBPTCBxAIBADBFMQ...FS6aKdZeGsysoCo4H9P",
     }),
     "signature"

So that's: a base64-encoded CSR (note the MII!), inside a JSON string value whose key is "csr", then all of that is base64url-encoded and put as the string value of a key called "payload" inside the JWS. Are you sure you're doing all of that? If yes, then what's the current error you're getting?

3 Likes

I will add: it looks like your CSR has a bunch of Subject fields (country, stateOrProvinceName, localityName, and organizationName) that we will not respect and might cause us to reject the request. I would expect us to have a better error message if that's the problem, though...

1 Like

I didn't know which ones I needed, at the moment my subject is just this

const subject = {
            commonName: 'ssl.boats',
            organizationName: 'Example Corporation',
            countryName: 'US',
        };
1 Like

We only accept common name. This is because validation of those other kinds of fields (which would make the certificate Organization Validated (OV), or Extended Validated (EV)) cannot be automated via the ACME protocol. All certs we issue contain only a Subject Common Name, a Subject Alternative Names extension, or both, and are therefore Domain Validated (DV) certs.

2 Likes

The SAN is not part of the Subject. Aaron already told you the subject should only contain the CommonName.

The SAN is an extension of the CSR (or certificate for that matter), not part of the subject.

Also, are you developing this for your own interest or to learn more about the protocol? Or are you seriously considering developing your own ACME client, including doing all the basic ASN.1 manually? Because if it's the latter, I strongly recommend to just use an existing library to do it for you.

2 Likes

No: Subject Alternative Names are a separate extension within the certificate, not a part of the Subject.

See RFC5280 for a description of the SAN extension: RFC 5280 - Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile

See a sample cert, for an example of the SAN extension being encoded correctly: https://crt.sh/?asn1=14765092435

1 Like

You have to send the CSR in DER format base64_url encoded.

If you modify generateCSRWithExistingKeys to return the DER instead of the PEM format, it should work.

RFC:

csr (required, string): A CSR encoding the parameters for the certificate being requested [RFC2986]. The CSR is sent in the base64url-encoded version of the DER format. (Note: Because this field uses base64url, and does not include headers, it is different from PEM.)

3 Likes

thanks @stewe, that is what @aarongable was talking about in his first reply.

1 Like