cloudflare / cfssl

CFSSL: Cloudflare's PKI and TLS toolkit
https://cfssl.org/
BSD 2-Clause "Simplified" License
8.66k stars 1.1k forks source link

Certificate serial stored as Decimal instead of Hex #965

Open electrical opened 5 years ago

electrical commented 5 years ago

When a certificate is created by cfssl it stores the serial number as a Decimal entry while the serial number is stored as hex in the cert it self. When doing a ocsp test using openssl it sends the serial number as a hex string which cfssl can't find in the database when using ocspserve.

Also found out that cfss-certinfo parses the serial number as a decimal entry instead of Hex.

Feels like a bug :-)

electrical commented 5 years ago

@cbroglie Something that you are aware of?

cbroglie commented 5 years ago

You're correct that cfssl stores the serial number as decimal. Changing that while maintaining backwards compatibility would be difficult, but changing ocspserve to handle both decimal and hex input sounds reasonable. PRs are welcome.

electrical commented 5 years ago

Any specific reason to maintain backwards compatibility for ocspserve? I find it hard to imagine it ever worked correctly as its searching the certs incorrectly? With regards to send a PR, unfortunately I don't understand golang at all. If it was Ruby or Python it would be a lot easier ;-)

bombsimon commented 5 years ago

Since I'll need this OCSP functionality in the near future I thought I would look into this issue and work on a PR. However, I'm having a hard time reproducing this issue. I'm on macOS Mojave 10.14 with openssl version reporting LibreSSL 2.6.4.

This is how I first generate my certificate from CFSSL with HTTPie:

$ echo '{"request":{"CN":"Test"}}' | \
    http POST http://127.0.0.1:8888/api/v1/cfssl/newcert | \
    jq .result.certificate | \
    sed 's,\\n,\n,g' > my-cert.pem

Then I produce a new OCSP dump for CFSSL:

$ cfssl ocsprefresh \
    -db-config my-config.json \
    -ca my-ca.pem \
    -responder my-ca.pem \
    -responder-key my-ca-key.pem
$ cfssl ocspdump -db-config my-config.json > ocsp-responses
$ cfssl ocspserve -address=0.0.0.0 -port=8889 -loglevel=0 -responses=ocsp-responses

Now I try to check my certificate towards the API:

$ openssl ocsp \
    -no_nonce
    -text \
    -issuer root-ca.pem \
    -cert my-cert.pem \
    -CAfile root-ca.pem \
    -respout response.out \
    -reqout request.out \
    -url http://127.0.0.1:8889

OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
          Hash Algorithm: sha1
          Issuer Name Hash: C67A6BCE137782A7C759B091B881C6B7038111B4
          Issuer Key Hash: D9DDA678A8B7292A7C16033195A350C8AF6A8C95
          Serial Number: 2FF85295B184F7DC2D9E6B1FF3AB2E41C96BAC64
[...]
/path/to/my-cert.pem: good
        This Update: Mar 27 08:00:00 2019 GMT
        Next Update: Mar 31 08:00:00 2019 GMT

So obviously this worked just fine for me with openssl. Since I stored the request in request.out i tried to do the same test with HTTPie:

$ http http://127.0.0.1:8889/"$(cat request.out | base64)"
HTTP/1.1 200 OK
Cache-Control: max-age=342249, public, no-transform, must-revalidate
Content-Length: 707
Content-Type: application/ocsp-response
Date: Wed, 27 Mar 2019 08:55:50 GMT
Etag: "B41133AA94018641D175E0A80B5F6EFDAB482275625A663B5DA392E7A73232F7"
Expires: Sun, 31 Mar 2019 08:00:00 UTC
Last-Modified: Wed, 27 Mar 2019 08:00:00 UTC

+-----------------------------------------+
| NOTE: binary data not shown in terminal |
+-----------------------------------------+

So again, I get the expected response from CFSSL including the OCSP response in binary form back.

If I check what's actually in the request created by openssl it looks like this:

$ hexdump request.out
0000000 30 55 30 53 30 51 30 4f 30 4d 30 09 06 05 2b 0e
0000010 03 02 1a 05 00 04 14 c6 7a 6b ce 13 77 82 a7 c7
0000020 59 b0 91 b8 81 c6 b7 03 81 11 b4 04 14 d9 dd a6
0000030 78 a8 b7 29 2a 7c 16 03 31 95 a3 50 c8 af 6a 8c
0000040 95 02 14 2f f8 52 95 b1 84 f7 dc 2d 9e 6b 1f f3
0000050 ab 2e 41 c9 6b ac 64                           
0000057

As seen in the fourth row the value of the serial is in fact an integer of length 14 (02 14). More details can be found with an ASN.1 Decoder which helps describe the type of the sequence value.

So I did the same test with Go code to see if the request created would be different (ignoring all errors):

// Construct an OCSP request from the *x509.Certificate
request, _ := ocsp.CreateRequest(cert, issuing, nil)

// Construct the URL with the base64 encoded request
httpResponse, _ := http.Get(
    fmt.Sprintf("http://127.0.0.1:8889/%s", base64.StdEncoding.EncodeToString(request)),
)

// Read response from body
responseBody, _ := ioutil.ReadAll(httpResponse.Body)

// Parse OCSP response
ocspResponse, _ := ocsp.ParseResponse(responseBody, issuing)

// ocspResponse.Status == ocsp.Good returns true

Not sure if I'm missing something here and I could of course create a PR to support serial number passed in HEX format but I don't see the need since neither of openssl and the Go library seems to produce that kind of request.

Is this really still an issue? If so, how would I do to reproduce it?

electrical commented 5 years ago

@bombsimon seems you don't use the in exactly the same way I am. I use cfssl ocspserve with the database connection instead of the exported file. If you try it with that it should fail in the same way

bombsimon commented 5 years ago

@electrical: I still have no issues with this, the underlying type is the same and since it parsed to a *big.Int in the request the String() method returns the same format as stored in the database. Remember that you still need to do the refresh since the ocsp_responses table isn't populated by default.

Refreshing the OCSP data.

$ cfssl ocsprefresh \
    -db-config my-config.json \
    -ca my-ca.pem \
    -responder my-ca.pem \
    -responder-key my-ca-key.pem

Starting ocspserve with DB access instead of from file.

$ cfssl ocspserve \
    -port 8889 \
    -ca root-ca.pem \
    -ca-key root-ca-key.pem \
    -config my-config.json \
    -db-config my-db-config.json \
    -loglevel 0

...
2019/04/06 22:22:49 [INFO] Registering OCSP responder handler
2019/04/06 22:22:49 [INFO] Now listening on 127.0.0.1:8889

Perform the OpenSSL request.

$ openssl ocsp \
    -no_nonce \
    -text \
    -issuer root-ca.pem \
    -cert my-cert.pem \
    -CAfile root-ca.pem \
    -respout response.out \
    -reqout request.out \
    -url http://127.0.0.1:8889

This give me the same proper OCSP respoce with a good status and proper dates as posted above. I can also see in the ocspserve logs that the request was processed.

2019/04/06 22:37:38 [DEBUG] Received OCSP request: MFUwUzBRME8wTTAJBgUrDgMCGgUABBTGemvOE3eCp8dZsJG4gca3A4ERtAQU2d2meKi3KSp8FgMxlaNQyK9qjJUCFEvEGBo6A3wVq3kBk95i+YHTpcOu

Use the same request with HTTP.

$ http http://127.0.0.1:8889/"$(cat request.out | base64)"
HTTP/1.1 200 OK
Cache-Control: max-age=343501, public, no-transform, must-revalidate
Content-Length: 707
Content-Type: application/ocsp-response
Date: Sat, 06 Apr 2019 20:34:58 GMT
Etag: "2FB278A1D5CF3CD7E6F4BEF90BEF1307E1626F07F2BC4C7E52608679EA08C96D"
Expires: Wed, 10 Apr 2019 20:00:00 UTC
Last-Modified: Sat, 06 Apr 2019 20:00:00 UTC

+-----------------------------------------+
| NOTE: binary data not shown in terminal |
+-----------------------------------------+

I could help debug and potentially implement required fixes if you provide me with a step-by-step guide of how to reproduce the issue since I'm unable to figure this out myself.

electrical commented 5 years ago

@bombsimon I will pick this up this week and document all actions and results. Sorry I haven't had the time yet to respond.

electrical commented 5 years ago

@bombsimon findings so far as i'm running into issues.

Revoking the actual certificate requires the serial number to be the decimal version and the authority_key_id has to be the hex version but lower case and without the : in there. There also seems to be no ability to do this remotely and has to be done locally on the cfssl instance ( which sort of defeats the purpose )

Next when I try to do the ocsprefresh command I get the following error:

2019/05/01 16:13:12 [CRITICAL] Unable to sign OCSP response: {"code":8100,"message":"Certificate not issued by this issuer"}
{"code":8100,"message":"Certificate not issued by this issuer"}

Command:

cfssl ocsprefresh -db-config /etc/cfssl/config/db-config.json -ca /etc/cfssl/certs/our_intermediate_cert.pem -responder /etc/cfssl/certs/our_ocsp_cert.pem -responder-key /etc/cfssl/certs/our_ocsp_cert-key.pem

The ocsp certificate was created/signed with the intermediate cert, as are all our signed certs.

Am I just missing something?

electrical commented 5 years ago

@bombsimon a friendly ping in case you missed it :-)

bombsimon commented 5 years ago

@electrical Sorry, I completely dropped this. As I understand it, this is what you're doing:

To support case insensitive serials with or without : when revoking is probably an easy enhancement but not related to this issue. Afaik it's possible to revoke via the HTTP API but if not I can just confirm the issue, I'll try to see if I can do that. The fact that you cannot sign the OCSP response doesn't sound related to the way serials are stored at all though.

It shouldn't matter if you use a CA created by CFSSL, one you created yourself or if it's an intermediate cert like you are using. For testing purposes I'm using a CA certificate and key generated with mkcert for easy testing purposes.

A question though, who signed our_ocsp_cert.pem you're using for the OCSP responses? Is it signed by your root CA or the intermediate certificate you're using for CFSSL? Did you sign it with or without CFSSL? Do you get the same result if you're using our_intermediate_cert.pem (and it's key) as responder?

electrical commented 5 years ago

@bombsimon no worries. I know we are all busy :-)

the our_oscp_cert.pem has been signed by our intermediate cert using CFSSL. I haven't tried to replace the ocsp certs with the intermediate cert. I'll give that a try.

bombsimon commented 5 years ago

So here are some results for me following the steps I wrote about above.

$ openssl ocsp \
    -no_nonce \
    -text \
    -issuer root-ca.pem \
    -cert my-cert.pem \
    -CAfile root-ca.pem \
    -url http://127.0.0.1:8889 | grep 'my-cert'

my-cert.pem: good

As seen it's fully possible to revoke remotely with the HTTP API.

$ http POST http://127.0.0.1:8888/api/v1/cfssl/revoke \
    serial=748095522558... \
    authority_key_id=d9dda678a8... \
    reason=keycompromise

HTTP/1.1 200 OK
Content-Length: 55
Content-Type: application/json
Date: Thu, 27 Jun 2019 11:57:21 GMT

{
    "errors": [],
    "messages": [],
    "result": {},
    "success": true
}
$ cfssl ocsprefresh \
    -db-config my-config.json \
    -ca my-ca.pem \
    -responder my-ca.pem \
    -responder-key my-ca-key.pem
$ openssl ocsp \
    -no_nonce \
    -text \
    -issuer root-ca.pem \
    -cert my-cert.pem \
    -CAfile root-ca.pem \
    -url http://127.0.0.1:8889 | grep 'my-cert'

my-cert.pem: revoked

So, regarding using a responder with a certificate issued by CFSSL. This is how I tried to reproduce your error.

