I have a set of WCF Services on one server that is being called from my website on another server. Without any security and just basicHttpBinding, this works just fine. Now I want to completely secure the same setup using SSL Transport with a self-signed certificate.
First I tried to use IIS on the Services Server to create a self-signed certificate, but the issue is that it is always creating the cert with the subject in mixed case but the url is lower case. Not sure if that will cause an issue, but I didn't succeed with it.
So I used makecert to (1) create a root cert that I then installed the *.cer file on the client server and service server into the Root CA stores and then (2) created another cert, using the root as the CA, that will be used for signing. I copied this one *.pfx and installed into the Personal store of the LocalComputer for both servers again.
So, now on my Services server, I went into IIS and setup the binding for 443 using the client cert. Then I selected my virtual directory and setup SSL requiring SSL and then selecting 'Required' for Client Certificates.
Now, if I just try to bring up the virtual directory in IE, using https and the full name as it shows in the cert, I get 403.7. I can't seem to get passed this error.
If I try to hit this virtual directory from the website server, I get a plain 403.
If I change my IIS setup to 'Accept' client certs instead of 'Require', the I can browse to my services on both boxes.
Somethings missing...but can't seem to find it.
Update:
Ok, so I created a one-page website and locked it down Requiring the same cert and was able to install that cert into IE on a client and hit the website. Finally, after installing the cert into IT, it allowed me to browse my Services virtual directory and even bring up the page on one of my services.
Question 1: When installing the server cert, I installed the private key version on both Server and Client machine into the LocalMachine -> Personal location. Is this correct or should I just install the public key into the client? It seems to be working.
Still problem - when making a WCF call now I'm getting the following:
SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'myserver.mydomain.com'.
Here's my server configuration:
<system.serviceModel>
<protocolMapping>
<add scheme="https" binding="basicHttpBinding"/>
</protocolMapping>
<bindings>
<basicHttpBinding>
<binding name="SecureCertBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="MyName.MyService" behaviorConfiguration="SecureBehavior">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="SecureCertBinding" name="SecureAlertService" contract="MyName.IMyService"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceCredentials>
<serviceCertificate findValue="myserver.mydomain.com" storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Here's my client configuration:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_SmallData" maxReceivedMessageSize="5000000">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="524288" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="SecureEndpoint">
<clientCredentials>
<clientCertificate findValue="myserver.mydomain.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://myserver.mydomain.com/Services/MyService.svc" binding="basicHttpBinding" behaviorConfiguration="SecureEndpoint"
bindingConfiguration="BasicHttpBinding_SmallData" contract="MyName.IMyService" name="BasicHttpBinding_IMyService"/>
</client>
</system.serviceModel>
How can I tell if the client is sending the certificate with the request?
Got it!! Finally.
I restarted the whole process, including creating new certs and all. Once I got passed the part where IE couldn't even navigate to the Services Virtual Directory, I knew from that point I was mainly up against WCF.
I use a service account on my Services Server for the application pool that has proper authority to the database. This way my connection strings are Windows Auth.
Make sure this service account has Full Permissions to the Cert (Snap-in...Manage Private Keys).
After doing this, I was still getting an error but I found an entry in my System Event Logs that stated an error occurred when trying to retrieve the private key from a cert. This finally helped.
My website (aka...Services client) is still running the application pool with ApplicationPoolIdentity. If this is the case:
Make sure to give the group 'IIS_Usrs' Full Permissions to the Cert.
Bingo...everything worked from this point forward...complete security.
Related
I am trying to call an existing bridge from Azure BizTalk Service, but getting following error:
Could not establish trust relationship for the SSL/TLS secure channel with authority 'domain-name'.
I have tried all possible configurations of WCF bindings but none of them work, the last configuration is as follows:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="ServiceBinding">
<security mode="Transport" />
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ServiceCredentialBehavior">
<clientCredentials>
<clientCertificate findValue="CN=certificate-name"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<clear />
<endpoint address="https://bridge-url" binding="wsHttpBinding" contract="System.ServiceModel.Routing.ISimplexDatagramRouter" name="OneWayExternalServiceEndpointReference2" bindingConfiguration="ServiceBinding" behaviorConfiguration="ServiceCredentialBehavior">
<identity>
<certificate encodedValue="encoded-value" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Could you please advise if I am missing something very obvious, will be appreciated.
Is your certificate self signed ? if yes then you must :
Create a self-signed certificate to be used to encrypt an SSL channel
Upload a certificate to Windows Azure using the Windows Azure Management portal
Enable an HTTPS endpoint on a Windows Azure web role
All detailed instructions are available here
[How To: Enable SSL with a Self-Signed Certificate on Windows Azure][1]
If your certificated is not self signed then I Think you must add the certificate to your local root certificate authority
WCF Exception - The client certificate is not provided. Specify a client certificate in ClientCredentials.
After reading most of what I have been able to find on this subject, and attempting many different options, I find I don’t have any more hair to pull, and hence this post.
I wish to use SSL with a self-hosted WCF service, having security mode as TransportWithMessageCredential with HTTP transport. I am using 2 dev machines and testing over a LAN.
As mentioned above, I have read and meticulously followed just about every example which demonstrates this, yet somehow still have issues with the certificates.
As far as the certificates are concerned, I have tried a number of things.
The main jist of what I did was to follow what is given in
http://msdn.microsoft.com/en-us/library/ff647171.aspx
I also used “How to: Use Certificate Authentication and Message Security in WCF Calling from Windows Forms” in
http://msdn.microsoft.com/en-us/library/ff648360.aspx
as a basic guide.
I first tested the service and client using basicHttpBinding over Http in order to verify things.
I then made changes for wsHttpBinding, SSL, and Certificates.
When I “Add Service Reference” on the client dev PC, I receive an error as follows:
Window Titled -
Security Alert
Visual Studio has detected a problem with the site's security certificate.
Issued By: RootCATest
Issued to: TempCert
Certificate is valid from---
The security certificate issued by a company is not in the untrust list. It might be trustable.
The security certificate date is valid.
The security certificate for host 'TempCert' does not match the name of the page you are trying to view.
Do you want to proceed? -
If I click “Yes” to proceed, and run the client code, an InvalidOperationException occurs with the following message.
“The client certificate is not provided. Specify a client certificate in ClientCredentials.”
The Service config is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="CN=TempCert"
storeLocation="LocalMachine"
storeName="My" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="SBSWCFServiceHost.Operations"
behaviorConfiguration="ServiceBehavior">
<endpoint name="wsHttpEndpoint"
address=""
binding="wsHttpBinding"
bindingConfiguration="wsHttpEndpointBinding"
contract="SBSWCFServiceHost.IOperations" >
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint name="mexHttpEndpoint"
address="mex"
binding="mexHttpsBinding"
contract="IMetadataExchange" >
</endpoint>
<host>
<baseAddresses>
<add baseAddress="https://10.0.0.103:8003/SBSWCFServiceHost/Operations/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
The client config is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="EndpointBehavior">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine"
storeName="My"
x509FindType="FindByThumbprint"
findValue="e4c87a961f796be6b6cab59c3760e43ffb6e941d"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Certificate" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://10.0.0.103:8003/SBSWCFServiceHost/Operations/"
binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint"
contract="SBSWCFService.IOperations" name="wsHttpEndpoint">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
The following is a Summary of the tasks I performed, based on the contents of numerous posts and documents.
Created a self-signed CA certificate (named RootCATest) on the server, and placed it in the Trusted Root Certification Authorities Certificates folder of the Local Computer.
Created a certificate which is signed by the RootCATest certificate (named TempCert), on the server, and placed it in the Personal Certificates folder of the Local Computer.
Exported the TempCert certificate and private key.
Copied the TempCert .cer and .pvk files to the client machine, and imported the TempCert Certificate into the Personal Certificates folder of the Local Computer.
Executed ICalcs.exe [private key path] /grant "NT AUTHORITY\NETWORK SERVICE":R on the server machine, using the path to the private key for the TempCert certificate.
Executed netsh http add sslcert ipport=o.o.o.o:8003 certhash=[TempCert thumbprint] appid=[{application id}] on the server machine
I believe I am close to getting this working.
It seems pretty obvious that the app is not happy with the TempCert certificate, yet I have not been able to resolve this, and am pretty much stuck.
Any assistance with respect to any problems in the given configurations, the steps I have followed in order to put the correct certificates in place, and those used to add access permissions and the sslcert entry, will be greatly appreciated.
Many thanks.
After some further experimentation, I have noticed additional behavior.
Steps taken are as follows:
I deleted both client and server certificates, and recreated them in accordance with
....codeproject.com/Articles/36683/9-simple-steps-to-enable-x-509-certificates-on-wcf
I added the new sslcert using netsh. I then exported the client certificate from the server and
imported it into the client store.
I modified the service app.config with the new certificates info, and started the service.
I modified the client app.config as follows:
<endpointBehaviors>
<behavior name="EndpointBehavior">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" findValue="WCFClient" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
I updated the Service Reference. The update procedure once again issued a Security Alert as before.
I then executed the client, and received this error:
"The client certificate is not provided. Specify a client certificate in ClientCredentials."
I then set a breakpoint on "client = new WCFService.Client();" and checked the "client" instance.
The value of client.ClientCredentials.ClientCertificate.Certificate = null.
I then added the following in code after "client = new WCFService.Client();":
X509Store store = new X509Store("My", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection) store.Certificates;
foreach (X509Certificate2 x509 in collection)
{
if (x509.Thumbprint == "236D7D4AD95753B8F22D3781D61AACB45518E1B5")
{
client.ClientCredentials.ClientCertificate.SetCertificate(
x509.SubjectName.Name, store.Location, StoreName.My);
}
}
After execution of this code, client.ClientCredentials.ClientCertificate.Certificate contained the certificate.
When then executing "client.Open();" , an exception is thrown with the following contents.
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
The remote certificate is invalid according to the validation procedure.
Could not establish trust relationship for the SSL/TLS secure channel with authority
If anyone with knowledge of what may be happening here could shed some light on this I will be very grateful.
The last error message indicates that your server is requesting a client certificate from the client (the server has to ask), and the client is providing a certificate, but the server is not able to determine whether the client certificate is valid based on information available on the server machine.
Since you are using self-signed certificates (not CA issued certificates), you need to tell the server how to validate the client certificates. You may need to install the client certificates in the server's My/LocalMachine/Trusted People cert store so that WCF's default certificate validation can find them, or implement your own custom client certificate validator on the server. (See WebHttpBinding.Credentials.ClientCertificate.Authentication.CertificateValidationMode and WebHttpBinding.Credentials.ClientCertificate.Authentication.CustomCertificateValidator)
I did notice that in your endpoint definition, you did not refer to the behavior that you defined. It looks as if you are doing the equivalent, by-hand via some code. It might be simpler to just wire-it-up in the config.
I would expect your endpoint to look (more) like this:
<endpoint address="https://10.0.0.103:8003/SBSWCFServiceHost/Operations/"
binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint"
contract="SBSWCFService.IOperations" name="wsHttpEndpoint"
behaviorConfiguration="EndpointBehavior">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
This was the solution, when I was getting the error "The client certificate is not provided. Specify a client certificate in ClientCredentials".
I've been attempting to set up WCF transport security using SSL on IIS6.
The client is on a seperate machine on the same domain.
I understand the premise of certificates, root CA etc and have a working set of certs for message security and can use these no probs in the same enviroment set up. (i've learn't a lot over the last week :)
I'm having an nightmare trying to get my client to authenticate against the IIS 6 service when i switch it to SSL. Always recieving 'annonymous authetication not allowed' when calling.
IN IIS i have
a root signed CA cert set on the site for SSL port 443
(if i browse the https:// svc page i can see the IE padlock and the page says you need a cert to communicate)
under secure communications i have
require SSL channel
require 128 bit encryption
require client certificates
enable client certificate mapping (set up with a many to 1 mapping to a admin account on the IIS box for now matched on the cert subject O field )
under web site security (authentication and access control)
Anonymous access = ON
Intergrated Windows Authentication = OFF
basic Authentication = ON
For the client wsHttpBinding i have a certificate ready to authenticate and a custom endpoint behaviour to supply this info but i don't think its getting this far!
UPDATED SERVER CONFIG
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="CertificateWithTransport">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WCFServiceCertificate.Service1" behaviorConfiguration="credentialConfig">
<endpoint address="https://svnvmig02/Service1.svc"
binding="wsHttpBinding"
bindingConfiguration="CertificateWithTransport"
contract="WCFServiceCertificate.IService1">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="credentialConfig">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
UPDATED CLIENT CONFIG
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://svnvmig02/Service1.svc" binding="wsHttpBinding" behaviorConfiguration="CustomBehavior"
bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference1.IService1"
name="WSHttpBinding_IService1">
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="svnvmig02" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
EDIT:
Probably worth mentioning that my VS projects are 3.5 but IIS6 is running .net4
With the amended config (thanks Fabio ;) i can now IE browse the address https://svnvmig01/Service1.svc from the client machine and see the generated svc page which allows me to click on the wsdl URl which is also available.
The majority of the pages i have found on the net refer to selfhosting or IIS7....I'm hoping IIS7 support is better ;)
Any help would be greatly appreciated :)
Your config includes:
https://svnvmig02:8091/Service1.svc
The normal port for ssl is 443.
It may be that the request is not going to the site that you expect it to go to. Therefore, you are getting and unexpected error message.
Check the IIS logs to make sure which site is receiving the request.
I think your issue here may be that you have IIS set to:
Anonymous access = OFF
I use transport security on several of my servers, and all the IIS6 ones have that setting ON, not OFF. This also corresponds to the error message you provided:
'annonymous authetication not allowed'
Without anon access off, IIS will either want the user to enter a username/password, or pass along a windows / active directory / kerberos credentials.
I am self hosting a REST WCF service in Windows service. The service exposes a method which returns a boolean and I am able to get the REST webservice to work on WinXp. I also generated a development certificate using makecert and assigned it to the port(1443) that the service listens on. Https also works well on WinXp. But When I use the same in Win2003, and type the URL in IE, it says "The page cannot be displayed". There were no errors in hosting the service, the service listens on the port(the ServiceHost.Open didnt have any errors and its successful). Is there an settings that has to be done in Win2003 for this? Win2003 is the production environment and https has to work on that.
Here is the config file I used,
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="httpBinding">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="spectrumServiceBehavior" name="MyApp.TestService">
<host>
<baseAddresses>
<add baseAddress="https://localhost:1443/" />
</baseAddresses>
</host>
<endpoint address=""
binding="webHttpBinding"
bindingConfiguration ="httpBinding"
contract="MyApp.ITestService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="spectrumServiceBehavior">
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpsGetEnabled ="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
Any ideas?
I've experienced the same problem. WinXP development environment working with HTTPS, certificate created locally and assigned to a custom port with httpcfg.
Production environment on Windows 2003 with the imported certificate and the same httpcfg configuration giving "interrupted connection" while trying to obtain the WSDL of the service.
Looking at the Windows log I finally found this error:
Event Source: Schannel
A fatal error occurred when attempting to access the SSL server credential private key. The error code returned from the cryptographic module is 0x80090016.
My problem was that I imported the certificate in the user-account store, and then copied in the local store. In this way the private-key is left behind... http://support.microsoft.com/kb/939616 (without even a warning!!!)
despite, opening the certificate in the new location, shows the presence of a private-key!
I expect that on Windows 2003 you must allow application to listen on port (unless it runs as admin) and you must assign SSL certificate to the port - both is performed by httpcfg.exe. Also check that there is no firewall blocking the communication on the port.
I have a WCF client/service app that relies on secure communication between two machines and I want to use use x509 certificates installed in the certificate store to identify the server and client to each other. I do this by configuring the binding as <security authenticationMode="MutualCertificate"/>. There is only client machine.
The server has a certificate issued to server.mydomain.com installed in the Local Computer/Personal store and the client has a certificate issued to client.mydomain.com installed in the same place. In addition to this the server has the client's public certificate in Local Computer/Trusted People and the client has the server's public certificate in Local Computer/Trusted People.
Finally the client has been configured to check the server's certificate. I did this using the system.servicemodel/behaviors/endpointBehaviors/clientCredentials/serviceCertificate/defaultCertificate element in the config file.
So far so good, this all works. My problem is that I want to specify in the server's config file that only clients that identify themselves with the client.mydomain.com certificate from the Trusted People certificate store are allowed to connect.
The correct information is available on the server using the ServiceSecurityContext, but I am looking for a way to specify in app.config that WCF should do this check instead of my having to check the security context from code.
Is that possible? Any hints would be appreciated.
By the way, my server's config file looks like this so far:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyServer.Server" behaviorConfiguration="CertificateBehavior">
<endpoint contract="Contracts.IMyService" binding="customBinding" bindingConfiguration="SecureConfig">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/SecureWcf"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CertificateBehavior">
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" x509FindType="FindBySubjectName" findValue="server.mydomain.com"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="SecureConfig">
<security authenticationMode="MutualCertificate"/>
<httpTransport/>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
There doesn't appear to be a way to do what I want using web.config.
I ended up adding a behavior with this tag:
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="CurrentUser" revocationMode="NoCheck"/>
</clientCertificate>
And then add the client's certificate to the "trusted people" certificate store of the user that the server runs as.
Check out the WCF Security Guidance page on Codeplex - excellent and very useful stuff!
In particular, check out the How-To's and even more specifically the
How To – Use Certificate Authentication and Message Security in WCF calling from Windows Forms
It explains in great detail how to set up a WCF service which requires its clients to present a valid certificate, and how to check that. If you want to allow only a single client, deploy that certificate only specifically to that one single client.
Hope this helps!