Adding a certificate to a WCF client. Cannot find X.509 certificate - wcf

I have a WCF client that is going to authenticate against some web service using a certificate issued by said service. At first my client used a https binding as below:
var httpsBinding = new BasicHttpsBinding();
httpsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
httpsBinding.Security.Mode = BasicHttpsSecurityMode.Transport;
but this gave the following error:
InvalidOperationException: The client certificate is not provided.
Specify a client certificate in ClientCredentials.
I then added the following code to my client configuration:
this.ChannelFactory.Credentials.ClientCertificate.SetCertificate("test", System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
System.Security.Cryptography.X509Certificates.StoreName.My);
And now I get the error
System.InvalidOperationException: 'Cannot find the X.509 certificate
using the following search criteria: StoreName 'My', StoreLocation
'LocalMachine', FindType 'FindBySubjectDistinguishedName', FindValue
'test'.'
I am absolutely certain that the certificate is placed in the Personal folder on my Local Machine, but it still cannot find it. I have tried placing the certificate in various folders, renaming it, using the thumbprint for identification, but my application still can't find it. What could be the issue here?

I suggest you set up the certificate by using X509FindType.FindByThumbprint.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
//client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.Root, X509FindType.FindByThumbprint, "cbc81f77ed01a9784a12483030ccd497f01be71c");
client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "9ee8be61d875bd6e1108c98b590386d0a489a9ca");
It corresponds to the below value.
In order to allow WCF service could access this local certificate, we usually add Everyone account to the management group of the certificate private key.
Besides, WCF service with authenticating the client with a certificate, this usually requires that we set up both the service certificate and the client certificate on the client-side.
Feel free to let me know if there is anything I can help with.

Related

Certificate based authentication in WCF

I am trying to understand certificate based authentication using the msdn sample https://msdn.microsoft.com/en-us/library/ms731074(v=vs.90).aspx
This is the server code:
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
// Create the URI for the endpoint.
Uri httpUri = new Uri("https://localhost/Calculator");
// Create the service and add an endpoint.
ServiceHost myServiceHost = new ServiceHost(typeof(ServiceModel.Calculator), httpUri);
myServiceHost.AddServiceEndpoint(typeof(ServiceModel.ICalculator), binding, "");
// Open the service.
myServiceHost.Open();
Console.WriteLine("Listening...");
Console.ReadLine();
// Close the service.
myServiceHost.Close();
This is the client code I wrote:
ChannelFactory<ICalculator> factory = null;
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
EndpointAddress address = new EndpointAddress("https://localhost/Calculator");
factory = new ChannelFactory<ICalculator>(binding, address);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
factory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "sroger");
ICalculator channel = factory.CreateChannel();
int y = channel.add(9, 8);
I am getting the following exception:
An unhandled exception of type 'System.ServiceModel.CommunicationException' occurred in mscorlib.dll
Additional information: An error occurred while making the HTTP request to https://localhost/Calculator. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.
I am running both client and server from the same machine. And "sroger" is the certificate in my current user\ personal\certificates which corresponds to my machine name..
Not sure what to do from here..Any thoughts?
In the server code what certificate server uses?
Thanks
Gulumal.
https://msdn.microsoft.com/en-us/library/ms731074(v=vs.90).aspx example you used is incomplete.
Consuming https wcf service requires a valid server certificate to work, in your case both client and server certificates are required.
This is because both client and server need to trust each other in a HTTPS connection.
To get started, read https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-mutual-certificates which is a more complete example that includes specifying certificate to authenticate the service.
For a hosted WCF library via https to work you need to do the following in order:
Configure the port with an X.509 certificate (which has been
answered in
webHttpBinding with certificate)
From your server, create certificate request for common name of your
server fully qualified domain name, or at-least including a DNS subjectAltName of your server fully qualified domain name.
(there are different ways to do this, you may already know this
though)
Issue certificate and install certificate on your server
Grab application id from assembly file of your App that hosts WCF
library (i.e [assembly:
Guid("5870aeed-caca-4734-8b09-5c0615402bcf")]) Grab the certificate
thumbprint by viewing certificate properties.
As administrator, open
CMD and run this command to bind X.509 certificate to the port used
by your app on server
netsh http add sslcert ipport=0.0.0.0:443 certhash= appid={} certstorename=MY
netsh http add iplisten ipaddress=0.0.0.0:443
Add this to your server code:
myServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySerialNumber, "<certificate thumbprint>");
In your client code, reference your server address by fully qualified domain name that certificate that is specified as certificate Common Name or subject Alt Name