$ r=$( echo '{"request":{"CN":"Test"}}' | \
    http POST http://127.0.0.1:8888/api/v1/cfssl/newcert); \
    echo $r | jq .result.certificate | \
        sed 's,\\n,\n,g' | sed 's,",,g'> my-ocsp-cert.pem; \
    echo $r | jq .result.private_key | \
        sed 's,\\n,\n,g' | sed 's,",,g'> my-ocsp-key.pem
$ cfssl ocsprefresh \
    -db-config my-config.json \
    -ca my-ca.pem \
    -responder my-ocsp-cert.pem \
    -responder-key my-ocsp-key.pem && echo $?

0
$ openssl ocsp \
    -no_nonce \
    -text \
    -issuer root-ca.pem \
    -cert my-cert.pem \
    -CAfile root-ca.pem \
    -url http://127.0.0.1:8889 | grep 'my-cert'

my-cert.pem: revoked

So I don't think I'm able to help much more by trying to guess how to reproduce this and I see no problem with certificate serials, revocation APIs, certificate generation, OCSP requests, OCSP refreshing etcetera.

Roydon commented 4 years ago

I am also facing 8100 Certificate not issued by this issuer. Documented my stapes here #1105 If you could help.

superbob commented 3 years ago

I'm facing a similar issue.

Right after having generated a certificate, I cannot verify it using openssl.

Here is what I do:

# Generate a new certificate remotely:
cfssl gencert -config client-config.json csr.json | cfssljson -bare my-cert

# Check it:
openssl ocsp -no_nonce -text -issuer ca.crt -cert my-cert.pem -CAfile cert-bundle.crt -url http://my-server/ocsp/

The output of the last command is:

OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
          Hash Algorithm: sha1
          Issuer Name Hash: FAD74B83B4F73E06394E4829AC676CAE193997A4
          Issuer Key Hash: B50D61F06271BD42ED7C4FD1A7334C832B79A167
          Serial Number: 765E60D2836B98C3B610DB32AD24C5A11238EE8C
Responder Error: unauthorized (6)

I've created my root and intermediate ca using another tool (neither mkcert, nor cfssl).

I'm also using a reverse-proxy between the client and cfssl ocspserve server, that's why the URL ends with /ocsp/.

Somehow I feel the issue could be linked to the way I have created the issuing certificate (ca.crt). It lacks OCSP extension and maybe it's required.

openssl x509 -in ca.crt -text
[...]
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Subject Key Identifier:
                38:2F:45:47:2D:1B:FE:57:73:10:46:55:DF:C0:EB:F9:AC:5E:65:10
            X509v3 Authority Key Identifier:
                keyid:3D:28:29:6D:C0:E5:9B:BF:88:EA:E8:84:A4:AF:D2:E4:77:16:7C:60
[...]

I didn't even try to revoke it given that I cannot even check it when it isn't.

EDIT: When generating OCSP response file: cfssl ocspdump -db-config db-config.json -ca ca.crt -responder ocsp.crt -responder-key ocsp.key > ocsp.response Then OCSP serve using the response file: cfssl ocspserve -port=8890 -responses=ocsp.response And checking against that, it works:

openssl ocsp -no_nonce -issuer ca.crt -cert my-cert.pem -CAfile cert-bundle.crt -url http://127.0.0.1:8890/
Response verify OK
my-cert.pem: good
        This Update: Jan  5 17:00:00 2021 GMT
        Next Update: Jan  9 17:00:00 2021 GMT

So the problem now seem to come from the cfssl ocspserve with -db-config. For the record, I'm using a postgres certdb.

arpan57 commented 2 years ago

@electrical : Were you able to get past this issue ? i.e.

[CRITICAL] Unable to sign OCSP response: {"code":8100,"message":"Certificate not issued by this issuer"}

I am stuck at the same place. After revoking the certificate, when I try to ocsprefresh it fails with the same error.

2021/11/03 23:55:27 [CRITICAL] Unable to sign OCSP response: {"code":8100,"message":"Certificate not issued by this issuer"}
{"code":8100,"message":"Certificate not issued by this issuer"}

I will be grateful, if you could share your approach.

Thanks, Arpan

arpan57 commented 2 years ago

I spent quite a few hours to solve this. Tried a various permutation combinations and following two work for me.

Noticing that many of us have faced this sharing the commands which worked for me for ocsprefresh.

cfssl ocsprefresh -db-config sqlite_db.json -ca server/server.pem -responder server/server.pem -responder-key server/server-key.pem

cfssl ocsprefresh -db-config sqlite_db.json -ca server/server.pem -responder ocsp/ocsp.pem -responder-key ocsp/ocsp-key.pem

klinux commented 5 months ago

@superbob I have the same issue, I created the CA and Intermediate using Hashicorp Vault, and, the ocsp using database can't validate, the problem is with issuerKeyHash, in my case, that value is not equal to AKI (Authority Key Identifier), If I use CA and Intermediate created by cfssl, it works like a charm, somehow, CA and Intermediate created by other tools (in my case with Vault) can't be used with database.

Here my AKI is: e90f44315310efc1453503f561a70ff6df81f4f0 By the issuer key hash is: 25C5F53C318A1E590F627720215EBB484E153235