Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto/tls: Client authentication fails for certificates that don't specify an extended key usage #11087

Closed
GuySirton opened this issue Jun 5, 2015 · 7 comments
Milestone

Comments

@GuySirton
Copy link

go version go1.4.2 linux/amd64
Expecting: client cert to be verified and connection proceeds.
Getting: "tls: client's certificate's extended key usage doesn't permit it to be used for client authentication"

Also see: #7423

This is basically the same issue reposted. At the time the issue was posted people who hit this issue generated new certs with EKU to get around the problem. However the behaviour for certs without EKU is IMO still incorrect.

I am using Go in an existing system where there are an existing set of certs that are already used for client authentication. Because of live upgrade considerations generating new certs is difficult and the existing certs do not specify EKU. From my reading of the standard the way we are currently using the certificates is compliant. One option I have to fit within this system is to patch the libraries which I'd rather not do. Trying to override the default verification appears to be complicated and error prone.

From RFC3280 (emphasis mine):

This extension MAY, at the option of the certificate issuer, be either critical or non-critical. If the extension is present, then the certificate MUST only be used for one of the purposes indicated.

From RFC5280:

If the extension is present, then the certificate MUST only be used for one of the purposes indicated.

If I'm reading it correctly, the existing code (ftls/handshake_server.go) appears to treat a missing EKU the same as if the EKU is specified without the client auth usage but as far I can tell there's no real justification in the standard for doing this?

ok := false
for _, ku := range certs[0].ExtKeyUsage {
    if ku == x509.ExtKeyUsageClientAuth {
        ok = true
    break
   }
}

Contrast this with the way server certificates are treated here (x509/verify.go):

if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
    // The certificate doesn't have any extended key usage specified.
    continue
}