WebRequest client certificate null on WebAPI side

I have a WebApi controller action that I decorated with my [x509Authorize] attribute. I'm debugging this endpoint locally - and at the same time running a console application that tries to call this endpoint.
Client side
Here's the client code - slightly simplified:
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\Temp\\ht-android-client.pfx");
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://localhost:44300/api/mobile/predict");
Request.ClientCertificates.Add(Cert);
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
....
I've asserted that the Cert is the correct certificate. I've installed the .pfx in my CurrentUser\Personal store and in the LocalMachine\Personal store - and modified to take the Cert from that store, as suggested here but that doesn't seem to make a difference:
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var Cert = store.Certificates.Find(X509FindType.FindBySubjectName, "Android", true)[0];
Server side
And I'm listening on the WebAPI endpoint like with the following code:
public class x509AuthorizeAttribute : AuthorizeAttribute
{
public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var cert = actionContext.Request.GetClientCertificate();
// value of 'cert' is null
I hit a breakpoint in the console app first - see that the correct certificate is selected. Then I hit the breakpoint on the server and see that the value of .GetClientCertificate() is null. What am I doing wrong? The other SO questions 1 and 2 didn't help me any further.
Additional information on the certificates
I've created a self-signed CA certificate which is installed on the LocalMachine\Trusted root CA store. I've created the android client cert - and signed it with my self-signed CA certificate. Then I converted that into a pkcs12 file. This is the certificate that the client is using - which is also installed in my personal stores ( both machine and currentUser ) and is valid ( you can see the chain go back to the ROOT CA cert ).
Also - the certificate's purpose is set to clientAuth:
So the problem is indeed that the server needs to have the following set in the web.config in order to force IIS to start the SSL cert negotiation:
<security>
<access sslFlags="SslNegotiateCert" />
</security>
If this is not present - the certificate will be ignored and you will get null on the GetClientCertificate() call.
This implies however that all clients for my WebAPI are now forced to present a valid certificate - so my original idea of having just one controller method requiring a certificate does not seem possible.
Then there's the challenge of setting this config paramter in web.config, because of the restrictions for Azure Cloud Services. However - this answer provides a solution for that.
EDIT
On a side note this is not supported yet in ASP.NET vNext ( v rc-01-final )

Getting the certificate info from the request on Restful POST

Hi I am having a Restful service (DotNet 4.0 WCF VS 2012) in HTTPS. My client will attach a certificate to it (certificate is given by me (.cer file)) I need to get the certificate back from the request and read its information to authenticate it, The serial Number, Thumprint are stored in DB when I need to check the same.
I did the SSL and Share the cer file to the client.
I used the following code to read back my certificate
C# code start
if (OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets == null)
throw new Exception ("No claimset service configured wrong");
if (OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets.Count <= 0)
throw new Exception ("No claimset service configured wrong");
var cert = ((X509CertificateClaimSet)OperationContext.Current.ServiceSecurityContext.
AuthorizationContext.ClaimSets[0]).X509Certificate;
C# code end
in the above code i always gets claimSets.Count = 0.
Is any setting I need to do in my server web.config, I did the following setting in my Server Side web.config
'security mode="Transport"'
'transport clientCredentialType="None"'
'security'
Please let me know Is I am missing any settings in the client side or the server side.
In the client side I am using following code the add the cer to the request
C# Code Start
X509Certificate2 cert = new X509Certificate2 ("C:\\xxxxxx.cer");
System.Net.ServicePointManager.ServerCertificateValidationCallback =
delegate(Object obj, X509Certificate X509certificate, X509Chain chain, System.Net.Security.SslPolicyErrors errors)
{
return true;
};
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(xxxxx.Text.Trim());
webRequest.ClientCertificates.Add(cert);
C# Code End
I did not have any special setting in my client web.config file.
Why you use clientCredentialType="None" and not clientCredentialType="Certificate"?
It is also possible your client does not send any certificate. Try to enable Network Tracing in App.config on the client - instructions here. That should create network.log with more debug info. Look for SecureChannel entries in log.

Issue in message security in WCF using certificate authentication

I have WCF service where I have implemented message security using certificate. But when I try to connect WCF service from my client application, I am getting following error :
The caller was not authenticated by the service.
My configuration settings are as below :
Service Settings :
ServiceHost host = new ServiceHost(typeof(HostService));
NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.Message);
tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
host.AddServiceEndpoint(typeof(IHostService), tcpBinding, "net.tcp://192.168.39.28:8000/HostService");
host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "server_cert");
Client Settings :
NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.Message);
tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
DuplexChannelFactory<IHostService> serviceFactory = new DuplexChannelFactory<IHostService>(new InstanceContext(MainWindow), tcpBinding, "net.tcp://192.168.39.28:8000/HostService");
serviceFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "client_cert");
serviceFactory.CreateChannel();
where I have created server_cert and client_cert certificates using makecert command. Can you please guide me what I missed ?
debugging certificate related issue is a big pain, I highly recommend to use wireshark. in your case, it's possible you client side didn't even send out the certificate. if the client cert is signed by another cert(s), make sure put it(them) into the trusted root on both client and server.

