dotnet / dotnet/wcf

Unable to create a client with separate X.509 cetificates for message and transport security

Open
#5,139 3 comments 0 reactions 1 assignee Claimed by @mconnew View on GitHub
triaged
Dominant language
C#
Stars
1.8k
Forks
576
Avg merge
6d 9h
Merged PRs (30d)
2

Description

I am unable to migrate a .NET Framework 4.8 WCF client to .NET Core (or .NET 5+) because the service expects two different certificates for transport and message security. Our current implementation uses a custom `System.ServiceModel.ClientCredentialsSecurityTokenManager` implementation and returns a different `System.IdentityModel.Selectors.X509SecurityTokenProvider` depending on whether the token requirement has the security binding element property.

```c#
internal class ClientCredentialsSecurityTokenManager : System.ServiceModel.ClientCredentialsSecurityTokenManager
{
private readonly ClientCertificateCredentials custCreds;

public ClientCredentialsSecurityTokenManager(ClientCertificateCredentials CustCreds)
: base(CustCreds)
{
custCreds = CustCreds;
}

public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
if (tokenRequirement.TokenType != SecurityTokenTypes.X509Certificate)
return base.CreateSecurityTokenProvider(tokenRequirement);

TransportSecurityBindingElement secBE = null;

if (tokenRequirement.Properties.TryGetValue(
ServiceModelSecurityTokenRequirement.SecurityBindingElementProperty, out var temp))
{
new X509SecurityTokenProvider(custCreds.ClientSigningCert);
}

return new X509SecurityTokenProvider(custCreds.ClientAuthCert);
}
}
```

However, the `X509SecurityTokenProvider` class [doesn't appear to be publicly exposed after .NET Framework 4.x](https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.selectors.x509securitytokenprovider?view=netframework-4.8.1&viewFallbackFrom=netstandard-2.0).

The method we are using is described in the following posts:

- https://learn.microsoft.com/en-us/dotnet/framework/wcf/extending/how-to-use-separate-x-509-certificates-for-signing-and-encryption#to-use-separate-certificates-for-signing-and-encryption
- https://blogesh.wordpress.com/2009/10/08/separate-certificates-for-transport-and-message-security-in-wcf/

I haven't been able to find any other way to achieve what we need. We also have no control over the service, so we can't change the requirement for two separate certificates.

**To Reproduce**
Steps to reproduce the behavior:
1. Create a class inheriting from `System.ServiceModel.ClientCredentialsSecurityTokenManager`.
2. Override `SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)` method.
3. Try to return a new `System.IdentityModel.Selectors.X509SecurityTokenProvider`.
4. The `X509SecurityTokenProvider` type won't resolve, and there seems to be no other way to return separate certificates for message and transport security.

**Expected behavior**
I can return different certificate token providers for transport and message security.

**Additional context**
Here's the code for the custom `System.ServiceModel.Description.ClientCredentials` we are using:

```c#
internal class ClientCertificateCredentials : ClientCredentials
{
public X509Certificate2 ClientAuthCert { get; set; }

public X509Certificate2 ClientSigningCert { get; set; }

public ClientCertificateCredentials(ClientCertificateCredentials other)
: base(other)
{
ClientSigningCert = other.ClientSigningCert;
ClientAuthCert = other.ClientAuthCert;

}

public ClientCertificateCredentials(X509Certificate2 ClientAuthCert, X509Certificate2 ClientSigningCert)
{
this.ClientAuthCert = ClientAuthCert;
this.ClientSigningCert = ClientSigningCert;
}

protected override ClientCredentials CloneCore()
{
return new ClientCertificateCredentials(this);
}

public override SecurityTokenManager CreateSecurityTokenManager()
{
return new ClientCredentialsSecurityTokenManager(this);
}
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.