Reputation: 12988
In my WCF self-hosting WebService using mutual certificate to validate the client, i set the CertificateValidationMode = PeerTrust
but its seems ignored, since i can still execute the methods with some client wich i have deleted the corresponding certificate of the TrustedPeople
server store.
Heres the host example:
static void Main()
{
var httpsUri = new Uri("https://192.168.0.57:xxx/HelloServer");
var binding = new WSHttpBinding
{
Security =
{
Mode = SecurityMode.Transport,
Transport = {ClientCredentialType = HttpClientCredentialType.Certificate}
};
var host = new ServiceHost(typeof(HelloWorld), httpsUri);
//This line is not working
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =X509CertificateValidationMode.PeerTrust;
host.AddServiceEndpoint(typeof(IHelloWorld), binding, string.Empty, httpsUri);
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"server.com");
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The client app:
static void Main(string[] args)
{
try
{
var c = new HelloWorld.HelloWorldClient();
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => true;
c.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"client.com");
Console.WriteLine(c.GetIp());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
I generate the server.com and the client.com with a RootCA certificate. This RootCA certificate is instaled on the trusted root store of the client and server.
The question is, i should not execute the GetIp()
method if my client.com certificate is not in the TrustedPeople store of the server, right? But im executing it without any problems.
The question is, how to, in this scenario, validate the client certificate put its public key on TrustedPeople of server?
ps: In this MSDN article of Transport security with client certificate, theres a quote saying The server’s certificate must be trusted by the client and the client’s certificate must be trusted by the server.
But i can execute the webmethods from client even if the client certificate isnt in the server TrustedPeople store.
Upvotes: 4
Views: 8711
Reputation: 54
My suggestion would be to use custom validation. This way you can set some breakpoints and watch the validation take place as well as see what other validation options you could come up with based on the data available throughout the validation process.
First make sure you have your binding requiring Certificate for Message Client Credentials. If you only use Certificate for Transport, the Client in my tests did not validate. This alone may fix your issue.
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType =
MessageCredentialType.Certificate;
To setup a custom validator follow the rest.
Replace:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.PeerTrust;
With:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.Custom;
host.Credentials.ClientCertificate.Authentication.CustomCertificateValidator =
new IssuerNameCertValidator("CN=client.com");
Then add this to create the custom validator and tweak as needed (this one validates based on Issuer):
public class IssuerNameCertValidator : X509CertificateValidator
{
string allowedIssuerName;
public IssuerNameCertValidator(string allowedIssuerName)
{
if (allowedIssuerName == null)
{
throw new ArgumentNullException("allowedIssuerName");
}
this.allowedIssuerName = allowedIssuerName;
}
public override void Validate(X509Certificate2 certificate)
{
// Check that there is a certificate.
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
// Check that the certificate issuer matches the configured issuer.
if (allowedIssuerName != certificate.IssuerName.Name)
{
throw new SecurityTokenValidationException
("Certificate was not issued by a trusted issuer");
}
}
}
Upvotes: 2