IIS 7.5 and client authentication

I have to do a proof of concept and thus far I'm finding primarily old articles that reference IIS6 which isn't helping.
In short I have the following requirements.
I need to secure one file/page and this one file/page only using a client certificate. The rest of the site does need to continue operating under SSL but doesn't require client certificate, just this one file. User mapping is forbidden as mapping will be done programatically via C#/VB.NET.
Now I know this shouldn't be hard. I mean I should have access to the Request.ClientCertificate property but my problem is that in my testing I can't get a client certificate to travelling along the wire.
I've set IIS on one folder ( just to make my life simple ) require SSL and accept client certs as well as require client certs but all i get from iis once visiting the page is
HTTP/1.1 403 Forbidden. I never get asked to choose a client certificate to send to the server it just spews all over my request and drops it.
It gets even weirder when I'm using some code to test this. In this client code the CertPolicy class just returns true from a method to ignore cert errors and test.cer is a self signed cert made from using MakeCert. Just to make it clear though, only the client cert if self signed, the main cert is properly signed, but i play with fiddler alot and I haven't trusted that cert so that's why I have the hacky callback.
Dim Cert As X509Certificate = X509Certificate.CreateFromCertFile("Cert\test.cer")
' Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = New CertPolicy()
' You must change the URL to point to your Web server.
Dim Request As HttpWebRequest = DirectCast(WebRequest.Create("https://local.domain.com/Cert/Server/"), HttpWebRequest)
Request.ClientCertificates.Add(Cert)
Request.UserAgent = "Client Cert Sample"
Request.Method = "GET"
Dim sr As StreamReader
Using Response As HttpWebResponse = DirectCast(Request.GetResponse, HttpWebResponse)
' Print the repsonse headers.
output.AppendFormat("{0}\r\n", Response.Headers)
output.AppendLine()
' Get the certificate data.
sr = New StreamReader(Response.GetResponseStream, Encoding.Default)
Dim count As Integer
Dim ReadBuf() As Char = New Char((1024) - 1) {}
Do
count = sr.Read(ReadBuf, 0, 1024)
If Not 0 = count Then
output.AppendLine(New String(ReadBuf))
End If
Loop While (count > 0)
End Using
The target page just returns the number of certs attached, which always returns if i set IIS to accept or ignore client certs but not required the.
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
Dim cs As HttpClientCertificate = Request.ClientCertificate
Response.Write(cs.Count)
Response.End()
End Sub
If anyone can help me find out how to configure IIS7.5 to allow client certs to be attached to a request and just passed through that would be great.
This is an old question but I found it while searching for my own answers and figured it should be answered. In the web.config for the web site, to enable client certificates, you must first make sure that authentication module is installed, then enable the feature:
<location path="yourpath">
<system.webServer>
<security>
<access sslFlags="Ssl, SslNegotiateCert"/> <!-- or SslRequireCert -->
<authentication>
<iisClientCertificateMappingAuthentication enabled="true"
oneToOneCertificateMappingsEnabled="true">
<!-- or manyToOneCertificateMappingsEnabled="true" -->
</iisClientCertificateMappingAuthentication>
</authentication>
</security>
</system.webServer>
</location>
Then you add the one-to-one or many-to-one mappings inside of the iisClientCertificateMappingAuthentication element.
When the server asks the browser for the client certificate, it sends a list of certificate authorities it trusts. The browser then filters the available certificates based upon this information in order to display only relevant certificates (those issued by CAs the server trusts) in the certificate choice dialog.
(At least this is how Internet Explorer works; I don't know if other browsers perform such filtering.)
Therefore the client cert should not be self signed, but 1) should be issued by a certificate authority, 2) the certificate of that certificate authority should be installed on the server (in the Trusted Root Certificate Authorities store of the Local Machine account).
For testing purposes, you may set up your own CA, just make sure its certificate is installed on the server.