I am just playing arround with WCF and certificates. I have installed a certificate on my computer, referenced it in my WCF service config like this:
<serviceCertificate findValue="testcert"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
I then start the service. All is fine. Then I create a simple client console program in visual studio, by adding the service as a service reference. I can infact contact the service, so all is working correctly! When I look in my app.config for my client, I have this identity:
<identity>
<certificate encodedValue="AwAAAAEAAAAUAAAAuFTG2BUHH8JSm1VyxrCgEzaOQfAgAAAAAQAAALIEAAAwggSuMIIClqADAgECAhBqosPak3lGkETZ3xl9oaC0MA0GCSqGSIb3DQEBBQUAMBMxETAPBgNVBAMTCHRlc3RjZXJ0MB4XDTE1MDUwMjIyMDAwMFoXDTI1MDUwOTIyMDAwMFowEzERMA8GA1UEAxMIdGVzdGNlcnQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCoRWN7519nqQ7uFwgKtUWdTyKUhz+OdF9XtQF431/A1nhVsQsZ+J2k2Ola63eyTIEdR4y6jUx7KfQ1TMOtKSKB6iobP+JT8jibS1Ll47s6Ym2iEGLI6myftsuTezjObrnFO3tifCIMeKtMKdu1XYkpWr/DQogBElI6ZjkpaxIN+R+kw0cBwUm1o29/3V3VdHY9rlh4j8YJPMY903WCU/p7kCjiCfHWrWEkicer1rC6OC0TPLQVP0FSt6dCo1ptvHHk+aIUOU/lExCpT/FYjGUw5l8KeLz+V2xJ+Uy/kjuaAfSqE9wel/yDhDq599xbiBCViyAFSOfl11IcmOpA+H2LLuqvYZaFfem0MrTNJbr79Xjp/ywaQYf0jcK/D7t+6ouPmv4hibkeeELO1Cy3LQIYc3MNkvAkoiIXTxve2TmmN8CThQPfX3jrFm+sHvUEja4HnP5sbMuk9yNEvS+pJZj6hcmRyHzj9GIRRB5rCYfg/6pe0ttIs0Y4vaVzO232i4ffL1S9OzSLf7OYHfKvPc7YeEuouqPSAjIzB9BbEh678AtIUmatOiSBPpHYsPgz5QN+0kNSCgXnVkV+XN+JEOjxmHxGaPQHP3bvXAXZ94Y8jeSzLH5SyDxakAEJuujTL3wSRbNAy+ORexSC3YGmuQixaOHuTZ40BCQKxrDo/+NX6wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCcPb5B8Q3X3XqIrrNbzl4Eb4mLzmHrRP/W64LssGLr9O0MoJYw+RdOWyIbEtvg7nq0p0qBSi7lBq1ete/RSNiT3WMjIRPCgwZkq/yadolGgtRBq3FVHR7royjLu1DwOBtAhMQuBHopK6Dt88E4z8Bh/vyMruMs7VSH6cFbA0+iII02TLkC4+cgt5ZTxdet2JwyGsHNdk9c8pfq50Wht3kFzZPMYrowYH/c0fxkaIqWUN+W2sabBj/lzpo3kd6q0bBqamek9Bmk6IgikQrgx/ktAF3ul1u+2ybltQ7gJZIIuZphDSPmRRm+/sB2Wf6XGSG1ZUhrxsofQ7HVhlksTIfDQ7CtB8IlTmtesorJNfXw9ymKDWzvDRfNEVChDj/OGu3GHX6qwq7PeCsstxQeQRVQgB4Mo+ghCeTjH20xCxOzzZZVP0IRe/WvxNzxJxcnzYCgF7F9ztQBJoqUBqLVKdXYnLFRymFPpnc6OOTIi/+yD/4oNV9qMfFX14K+rVmq+HkDW1p3ipteDz2xzodiiYTk27yILHb3yJ2GgvuLmv4zKgUc3KTkxQeN4fmDOgqFP9VFXGWqgiPrbuFiBB+VNFDwbqgQtnz/tmZ6PDCNuMFJYALYSnKzU9/IWWScivzVeyrZuWPALX8nW3gAtOOgTaSKg9/3jAAqIWqTvkO0sviG6A==" /></identity>
I have read somewhere that this value, is the base64 encoded value of the public key, that is served by the wsdl of the service. Is this correct? If I look at the wsdl, the public key here is,
<Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>
MIIErjCCApagAwIBAgIQaqLD2pN5RpBE2d8ZfaGgtDANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwh0ZXN0Y2VydDAeFw0xNTA1MDIyMjAwMDBaFw0yNTA1MDkyMjAwMDBaMBMxETAPBgNVBAMTCHRlc3RjZXJ0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqEVje+dfZ6kO7hcICrVFnU8ilIc/jnRfV7UBeN9fwNZ4VbELGfidpNjpWut3skyBHUeMuo1Meyn0NUzDrSkigeoqGz/iU/I4m0tS5eO7OmJtohBiyOpsn7bLk3s4zm65xTt7YnwiDHirTCnbtV2JKVq/w0KIARJSOmY5KWsSDfkfpMNHAcFJtaNvf91d1XR2Pa5YeI/GCTzGPdN1glP6e5Ao4gnx1q1hJInHq9awujgtEzy0FT9BUrenQqNabbxx5PmiFDlP5RMQqU/xWIxlMOZfCni8/ldsSflMv5I7mgH0qhPcHpf8g4Q6uffcW4gQlYsgBUjn5ddSHJjqQPh9iy7qr2GWhX3ptDK0zSW6+/V46f8sGkGH9I3Cvw+7fuqLj5r+IYm5HnhCztQsty0CGHNzDZLwJKIiF08b3tk5pjfAk4UD31946xZvrB71BI2uB5z+bGzLpPcjRL0vqSWY+oXJkch84/RiEUQeawmH4P+qXtLbSLNGOL2lcztt9ouH3y9UvTs0i3+zmB3yrz3O2HhLqLqj0gIyMwfQWxIeu/ALSFJmrTokgT6R2LD4M+UDftJDUgoF51ZFflzfiRDo8Zh8Rmj0Bz9271wF2feGPI3ksyx+Usg8WpABCbro0y98EkWzQMvjkXsUgt2BprkIsWjh7k2eNAQkCsaw6P/jV+sCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAnD2+QfEN1916iK6zW85eBG+Ji85h60T/1uuC7LBi6/TtDKCWMPkXTlsiGxLb4O56tKdKgUou5QatXrXv0UjYk91jIyETwoMGZKv8mnaJRoLUQatxVR0e66Moy7tQ8DgbQITELgR6KSug7fPBOM/AYf78jK7jLO1Uh+nBWwNPoiCNNky5AuPnILeWU8XXrdicMhrBzXZPXPKX6udFobd5Bc2TzGK6MGB/3NH8ZGiKllDfltrGmwY/5c6aN5HeqtGwampnpPQZpOiIIpEK4Mf5LQBd7pdbvtsm5bUO4CWSCLmaYQ0j5kUZvv7Adln+lxkhtWVIa8bKH0Ox1YZZLEyHw0OwrQfCJU5rXrKKyTX18Pcpig1s7w0XzRFQoQ4/zhrtxh1+qsKuz3grLLcUHkEVUIAeDKPoIQnk4x9tMQsTs82WVT9CEXv1r8Tc8ScXJ82AoBexfc7UASaKlAai1SnV2JyxUcphT6Z3OjjkyIv/sg/+KDVfajHxV9eCvq1Zqvh5A1tad4qbXg89sc6HYomE5Nu8iCx298idhoL7i5r+MyoFHNyk5MUHjeH5gzoKhT/VRVxlqoIj627hYgQflTRQ8G6oELZ8/7ZmejwwjbjBSWAC2Epys1PfyFlknIr81Xsq2bljwC1/J1t4ALTjoE2kioPf94wAKiFqk75DtLL4hug=
</X509Certificate>
</X509Data>
</KeyInfo>
</Identity>
And it matches the public key, when I export the key (base64 encoded export) from the certificate store. All fine. But what I dont get how the value from the wsdl and export corresponds to value in the client app.config file? If I base64 encode the exported value (or the value from the wsdl), i dont get the encoded value.
So my questions is: how is the value, in the client app.config file encoded? Have I missed something? Is it even the public key that is present in the client app.config at all?
Both of the values are for a certificate with the thumbprint B854C6D815071FC2529B5572C6B0A013368E41F0 and subject CN=testcert
One of them is the base64 encoding of the the bytes of what your would get if you used this method (https://msdn.microsoft.com/en-us/library/dxz81eb9(v=vs.110).aspx) and passed the Cert X509ContentType and the other is using the SerializedCert X509ContentType.
I don't know why one is used over the other but they represent the same thing (though from the link I posted, it appears the SerializedCert contains properties that were included in the Windows certificate store as well as the certificate itself)
Related
I'm trying to make it so my Send Port which is of type WCF-BasicHttp, will send a client certificate that will be accepted by the IIS server of the WCF service.
So far, as far as I can tell, the Client Cert is being attached (because when I put the wrong thumbprint value in there, I get a "Client Cert not found error"). However, now I'm getting an error as such
System.Net.WebException: The underlying connection was closed: Could
not establish trust relationship for the SSL/TLS secure channel. --->
System.Security.Authentication.AuthenticationException: The remote
certificate is invalid according to the validation procedure.
Which to me, sounds like something in processing the IIS SSL certificate is not working.
This service
works fine when being called from another Winforms application, using
the same Client Cert.
is set to Require SSL
is set to Require Client Certificates
Have a look at the image below...the only way I was able to the WCF to recognize a client cert was being attached was through configuring the send port as such:
My question is, does this look correct? Notice the highlighted field, what is supposed to go in there? Is this the reason I'm getting the SSL/TLS error?
EDIT Or, do I have it all wrong, and the lower Certificate Reference area is not supposed to contain the Client Certficate?
So, it turns out, that in order to get it to work, I had to provide the Service's identity in the top portion. I noticed that putting anything in the Endpoint Identity dialog, after saving, a nice little bit of XML is written to the WCF-BasicHttp dialog after.
<identity>
<dns value="myserver.mydomain.ca" />
<certificateReference storeName="My" storeLocation="LocalMachine" x509FindType="FindByThumbprint" findValue="[thumbprint]" isChainIncluded="False" />
</identity>
with only the lower portion filled out as in my question above, it provides the <certificateReference> portion, but the <dns value="myserver.mydomain.ca" /> does not show up until you provide something in one of the textboxes in the "General" section. It worked when I provided either the DNS, or the Base64 of the server certificate.
So, I guess the answer is, once you fill out one of the sections, both are required, the send port is not going to automatically guess on the endpoint based on the address.
WCF is extremely extensible and has a lot of ready-to-use features, however I continue struggling with some topics and the more documentation I read, the more I get confused.
I hope to get some answers from the community. Feedback on any assumption or question is extremely welcome.
For the record: to really accept a single answer I should divide this post in multiple questions but it would lead to even more confusion.
I am pretty sure there are some real WCF experts online who can answer the few questions in this document all at once so I can accept a single answer as the real deal to setup clientcertificate authentication using IIS the right way.
Let me sketch the situation and partner request:
1: The partner requirement and the question to use a client certificate.
Partner X needs to call an API on my backend and they have the clear requirement to use Clientcertificate authentication.
They created the clientcertificate and provided us the certificate with only the public key since it seems only logic they keep the private key actually private and in their own system(s).
The certificate was imported on the local computer account and looking at the certification path this is valid. All intermediate certification authorities and in the end the root certification authority are trusted.
2: Our WCF serverside configuration
I have a serviceBehavior configured as such:
<behavior name="ClientCertificateBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceCredentials>
<serviceCertificate findValue="<serialnumber here>" x509FindType="FindBySerialNumber" />
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" />
</clientCertificate>
</serviceCredentials>
</behavior>
I guess I made a first mistake here and should use ChainTrust to actually validate the certificate using its certification path. What do you think?
The service is configured as such:
<service behaviorConfiguration="ClientCertificateBehavior" name="<Full service namespace and servicename>">
<endpoint binding="basicHttpBinding" bindingConfiguration="Soap11CertificateBasicHttpBinding"
contract="<The interface>"></endpoint>
</service>
The binding looks like this:
It is a basicHttpBinding to force SOAP1.1 (according to the partner's specifications).
<binding name="Soap11CertificateBasicHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
3: Hosting the WCF service in IIS and the IIS configuration
We host our WCF services in IIS7.
We configured the folder in which the services reside to require SSL and to accept Client certificates.
Authentication-wise anonymous authentication is enabled.
The thing is that communication from the partner works and we were confident that everything was OK, however toggling the IIS-setting to 'require' client certificate shows us that all of a sudden it is no longer possible to successfully call our service.
Am I correct to assume that following things are not done correctly:
The serviceCerticate in the serviceBehavior is not really necessary. This is a setting used by the client. Or is it necessary to provide this certificate information for the service endpoint to match the certificate that's being send by the client?
For clientcertificate authentication to really work in IIS the certificate needs to be mapped to a user. This user should be granted permissions on the folder containing the services and all authentication mechanisms (anonymous, windows,...) should be disabled.
This way IIS will handle the actual handshake and validate the servicecommunication.
Or is it more a matter of extra security mapping the certificate to a user?
By setting 'Accept' on IIS we bypass the actual certificate validation between client and server.
All authentication mechanisms like 'anonymous' and 'windows' have to be disabled on IIS for the folder which holds the services.
In your scenario, you don't need to configure certificates in WCF, IIS handles those for you. You can clear the entire <serviceCredentials> block, because:
The <serviceCertificate> of <serviceCredentials> specifies an X.509 certificate that will be used to authenticate the service to clients using Message security mode, which you do not use, and the <clientCertificate> of <serviceCredentials> defines an X.509 certificate used to sign and encrypt messages to a client form a service in a duplex communication pattern.
See here how to map client certificates to user accounts.
Below are the configuration setting i have in Server web.config file which is WCF application. Here i used makecert.exe for creating X509 certificate.
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType= "WcfService1.CCustomValidatorClass,WcfService1"/>
<serviceCertificate
findValue="CN=SignedByCA1"
x509FindType="FindBySubjectDistinguishedName"
storeLocation="CurrentUser"
storeName="My" />
</serviceCredentials>
"CN=SignedByCA1" is the Private Key and now , I want to invoke Web Method on WCF service from the Client.
But i need to set SetDefaultCertificate from the Client, could you please help how can I get key for accessing Private key X509 Certificate.
ServiceReference1.Service1Client obj = new ServiceReference1.Service1Client();
obj.ClientCredentials.UserName.UserName = "Sukesh";
obj.ClientCredentials.UserName.Password = "Sukesh";
obj.ClientCredentials.ServiceCertificate.SetDefaultCertificate(
System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser,
System.Security.Cryptography.X509Certificates.StoreName.My,
System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectDistinguishedName,
"XYZ");
string str = obj.GetData(1); // web method
Please help me here...
ERROR: {"The X.509 certificate CN=SignedByCA1 chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. The revocation function was unable to check revocation for the certificate.\r\n"}
Thanks,
Sukesh.
You're getting an error due to a self signed certificate.
You can either ignore the error using, http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback(v=vs.100).aspx
or you could install the client certificate into the server's trusted root authority (assuming this is just for development).
Or, of course, you could get the certificate signed.
The scenario is this: there are 2 WCF Web Services, one a client (WCFClient), one a server (WCFServer), deployed on different machines. I needed certificate communication between the two of them.
On the server WCF I have set the binding to use certificates as client credential type.
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
Also, in the behaviour section, among other settings, I have
<serviceBehaviors>
<behavior name="Server.ServiceBehavior">
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</clientCertificate>
<serviceCertificate findValue="Server"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
On the client WCF Service I added this endpoint behaviour
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="Client"
x509FindType="FindBySubjectName"
storeLocation="LocalMachine"
storeName="TrustedPeople" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
When I wanted to test my services, I had an error message:
The service certificate is not provided for target 'http://blablabla...'. Specify a service certificate in ClientCredentials.
So I started checking things out on the Internet. After trying many things, the only thing that actually worked is adding this on my client:
<serviceCertificate>
<defaultCertificate findValue="Server"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
As you might think, yes, this means I need the Server certificate on my client machine. Which is clearly a very bad thing.
It works for my testing purposes, but it is an unacceptable for deployment.
I would want to understand what really could cause that error message and what the solution may be.
Later edit: In this project the client must not have the server certificate (even without having the private key). This is the specification of the system and it's quite difficult (in bureaucracy terms) to go beyond this.
There will be multiple clients, each with the client WCF service running, and each should know nothing more that their own certificate. The server will know the server certificate and all the clients certificate.
Looking here it reads,
When considering authentication, you may be used to thinking primarily
of the client identity. However, in the context of WCF, authentication
typically refers to mutual authentication. Mutual authentication not
only allows positive identification of the clients, but also allows
clients to positively identify the WCF services to which they are
connected. Mutual authentication is especially important for
Internet-facing WCF services, because an attacker may be able to spoof
the WCF service and hijack the client’s calls in order to reveal
sensitive data.
The service credentials to be used depend largely on the client
authentication scheme you choose. Typically, if you are using
non-Windows client authentication such as username or certificate
authentication, a service certificate is used for both service
authentication and message protection. If you are using Windows client
authentication, the Windows credentials of the process identity can be
used for both service authentication and message protection.
It looks to me that you do need the server certificate on the client machine, and that this is a good thing, not a bad thing. Note that you do not need (and should not put) the server's private key on the client machine. The private key is not contained in a certificate -- only the public key is.
Having the server certificate on the client machine means only having the server's public key on the client machine. The benefit is that the client now knows that it is talking to the real server.
I'm not familiar with WCF services, but this seems fine as far as the use of certificates.
why is it bad to have the service certificate on the client machine? it is only the public portion of it, not the private key.
if you use wshttpbinding you can set negotiateServiceCredential=true in which case the client will get the server cert dynamically. The price is a little bit of performance hit, and this endpoint will not be interoperable to non .net clients.
I actually forgot about this question, but at that time I have found the solution.
My actual problem was that I was using a basicHttpBinding for the communication I wanted to secure. basicHttpBinding implies ussing that serviceCredential part.
http://msdn.microsoft.com/en-us/library/ms731338(v=vs.85).aspx
Because of the system requirements I had, I changed the binding to wsHttpBinding. Now I don't need to put the server certificate on the client machine.
I'm trying to access a publicly-hosted SOAP web service (not WCF) over https, and I'm getting an error that I've never seen before. First, here are the facts:
This service requires client certificates. I have a certificate that is signed by the same CA as the server's certificate.
I know that the URL is available, as I can hit it in Internet Explorer. IE brings up the "choose certificate" window, and if I pick it (and ignore the server-host-name-does-not-match-certificate error), it goes on and gives me an HTTP 500 error.
If I open the site in Chrome, after picking the cert and ignoring the error, I get a normal error message about WSA Action = null.
If I open the site in FireFox, after ignoring the error, I get a page about how the server couldn't validate my certificate. It never asked me to pick one, so that makes perfect sense.
Now, the exception:
Error occurred while executing test 12302: System.ServiceModel.Security.SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'ihexds.nist.gov:9085'. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
I've traced the interaction with WireShark, but because I'm not an expert in the TLS protocol, I could be missing clues as to what's going on. Here, however, is what I do see:
C -> S Client Hello
Contains things like a random number, date/time, cypher suites supported, etc
S -> C Server Hello, Certificate, Certificate Request, Server Hello Done
Contains the server's certificate, and a request for a client certificate
C -> S Certificate, Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
HERE IS THE INTERESTING PART -- The first part of this packet is the Certificate handshake, where I assume the client certificate would be, but there are no certificates present (Certificates Length: 0).
S -> C Alert (Level: Fatal, Description: Bad Certificate)
Well, yeah, there was no certificate sent.
My binding is set up as follows:
<binding name="https_binding">
<textMessageEncoding />
<httpsTransport useDefaultWebProxy="false" />
</binding>
My behavior is set up as follows:
<behavior name="clientcred">
<clientCredentials>
<clientCertificate findValue="69b6fbbc615a20dc272a79caa201fe3f505664c3" storeLocation="CurrentUser" storeName="My" x509FindType="FindByThumbprint" />
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
<messageInspector />
</behavior>
My endpoint is set up to use both the binding and the behavior. Why does WCF refuse to send the certificate when it creates the https connection?
I solved the problem, but I do not understand why this configuration change fixed it. I changed this line:
<httpsTransport useDefaultWebProxy="false" />
to this:
<httpsTransport useDefaultWebProxy="false" requireClientCertificate="true" />
and magically it started working. I had understood that the requireClientCertificate "knob" was for server-side, so I hadn't tried it during my wrangling. Apparently I was wrong.
There should have been a CertificateRequest from the server, naming acceptable cert types and CAs. If your certificate doesn't match those it won't be sent.
It could be a problem negotiating which security protocol to use. Specifically im thinking that the server might not like WCF trying to use TLS 1.0.
To see if this is the case try to add the following before invoking the service
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3
This could be added either in client code or by placing it in an IEndpointBehavior