(if the same requirement for EKU was applied to server certs that would probably break everyone's applications... client authentication is not very common which is why only a few people seem to have hit this...)

Further reference:
http://tools.ietf.org/html/rfc5280#section-4.2.1.12
http://blogs.msdn.com/b/kaushal/archive/2012/02/18/client-certificates-v-s-server-certificates.aspx
http://security.stackexchange.com/questions/68491/recommended-key-usage-for-a-client-certificate
http://stackoverflow.com/questions/20875626/tls-clientauth-requires-extkeyusageclientauth-through-whole-certificate-chain

I'd be happy to propose a patch if there's agreement there is an issue. Either changing the default behaviour or providing some way of overriding could solve my problem. Any other suggestions would be appreciated. (The proper way is probably to pass the client auth usage to the verify function which already seems to do the right thing rather than doing the extra verification in the server handshake code)

@ianlancetaylor ianlancetaylor added this to the Go1.5Maybe milestone Jun 5, 2015
@agl
Copy link
Contributor

agl commented Jun 5, 2015

I think that the requirement that client-auth certificates be explicitly marked for client-auth is a reasonable default. Most people don't know about EKUs and this might save them from a nasty security bug where they didn't think about what other certificates a given CA might be issuing.

For cases where one wants to override this, there's already an option for bypassing automatic verification and then you can do whatever you want.

I'm not seeing a convincing case for changing things here.

@GuySirton
Copy link
Author

Thanks for your comment...

@agl by this logic you should not allow server certificates that don't specify ExtKeyUsageServerAuth.
Most people don't know about EKUs and they might use a cert for a server that doesn't have KeyUsageServerAuth specified... So requiring a server cert to be marked for server use is a reasonable default... Do you agree?

This isn't about people knowing or not knowing about EKUs. If an EKU isn't there you aren't required to check for this... But you are. This is incompatible with the standard and is breaking existing client certs. You're not really saving people from security bugs, it's perfectly fine/secure to use a cert without any specified key usages as a client auth certificate.

I agree that if EKU is in the cert you should enforce it.

I'd appreciate it if you could point me to how I could bypass automatic verification in the context of a TLS HTTP server application. I haven't been able to find a clean way to do that. I'm using both client and sever authentication.

@agl
Copy link
Contributor

agl commented Jun 5, 2015

by this logic you should not allow server certificates that don't specify ExtKeyUsageServerAuth.

We don't. Although, as a retreat at the hands of practicality, we accept no EKUs to mean "all" in that case. (And we accept the Netscape EKU.)

I'd appreciate it if you could point me to how I could bypass automatic verification in the context of a TLS HTTP server application.

Just set RequestClientCert or RequireAnyClientCert (http://golang.org/pkg/crypto/tls/#ClientAuthType) and then http://golang.org/pkg/crypto/tls/#Conn.ConnectionState to get the chain that the client provided. That can can be passed to Verify with whatever options you wish. See http://golang.org/src/crypto/tls/handshake_server.go#L605

@GuySirton
Copy link
Author

I see the situation as symmetric between client and server. I think your client implementation is standard compliant (no EKU means all) and your server implementation not compliant (no EKU means none). Since client authentication isn't very commonly used there aren't too many people who care about this... Not sure if there's something else I can say to convince you here that isn't simply a matter of opinion. I can do a little more research and see if I come up with something. In situation where I've seen client certs used EKU wasn't typically set (hence the original report and the similar SO question).

If I set RequireAnyClientCert (which I tried) it seems the only place I can verify the cert is in the context of my handler functions. I'm using persistent connections and I don't want to need to re-verify this for every HTTP request. It's not clear where I can hook the server to only verify on the initial incoming connection. Also I wouldn't be able to fail the TLS negotiation at this point, it would need to be an HTTP error. Is there a better way of doing this?

@GuySirton
Copy link
Author

@agl Maybe relevant and last attempt to re-iterate on this :)

https://www.ietf.org/mail-archive/web/pkix/current/msg05179.html

If the keyUsage extension is absent, then it can be used for any purpose, even for verifying signatures on certificates and CRLs, unless that usage is restricted by some other means as described above.

This implies that the keyUsage extension only needs to be present if there is a desire to restrict the use of the key and so the absence of the extension means the absence of restrictions. Would people prefer for section 4.2.1.3 (Key Usage) to use language closer to the language for extended key usage?

http://lists.shmoo.com/pipermail/hostap/2014-February/029519.html

and EAP-TTLS (RFC 5281):
Clients and servers should implement policies related to the Extended
Key Usage (EKU) extension [RFC5280] of certificates it receives, to
ensure that the other party's certificate usage conforms to the
certificate's purpose. Typically, a client EKU, when present, would
be expected to include id-kp-clientAuth; a server EKU, when present,
would be expected to include id-kp-serverAuth. Note that absence of
the EKU extension or a value of anyExtendedKeyUsage implies absence
of constraint on the certificate's purpose.

@agl
Copy link
Contributor

agl commented Jun 8, 2015

Having slept on it, I think that I'm convinced that this isn't a battle worth fighting: https://go-review.googlesource.com/#/c/10806/

@gopherbot
Copy link

CL https://golang.org/cl/10806 mentions this issue.

@agl agl closed this as completed in c72b8aa Jun 9, 2015
@mikioh mikioh modified the milestones: Go1.5, Go1.5Maybe Jun 10, 2015
@golang golang locked and limited conversation to collaborators Jun 25, 2016
FiloSottile pushed a commit to FiloSottile/go that referenced this issue Oct 12, 2018
Previously we enforced both that the extended key usages of a client
certificate chain allowed for client authentication, and that the
client-auth EKU was in the leaf certificate.

This change removes the latter requirement. It's still the case that the
chain must be compatible with the client-auth EKU (i.e. that a parent
certificate isn't limited to another usage, like S/MIME), but we'll now
accept a leaf certificate with no EKUs for client-auth.

While it would be nice if all client certificates were explicit in their
intended purpose, I no longer feel that this battle is worthwhile.

Fixes golang#11087.

Change-Id: I777e695101cbeba069b730163533e2977f4dc1fc
Reviewed-on: https://go-review.googlesource.com/10806
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Adam Langley <agl@golang.org>
FiloSottile pushed a commit to FiloSottile/go that referenced this issue Oct 12, 2018
Previously we enforced both that the extended key usages of a client
certificate chain allowed for client authentication, and that the
client-auth EKU was in the leaf certificate.

This change removes the latter requirement. It's still the case that the
chain must be compatible with the client-auth EKU (i.e. that a parent
certificate isn't limited to another usage, like S/MIME), but we'll now
accept a leaf certificate with no EKUs for client-auth.

While it would be nice if all client certificates were explicit in their
intended purpose, I no longer feel that this battle is worthwhile.

Fixes golang#11087.

Change-Id: I777e695101cbeba069b730163533e2977f4dc1fc
Reviewed-on: https://go-review.googlesource.com/10806
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Adam Langley <agl@golang.org>
@rsc rsc unassigned agl Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants