Api Acme /finalize

If I use zlint to parse that CSR (convert base64url to base64 or decode to DER first), I'm getting the following error:

FATA[0000] unable to parse certificate: asn1: structure error: tags don't match (6 vs {class:0 tag:17 length:24 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} ObjectIdentifier @2 

I have no clue what so ever what that means.

An online ASN.1 decoder seems to be happy with it: ASN.1 JavaScript decoder

1 Like

@protypangel: You must never upload or expose a private key. Please immediately revoke any certificates that are using the key you posted, destroy it, and generate a new private key.

5 Likes

don't see any private key there? just public key.pem

1 Like

Yes, I edited the post.

5 Likes

zlint expects its input to be a Certificate, not a CSR, so I would not expect this to work regardless.

I can reproduce this error here.

DER, the binary encoding format for ASN.1 data, is a tag-length-value encoding: each field begins with a tag (telling the decoder what field it is), then a length (telling the decoder how many bytes to consume), and then the data itself.

This error comes from here in the Go standard library's asn.1 parser. It means that the CSR contains a field which claims to be a certain length (e.g. 100 bytes), but in fact the CSR runs out of bytes to parse first (e.g. it had only 99 more bytes worth of data before the end of the string).

What is the exact command that you used to generate this CSR?

7 Likes

Hmm, I thought I read somewhere zlint also was able to check CSRs, but I can't find it a second time. Maybe I'm mistaken with some other linter, silly me.

1 Like

Taking the "csr" field, decoding it, and having openssl parse it works for me:

echo MIICXTCCAUUCAQAwGjEYMBYGA1UEAxMPYWwtbXVtaW51bmUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4sS2nJxk-bnqwvMf1GIcxRh-ufFIvF_LoD2e4JIcUEjJbuBZ_V2ldjl8VqNE0pjiISXCrtyLIiUsoTvStlE7MMFbPV26hS8CNjKP4x93ZOkELZMdPpE4IIwn1NCkYU2XSJEz-6U1uFlTXP-2bg52VqQI3pd8aNryUsKFWBk4vlnCU2H327eWrvIG5t82z7XHsunl_62PqUxVt64sOv38QC59NQLVCxqqxZvBr-eRuxIhqK3olWk6tKUsP-BjCH_GzLfcj-RWqhSe1n9t42yxIZvts2JjaUvrVigqYMWSsQ62kBHqSEIk6EEScVHV2vwZkjVDdgr7hcINERbG_DPhFwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQDgy5DnTHG_mKKWed6ADX_4xIWDNdxXZfdAWxbIBKwiWJBfAA8SqiuuC4HLtOBmrkiFim-b2P4zd7uCH5b99oUgL9CTmyn4X-_gW4EYTaisSPZ_10ZnWREzUikwysRAGMNOgCVbYoIHrMY6tobsIghjAjrAuxRQxwumy8YmuLecUa5k9ZjQVku2ydnCzA30LSJR7ynVkxQf1W0KYAOC_6hQ1hhIaEya-U2hoV5TH4js28SWkI1uK9U42eGOt9f6Fu_mKljZjwiaqh3HufHka8RgKTh0PVZfJbss4wyTl6Dv6tuIv4z6wraw-2wbsiWjuDT2P9PrrxfppDG1qqBZffPZ | base64 -D | openssl req -inform DER -noout -text -in -
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: CN=al-muminune.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:e2:c4:b6:9c:9c:64:f9:b9:ea:c2:f3:1f:d4:62:
                    1c:c5:18:7e:b9:f1:48:bc:5f:cb:a0:3d:9e:e0:92:
                    1c:50:48:c9:6e:e0:59:fd:5d:a5:76:39:7c:56:a3:
                    44:d2:98:e2:21:25:c2:ae:dc:8b:22:25:2c:a1:3b:
                    d2:b6:51:3b:30:c1:5b:3d:5d:ba:85:2f:02:36:32:
                    8f:e3:1f:77:64:e9:04:2d:93:1d:3e:91:38:20:8c:
                    27:d4:d0:a4:61:4d:97:48:91:33:fb:a5:35:b8:59:
                    53:5c:ff:b6:6e:0e:76:56:a4:08:de:97:7c:68:da:
                    f2:52:c2:85:58:19:38:be:59:c2:53:61:f7:db:b7:
                    96:ae:f2:06:e6:df:36:cf:b5:c7:b2:e9:e5:ff:ad:
                    8f:a9:4c:55:b7:ae:2c:3a:fd:fc:40:2e:7d:35:02:
                    d5:0b:1a:aa:c5:9b:c1:af:e7:91:bb:12:21:a8:ad:
                    e8:95:69:3a:b4:a5:2c:3f:e0:63:08:7f:c6:cc:b7:
                    dc:8f:e4:56:aa:14:9e:d6:7f:6d:e3:6c:b1:21:9b:
                    ed:b3:62:63:69:4b:eb:56:28:2a:60:c5:92:b1:0e:
                    b6:90:11:ea:48:42:24:e8:41:12:71:51:d5:da:fc:
                    19:92:35:43:76:0a:fb:85:c2:0d:11:16:c6:fc:33:
                    e1:17
                Exponent: 65537 (0x10001)
        Attributes:
            Requested Extensions:
    Signature Algorithm: sha1WithRSAEncryption
    Signature Value:
        e0:cb:90:e7:4c:71:bf:98:a2:96:79:de:80:0d:7f:f8:c4:85:
        83:35:dc:57:65:f7:40:5b:16:c8:04:ac:22:58:90:5f:00:0f:
        12:aa:2b:ae:0b:81:cb:b4:e0:66:ae:48:85:8a:6f:9b:d8:fe:
        33:77:bb:82:1f:96:fd:f6:85:20:2f:d0:93:9b:29:f8:5f:ef:
        e0:5b:81:18:4d:a8:ac:48:f6:7f:d7:46:67:59:11:33:52:29:
        30:ca:c4:40:18:c3:4e:80:25:5b:62:82:07:ac:c6:3a:b6:86:
        ec:22:08:63:02:3a:c0:bb:14:50:c7:0b:a6:cb:c6:26:b8:b7:
        9c:51:ae:64:f5:98:d0:56:4b:b6:c9:d9:c2:cc:0d:f4:2d:22:
        51:ef:29:d5:93:14:1f:d5:6d:0a:60:03:82:ff:a8:50:d6:18:
        48:68:4c:9a:f9:4d:a1:a1:5e:53:1f:88:ec:db:c4:96:90:8d:
        6e:2b:d5:38:d9:e1:8e:b7:d7:fa:16:ef:e6:2a:58:d9:8f:08:
        9a:aa:1d:c7:b9:f1:e4:6b:c4:60:29:38:74:3d:56:5f:25:bb:
        2c:e3:0c:93:97:a0:ef:ea:db:88:bf:8c:fa:c2:b6:b0:fb:6c:
        1b:b2:25:a3:b8:34:f6:3f:d3:eb:af:17:e9:a4:31:b5:aa:a0:
        59:7d:f3:d9

