cli icon indicating copy to clipboard operation
cli copied to clipboard

`step ca renew` fails when existing certificate doesn't have `clientAuth` extended attribute

Open frebib opened this issue 3 years ago • 2 comments

Subject of the issue

Issuing an immediately attempting to renew the certificate causes the following error

frebib@:~$ STEPPATH=/tmp/step step ca certificate $HOSTNAME /tmp/step/$HOSTNAME.crt /tmp/step/$HOSTNAME.key --token $TOKEN
✔ CA: https://<snip>/1.0/sign
✔ Certificate: /tmp/step/<snip>.crt
✔ Private Key: /tmp/step/<snip>.key
frebib@:~$ STEPPATH=/tmp/step step ca renew /tmp/step/$HOSTNAME.crt /tmp/step/$HOSTNAME.key
error renewing certificate: client.Renew; client POST https://<snip>/renew failed: Post "https://<snip>/renew": remote error: tls: bad certificate

step-ca logs:

2021/10/17 11:52:29 /usr/local/go/src/net/http/server.go:3157: http: TLS handshake error from <snip>: tls: failed to verify client certificate: x509: certificate specifies an incompatible key usage

I'm using a "pretty much default" JWK provisioner, but with this template, which I notably removed clientAuth from:

{
    "subject": {{ toJson .Subject }},
    "sans": {{ toJson .SANs }},
{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }}
    "keyUsage": ["keyEncipherment", "digitalSignature"],
{{- else }}
    "keyUsage": ["digitalSignature"],
{{- end }}
    "extKeyUsage": ["serverAuth"]
}

I expect that this is "that's just how it works" and my lack of understanding of some of the nuances of x509. It would probably make sense to spit out a client warning/error, though.

Your environment

  • OS - Debian
  • Version - Whatever the latest is at time of writing (0.7.5?)

Steps to reproduce

See above

Expected behaviour

Either a warning, or the certificate renews without problem

Actual behaviour

Non-descript error on the client side, slightly more information in the server logs. Certificate is not renewed

frebib avatar Oct 17 '21 12:10 frebib

Our renewal mechanism uses client authentication / mTLS. I'm not sure I would say this is "working as intended", but what's going on here totally makes sense and the TLS stack is behaving as expected by enforcing the extended key use constraints.

The obvious / easy workaround here would be to add clientAuth back. But, presumably, you've removed it for a reason (I am curious what that reason is, but understand if you're unable to share).

We do have a project on our backlog to add an alternative mechanism for renewal using JWTs (still authenticated using your cert / signed using the client's private key / using the x5c header). I believe this only requires the digitalSignature key use, and wouldn't require clientAuth (which I believe is specific to mTLS). So this may be resolved by https://github.com/smallstep/certificates/issues/177. I don't currently have an ETA on this functionality. It's a surprisingly big project. I suppose we should consider how one might renew a certificate that doesn't have the digitalSignature key use, either.

Regarding error messages... noted, but I am afraid this error is probably triggered pretty deep in the golang TLS stack during the handshake, so that may be difficult to do. At a minimum, we can add this to the list of reasons to switch to x5c/JWT.


Edit:

I just realized I didn't really connect the dots between the "renew after expiry" ticket I linked above and x5c/JWT-based renewal. Those are related because JWT-based authentication allows us to implement more sophisticated authentication policies and depend less on clunky workarounds to RFC5280 certificate path validation used by [m]TLS. This is relevant to renew-after-expiry, where we'd like to allow granular use of certificates after they've expired. It's also relevant here, where we may want to ignore [extended] key use for this particular endpoint.

mmalone avatar Oct 18 '21 19:10 mmalone

Yes, this is working as intended.

But our X5C provisioner might not, right now this requires clientAuth and digitalSignature, probably digitalSignature should be the only one required, as you're signing the token with the certificate key, I can't see any real reason to require the clientAuth. @mmalone

maraino avatar Oct 18 '21 21:10 maraino