Finalize Order URL returning error

I am writing code for Let's Encrypt clients in the staging environment using APIs.
I am able to pass the dns-01 authorization also. Now, when I try to finalize the order, I am getting a bad syntax error(400)

My question is how to pass the CSR value? I am following the RFC8555 documentation-

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": "uOrUfIIk5RyQ...nw62Ay1cl6AB"
   }

According to my understanding for the payload, generate CSR using some website-

-----BEGIN CERTIFICATE REQUEST-----
MIICYDCCAUgCAQAwGzEZMBcGA1UEAwwQdG9vbHNndXJ1aHViLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAOnIalknEZIUU7ZW+xdtnE1j3qox6viB
2PQCsGLJnV6a88j1kHqH7X92WSeJsW73GE75dkjsVX9nHy03MA8Rz6FRzR5rIq95
IJwml/S9tQ+hH0eH9BJJ78niqZ2s6Xf/CBN9jYsbnNA0DB3ZjEFjrvNT8g46AQch
w7Ij7NxnYI6Vcz+tNUtd968fvY4Ewslc/ishX5B8OrNCAhOEn+0K3K91KPEC3FAO
vgdO8rVHSx97CsqG55UTBRSRGd2foZfBs9cb/mxcu46QuNhnuS3qauvDTxj0d+Ut
96Xo7qS8br0xR9S0lS6+PAnqJ+YiWS7Lqj5qrbxF0OaESnOwA+XdmIcCAwEAAaAA
MA0GCSqGSIb3DQEBCwUAA4IBAQC5wkPhZhuZMwxhA0IokHTxAtIToMytwQdia3yb
nC9RgGL9+2r/LsFmoJkXJYKRpUX6yu4x9OSVe2TyL6t45g2e6zRm8mIsWVyvuj4J
+QA7RUca8buuX4NIrRRrO7tU/LFVXTZ+WoVuFNEUoifls9hJ7ljaqOBuvK5K6vF/
Yn0Kx8TcFme335gFl/O182QXZXJSHNy7axdvCLgy/Y6/HE7kevs6/ITv3BWVWhX1
3Q+dNlov0IB4H0bgiylPKOCpbbg4RmOSKJ3uHy3JlUdO5m7ZnFIdxevELM/d1VFW
ETJSu9WGCvodPB41JZ29LSmCxkfAr34190CwWeBnn73xWM9B
-----END CERTIFICATE REQUEST-----

remove the headers, make the whole CSR into a single line, generate a JSON using this as {"csr":"<THE_RESULTING_CSR>"}, and encode it to base64url.

Is this correct or am I missing something?

My payload-

eyJjc3IiOiJNSUlDWURDQ0FVZ0NBUUF3R3pFWk1CY0dBMVVFQXd3UWRHOXZiSE5uZFhKMWFIVmlMbU52YlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTmZXd1FKK1FkSGRRVGVpalNkRTArUHB6a3FrWUNkTytYSFo5alZxQ0VYZ0RBU0F1Nm9KaGplTS9wUkdoYndmZnRRRWV2a0ZuYm1GUXFCRjhjR0JFRXJKTnlSOC9oU1NmUXZuNmZKY2dERWRDV0JLelpuZ3lVWnZpWHZOOEEvZ2JReWRFOUtpbVZSRnFhRmFMUFlTZlVKdC9UNkRISjJSU1MzZGNGQ2toa2oxWkVOQnA4V1VuRGVLbUlTa05xRENtRzEzT3ZiNml4WXhEbE9teGRFL1NwVHZuQ1RZdDdjQ01FRGZjaDNka2FCU0NFTVpNVkk4ZXoxUGkyMVF2cjA3anJFUU1QOThKSHJ3cDZ5ajVyQTg3bk41MThhYkgyZ3NEbmwvby9FQXQ3UHFvd3RPRTV6c3crQ0VwaEN0cFBSSHdFamgzY3RpRnREUG0vYm5xdGg4UkJNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBNVBrbXA3WTAwQ1h4cUNCdGttdUM4ZGlBOG5BcHFaamMxclVvWkJGSWxUM1R4dVg0eEZhY1YwQTBWMVN3Qk1kQTRFZXFKa25ONW0yUzh2ajNyU3k2SVF4cFVlYXlpbGpQcVFtelFQWGx6S1BiMXR6N3NnSjg2NDN4bkdjNkJVeG02NVNGakhvUFp4L2k5bUxBMUFYT212R2IxS0o0WXhpeXFqQXA1d1FFQUZqN2gwNVdJclducytjTVQ2Uk5UR3dlRkdabUV3eHp3U0J2Q1FSQVZPQVN2RHhoWG45bU12cGpEaUVWRzRGS28xK2xvRHdxRCtjUHBMRHFjSUhhYkVZVnZmY3k3VUNFa1FpdFFZV1d6OFVUV3E3N2YwbEdMS3U4Njc1S1ZmdXMxbGFMUFdWejZ1SE9hKzQxd2w5S3g0U0JZL0M4Vm1oOUM4ajBnU3hobjROTUMifQ

My protected field-

eyJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2ZpbmFsaXplLzEyOTM3OTMyNC8xMzA1NjI0MjM5NCIsImtpZCI6Imh0dHBzOi8vYWNtZS1zdGFnaW5nLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvYWNjdC8xMjkzNzkzMjQiLCJub25jZSI6Im1NcGJXT2xMdVFSNTN2T1h4WVl1NEkxOE1QbXpsNUloSTZfQUF6UWxfYjZtdVFVR1JSRSIsImFsZyI6IlJTMjU2In0

I think your problem is that you're Base64URL encoding the already Base64 encoded CSR contents. You need to either decode the CSR back into a byte array before re-encoding it to Base64URL or just transform the Base64 string into Base64Url by removing any trailing = and replacing + and - with / and _.

7 Likes

Yes; I suspect that's it. Per the spec:

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

The "PEM" format is the base64 one using that -----BEGIN CERTIFICATE REQUEST-----, but ACME requires the direct DER-formatted request, encoded in base64url instead.

5 Likes

I strongly hope that's just for testing purposes?

4 Likes

It's for testing only. But is there any issue with generating the CSR using some other website in production environment?

If you provide anyone with the private key, then yes [very problematic indeed].

6 Likes

Leaking the private key to a third party to generate the CSR would mean you'd have to revoke the issued cert immediately after issuance. So that doesn't make much sense to begin with.

2 Likes

I just wanted to share the following suggestions for your client development:

1- build discrete functions for each step/payload and write unit tests for them

2- use the Pebble (GitHub - letsencrypt/pebble: A miniature version of Boulder, Pebble is a small RFC 8555 ACME test server not suited for a production certificate authority.) server locally to build the client/tests against, and run your unit tests against it

3- use the Staging API for integrated tests and release testing

You can certainly build everything against the Staging API, but most people find things progress much faster and more smoothly with a local test server -- and Pebble is perfect for this use

6 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.