Maybe the problem is the SHA1 signature?

It's just pock so you don't need to worry about the private key.
How do you will generate the secure part of the api to proof that everything is okey if you don't have the private key ?

Maybe it's this part :

  PKCS10CertificationRequest pkcs10 = new PKCS10CertificationRequest(
      "SHA1withRSA", // here
      subject,
      keyPair.getPublic(),
      null,
      keyPair.getPrivate()
  );

I used SHA1 when i sign with SHA256.

Edit : Still doesn't work with SHA256withRSA :frowning:

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: "{<EOL>  "type": "urn:ietf:params:acme:error:malformed",<EOL>  "detail": "Error parsing certificate request: asn1: syntax error: sequence truncated",<EOL>  "status": 400<EOL>}"

I see you figured out that I was talking about the signature in the CSR itself. But as you note fixing that doesn't change anything.

I took at look at what getssl does since that's what I use, and here's the openssl command it runs:

openssl req -new -sha256 -key "$csr_key" -subj "$CSR_SUBJECT" -reqexts SAN -config "$tmp_conf"

The -reqexts option is specifying a section of a generated OpenSSL config file that is listing all of the SANs for the certificate. It looks like getssl will ALWAYS put in SANs in the CSR, even if there is only one domain. I know that RFC 8555 says putting the domain name in the subject is sufficient, but maybe you could try putting the domain name in the SAN list as well?

Edit: It looks like that is your problem. See here: boulder/docs/acme-implementation_details.md at main · letsencrypt/boulder · GitHub

2 Likes

what's the tmp config ?
The csr_key is the private key ?
I imagine the csr_subject is just "CN=al-muminune.org, SAN=al-muminune.org"

The SAN is an extension, not part of the subject.

1 Like

Osiris already explained that the SAN is a certificate extension, not part of the subject. I do not know how you put certificate extensions in your CSR using the API you are using; I'm assuming it's via one of the arguments you pass in as a null. You're going to have to figure that out, but I am 99% sure your API has that ability, as this is extremely common thing to do.

My reading of the getssl code is that the default openssl configuration file is copied to a temporary location (defined by the variable tmp_conf) and the following config file fragment is appended to the end of it:

[SAN]
subjectAltName=DNS:host.name.one,DNS:host.name.two,DNS:host.name.etc.cetera

The default openssl configuration file is determined by running openssl version -d and looking for openssl.cnf in the OPENSSLDIR value returned by that command. The -reqexts SAN option means "Add the stuff specified in the SAN section of the config file as an extension to the CSR". The exact details of that format is specified in the openssl man page x509v3_config.

FYI, there are two different openssl behaviors regarding this.

The first example is for OpenSSL until v1.1.1 and uses -reqexts; the second example applies to v1.1.1 and onwards - including the 3.x version

I illustrate them in the following docstring:

3 Likes

Oh, good to know! It looks like using -addext is much simpler.

1 Like

Yes, it's only a slight API improvement, but is significantly easier to execute.

There were a bunch of API and behavior changes around 1.x that often trip people up due to older docs/posts/howtos. This is one of them.

4 Likes

I understand nothing.
Can you please write the commande of openssl ?

In that case you might want to reconsider writing such a "ssl generator" for your website yourself, but stick to existing tooling.

3 Likes

are you okey ?
I ask question to upgrade my skill not to satisfy you ...
I need to understand how to generate a correct csr, is that hard to you to understand / write the command when you have skill that i havent ?

In that case my skill would be "using Google":

openssl req -new -subj "/CN=myhostname" -newkey rsa:2048 -keyout key.pem -nodes -out csr.pem

This generates a CSR which is perfectly accepted by the Let's Encrypt API when I use it as input for Certbot using the --csr option. The payload send to the ACME server is exactly the same as the PEM CSR file minus of course the base64 -> base64url conversion, the linebreaks and the two header lines which are present in the PEM file, but of course not in the payload..

Thus, generating the required CSR should not be that hard. An absolute bare minimum is only that's required.

1 Like