Error unmarshaling request

Im currently developing a client from scratch, but Im now stuck at the finalize step. Everything else works.

It says:

{
  "type": "urn:ietf:params:acme:error:malformed",
  "detail": "Error unmarshaling finalize order request",
  "status": 400
}

What does that mean?

my CSR object looks like this:

{"csr": "MIIBPjCBxAIBADATMREwDwYDVQQDDAhzZWJiZS5ldTB2MBAGByqGSM49AgEGBSuBBAAiA2IABORdGREVVncBlEzKeTyw97X/s65sLmsxZLU3rSXJGcYnwxAwTGE/7C09Tso9b0C6qj1CAdGbb6LIYpb7kNFDU7z1BKXg27IMWasmUkDSQS5Swj68Ytsh4sJJFZ75MsHt56AyMDAGCSqGSIb3DQEJDjEjMCEwHwYDVR0RBBgwFoIIc2ViYmUuZXWCCiouc2ViYmUuZXUwCgYIKoZIzj0EAwMDaQAwZgIxAIE7IFpwIYY0pbDTLj4trYZp/+DQzmfFaSTa7kAOjzk2foB+Yu8f9R5F26r/tEFEVAIxAP0aYlAXnV6Jl6xI7h6jkG3WTk+xYUWfinRGH6gCdT9b78FZG7nsoTxC3r7J0z0rYQ=="}

and it unpacks to:

verify OK
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: CN = sebbe.eu
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                pub:
                    04:e4:5d:19:11:15:56:77:01:94:4c:ca:79:3c:b0:
                    f7:b5:ff:b3:ae:6c:2e:6b:31:64:b5:37:ad:25:c9:
                    19:c6:27:c3:10:30:4c:61:3f:ec:2d:3d:4e:ca:3d:
                    6f:40:ba:aa:3d:42:01:d1:9b:6f:a2:c8:62:96:fb:
                    90:d1:43:53:bc:f5:04:a5:e0:db:b2:0c:59:ab:26:
                    52:40:d2:41:2e:52:c2:3e:bc:62:db:21:e2:c2:49:
                    15:9e:f9:32:c1:ed:e7
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:sebbe.eu, DNS:*.sebbe.eu
    Signature Algorithm: ecdsa-with-SHA384
         30:66:02:31:00:81:3b:20:5a:70:21:86:34:a5:b0:d3:2e:3e:
         2d:ad:86:69:ff:e0:d0:ce:67:c5:69:24:da:ee:40:0e:8f:39:
         36:7e:80:7e:62:ef:1f:f5:1e:45:db:aa:ff:b4:41:44:54:02:
         31:00:fd:1a:62:50:17:9d:5e:89:97:ac:48:ee:1e:a3:90:6d:
         d6:4e:4f:b1:61:45:9f:8a:74:46:1f:a8:02:75:3f:5b:ef:c1:
         59:1b:b9:ec:a1:3c:42:de:be:c9:d3:3d:2b:61

Whats wrong?

(Also tried to sign the CSR with ecdsa-with-SHA256 if that would help in case LE don't support ecdsa-with-SHA384, but that didn't help either)

ooh solved it.

Apparently its not enough to just strip BEGIN CERTIFICATE REQUEST and END CERTIFICATE REQUEST from the CSR. I have to convert it from b64 to b64_url.

This was not clear in the example provided in the RFC 8555.

And logically it also don't make sense - since the outer JSON object is b64_url encoded, its "protected" from being "malformed" in the POST request, so technically the inner CSR object should not need it.

Maybe the error message should be more clear - atleast in staging where it doesn't matter so much if "sensitive" information is leaked.

Like: "non-base64-url characters found in CSR" or similiar. Then its immediately clear why its not going through.

My HTML JS ACME2 client works now :slight_smile:

1 Like

it's base64url encoded binary data (der): JWS RFC 7515 - JSON Web Signature (JWS) says it uses base64url to carry signature, so acme used it as is

1 Like

yeah but technically it only applies to the outer container. (the JWS web signature standard)

thats why I found it weird that it also requires the inner CSR to be urlsafe too (which already is packed into a b64_url container).

It wasn't also clear either in the standard, its easy to think you can just cut the BEGIN CERTIFICATE REQUEST and END CERTIFICATE REQUEST from a standard one and put in, but nope, you need to b64 --> b64_url translate it too.

1 Like

Only if you're thinking of a CSR as being the PEM-encoded version within those "CERTIFICATE REQUEST" headers. But if you look at the specifications (like RFCs 2986 & 5967), you'll find them mainly thinking of them in ASN.1 DER/BER format, with any conversion to ASCII-only being a separate additional step. And RFC 8555 specifically does call out the difference: "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.)"

If you're converting a CSR to PEM, and then from PEM to base64url, it sounds to me like you're just adding extra steps. It may work fine, and may be the easiest way to do it for the language/library you're using, but it's not how the standards describe the process.

2 Likes

yeah, the thing is that the PEM is "de facto standard" and have been the whole time. You might remember when people ran IIS and they outputted DER-encoded CSRs (.der files). You actually had to run them through a PEM converter for the CA to accept them. Some CA's did accept the DER's however.

Thats why it became so "weird" for me when you actually had to b64_url it, and not just b64 it, because if it would accepted b64, you just strip the headers and put in the resulting string.

And yeah, you actually have to convert them to PEM in this case, theres and then from PEM to b64_url, since theres no builtin function in javascript to actually b64_urlencode, so you still have to go through the PEM step, and then do a search/replace on the b64 string to make it urlsafe.

The spec is pretty explicit about how the CSR should be passed. From section 7.4

Once the client believes it has fulfilled the server's requirements,
it should send a POST request to the order resource's finalize URL.
The POST body MUST include a CSR:

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

2 Likes