Skip to content

Client Certificate Validation in .NET core is too restrictive #94152

@systems2000

Description

@systems2000

Description

For a few days we have struggled with calling some SOAP and REST api's with client certificate authentication.
Although we've set HttpClientHandler's ClientCertificates property, the server still rejected our requests with 403.
It soon turned out that the client certificates, although set are not send. We have found a workaround with
using SocketsHttpHandler (easy when using HttpClientHandler directly, not so easy with WCF Stack).
However - as it bothered me for a while why the certs are not sent I have checked .NET Core code, and I've found the
code responsible for client certificates validation:

The IsValidClientCertificate method checks the cert's Extensions checking whether there is an X509EnhancedKeyUsageExtension
(by the way - the name of the property is wrong, it should be ExtendedKeyUsageExtension) defined as Client Authentication.

If the extension is X509KeyUsageExtension (not Enhanced) it checks whethever it is valid for a digital signature - checking X509KeyUsageFlags for DigitalSignature flag.
For the client certificates it is required to have the extensions described above.
However, neither the RFC 5280 nor TLS v1.2 restricts the client certificate to have only these extensions and allows others when there is a strong justification. For our customer's it looks like they have one.
I our case the first extension usage our client certificate has is the "Key Encipherment" (20).
With this in place the first iteration of the .Extensions loop in IsValidClientCertificates method checks this extension (ku) for digital signature and returns false.
This causes the validation to return false and the client certificate is not sent.
We have checked how other technologies and tools behave in this situation - PostMan, curl, Java & ... .Net Framework 4.6 & 4.8.
They all send the certificate without any warning or complain - so I believe, .NET core also should.
I'll be glad to prepare Pull Request to resolve the issue.

Reproduction Steps

  1. Generate client certificate with following key usage extensions (order matters)
    a) Key Encipherment
    b) Digital Signature
    c) Client Authentication (extended)
  2. Prepare request with HttpClientHandler and set ClientCertificates property.
  3. Call API protected with ClientCertificateAuthentication.

Expected behavior

Client certificate is send, API is called and request succedded.

Actual behavior

Client certificate is not send, API is called and error code 403 is returned.

Regression?

It works properly with checked .NET Framework versions (4.6 & 4.8).

Known Workarounds

Use SocketsHttpHandler and override LocalCerticateSelectionCallback to send the client certificate.

Configuration

.NET Core 3.1, .NET Core 5.0, .NET Core 6.0.

Other information

the code responsible for client certificates validation, see description.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions