Closed maraino closed 3 months ago
Quick note: Unlike certificates, there is no canonical "fingerprint" for CSRs as far as I know. Rather one would generate the message digest of the der format.
openssl req -in example.csr -outform DER | openssl dgst -sha256 -c
There is a rather 🔥 discussion on this right here: https://security.stackexchange.com/questions/262476/getting-fingerprint-of-certificate-signing-request
@daFritz84, that was the idea, and that's exactly how the certificate fingerprint is calculated.
@daFritz84 after our team discussion, some voices were not fully convinced about adding this. Can you provide more insight into why and how you want to use this? How are your token/certificate flows that this can be a problem?
@maraino sure thing. I whipped up a quick sketch to illustrate my case:
So I do have a scenario where an 'IoT device' is requesting a certifcate from the 'Certificate authority' in the cloud. The request is transfered via asynchronous communication (publish/subscribe). There is only segment-by-segment encrypted tunnels, and the CSR (as by the nature of async communication of Kafka and MQTT) cached at, at least, two other services. Hence I would prefer a strong binding between the JWT and the CSR.
I hope I have described the scenario clearly, and this brings my point across. Btw. async communication (MQTT) was implemented due to low bandwidth/unreliable networks. In my threat model, the edge gateway or the proxy could be compromised to allow MITM attacks on the CSRs.
@daFritz84, we will add support for this in our cli and certificates, it is not a high priority, so it can take some time.
@maraino Thanks for the x-mas gift 😄, I'll take it.
I've been working on a yet incomplete implementation where tokens for the JWK and X5C provisioners can have a confirmation claim, cnf
with a kid
parameter as defined in RFC 7800#section-3.4.
The payload of a token with this will look like:
{
"aud": "https://ca.smallstep.com:9000/1.0/sign",
"cnf": {
"kid": "PJLNhtQoBE1yGN_ZKzr4Y2U5pyqIGiyyszkoz2raDOw"
},
"exp": 1703803318,
"iat": 1703803018,
"iss": "mariano@smallstep.com",
"jti": "458bd150aaa7ba0bf1b0c927f17e1f52e22954db64e3aaa3b7da92a4ddcdc832",
"nbf": 1703803018,
"sans": [
"mariano@smallstep.com"
],
"sha": "a78a850025c0c234385eda23d7192964ca56aa9d727f535653b0afb81c1e0559",
"sub": "mariano@smallstep.com"
}
In this case, that kid
is SHA-256 of the raw data of a CSR using the base64url without padding encoding. In the same way, I'm also working on adding the same functionality for SSH certificates. In this case, the fingerprint will be in the marshaling of the SSH public key.
When the JWK or X5C provisioner of the CA sees a token with that confirmation claim, it will validate that the fingerprint matches the provided CSR or SSH public key; if it does not, it will return a 403 error.
Note that the fingerprint for a CSR will be based on the full CSR, not just the key.
Description
In https://github.com/smallstep/certificates/discussions/1636, @daFritz84 proposes that binding JWT tokens for the JWK provisioner to a given CSR might be a good option to prevent some attacks.
Optionally, adding a new field in the token with a CSR fingerprint can be a good idea. Commands that generate their own token and CSR can always add the new field, and the JWK provisioner will validate it.
step ca token
can have an extra flag to add that fingerprint and a flag to pass a CSR to get the fingerprint from.It might also be a good idea to add a fingerprint to
x509util.CertificateRequest
, so it can be optionally checked in a template or in a webhook, although the raw CSR is also present in a webhook request.