cfssl
cfssl copied to clipboard
Certificate serial stored as Decimal instead of Hex
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 :-)
@cbroglie Something that you are aware of?
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.
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 ;-)
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?
@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
@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.
@bombsimon I will pick this up this week and document all actions and results. Sorry I haven't had the time yet to respond.
@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?
@bombsimon a friendly ping in case you missed it :-)
@electrical Sorry, I completely dropped this. As I understand it, this is what you're doing:
- Create a certificate via API or CLI
- Revoke said certificate
- This is made by using the serial (lowercase hex without
:
) + aki - This cannot be made remotely
- This is made by using the serial (lowercase hex without
- Perform
ocsprefresh
-> cannot sign OCSP response
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?
@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.
So here are some results for me following the steps I wrote about above.
- Create a certificate via API and perform an OCSP request
$ 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
- Revoke the certificate
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
}
- Refreshing OCSP data (using root CA does not cause error
8100
):
$ cfssl ocsprefresh \
-db-config my-config.json \
-ca my-ca.pem \
-responder my-ca.pem \
-responder-key my-ca-key.pem
- Performing OCSP request once again after revocation and
ocsprefresh
$ 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.
- Generate and store a new certificate and key issued by CFSSL
$ 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
- Perform
ocsprefresh
with these certificates
$ cfssl ocsprefresh \
-db-config my-config.json \
-ca my-ca.pem \
-responder my-ocsp-cert.pem \
-responder-key my-ocsp-key.pem && echo $?
0
- Performing the OCSP request again after the refresh
$ 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.
I am also facing 8100 Certificate not issued by this issuer
. Documented my stapes here #1105
If you could help.
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.
@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
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
@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